mirror of
https://github.com/HigherOrderCO/Kind1.git
synced 2024-09-11 08:45:32 +03:00
fix: some fixes
This commit is contained in:
commit
0b16e63eed
@ -99,10 +99,7 @@ fn range_to_num(range: Range) -> Box<Term> {
|
|||||||
fn set_origin(ident: &Ident) -> Box<Term> {
|
fn set_origin(ident: &Ident) -> Box<Term> {
|
||||||
mk_lifted_ctr(
|
mk_lifted_ctr(
|
||||||
"Kind.Term.set_origin".to_owned(),
|
"Kind.Term.set_origin".to_owned(),
|
||||||
vec![
|
vec![range_to_num(ident.range), mk_var(ident.to_str())],
|
||||||
range_to_num(ident.range),
|
|
||||||
mk_var(ident.to_str()),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,10 +148,16 @@ fn codegen_all_expr(
|
|||||||
) -> Box<Term> {
|
) -> Box<Term> {
|
||||||
use kind_tree::desugared::ExprKind::*;
|
use kind_tree::desugared::ExprKind::*;
|
||||||
match &expr.data {
|
match &expr.data {
|
||||||
Typ => mk_lifted_ctr(eval_ctr(quote, TermTag::Typ), vec![range_to_num(expr.range)]),
|
Typ => mk_lifted_ctr(
|
||||||
|
eval_ctr(quote, TermTag::Typ),
|
||||||
|
vec![range_to_num(expr.range)],
|
||||||
|
),
|
||||||
NumType {
|
NumType {
|
||||||
typ: kind_tree::NumType::U60,
|
typ: kind_tree::NumType::U60,
|
||||||
} => mk_lifted_ctr(eval_ctr(quote, TermTag::U60), vec![range_to_num(expr.range)]),
|
} => mk_lifted_ctr(
|
||||||
|
eval_ctr(quote, TermTag::U60),
|
||||||
|
vec![range_to_num(expr.range)],
|
||||||
|
),
|
||||||
NumType {
|
NumType {
|
||||||
typ: kind_tree::NumType::U120,
|
typ: kind_tree::NumType::U120,
|
||||||
} => mk_lifted_ctr(
|
} => mk_lifted_ctr(
|
||||||
@ -325,7 +328,10 @@ fn codegen_all_expr(
|
|||||||
vec![range_to_num(expr.range), mk_u60(*num)],
|
vec![range_to_num(expr.range), mk_u60(*num)],
|
||||||
),
|
),
|
||||||
Str { val } => codegen_all_expr(lhs_rule, lhs, num, quote, &desugar_str(val, expr.range)),
|
Str { val } => codegen_all_expr(lhs_rule, lhs, num, quote, &desugar_str(val, expr.range)),
|
||||||
Hlp(_) => mk_lifted_ctr(eval_ctr(quote, TermTag::Hlp), vec![range_to_num(expr.range)]),
|
Hlp(_) => mk_lifted_ctr(
|
||||||
|
eval_ctr(quote, TermTag::Hlp),
|
||||||
|
vec![range_to_num(expr.range)],
|
||||||
|
),
|
||||||
Err => panic!("Internal Error: Was not expecting an ERR node inside the HVM checker"),
|
Err => panic!("Internal Error: Was not expecting an ERR node inside the HVM checker"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,7 +181,7 @@ pub fn run_cli(config: Cli) {
|
|||||||
}
|
}
|
||||||
Command::ToHVM { file } => {
|
Command::ToHVM { file } => {
|
||||||
compile_in_session(render_config, root, file.clone(), true, &mut |session| {
|
compile_in_session(render_config, root, file.clone(), true, &mut |session| {
|
||||||
let book = driver::erase_book(session, &PathBuf::from(file.clone()), &entrypoints)?;
|
let book = driver::erase_book(session, &PathBuf::from(file.clone()))?;
|
||||||
Some(driver::compile_book_to_hvm(book))
|
Some(driver::compile_book_to_hvm(book))
|
||||||
})
|
})
|
||||||
.map(|res| {
|
.map(|res| {
|
||||||
@ -194,7 +194,6 @@ pub fn run_cli(config: Cli) {
|
|||||||
let book = driver::erase_book(
|
let book = driver::erase_book(
|
||||||
session,
|
session,
|
||||||
&PathBuf::from(file.clone()),
|
&PathBuf::from(file.clone()),
|
||||||
&["Main".to_string()],
|
|
||||||
)?;
|
)?;
|
||||||
driver::check_main_entry(session, &book)?;
|
driver::check_main_entry(session, &book)?;
|
||||||
Some(driver::compile_book_to_hvm(book))
|
Some(driver::compile_book_to_hvm(book))
|
||||||
@ -227,7 +226,7 @@ pub fn run_cli(config: Cli) {
|
|||||||
}
|
}
|
||||||
Command::Erase { file } => {
|
Command::Erase { file } => {
|
||||||
compile_in_session(render_config, root, file.clone(), true, &mut |session| {
|
compile_in_session(render_config, root, file.clone(), true, &mut |session| {
|
||||||
driver::erase_book(session, &PathBuf::from(file.clone()), &entrypoints)
|
driver::erase_book(session, &PathBuf::from(file.clone()))
|
||||||
})
|
})
|
||||||
.map(|res| {
|
.map(|res| {
|
||||||
print!("{}", res);
|
print!("{}", res);
|
||||||
|
@ -75,7 +75,7 @@ fn test_eval() -> Result<(), Error> {
|
|||||||
let root = PathBuf::from(".");
|
let root = PathBuf::from(".");
|
||||||
let mut session = Session::new(root, rx);
|
let mut session = Session::new(root, rx);
|
||||||
|
|
||||||
let check = driver::erase_book(&mut session, &PathBuf::from(path), &["Main".to_string()])
|
let check = driver::erase_book(&mut session, &PathBuf::from(path))
|
||||||
.map(driver::compile_book_to_hvm);
|
.map(driver::compile_book_to_hvm);
|
||||||
|
|
||||||
let diagnostics = tx.try_iter().collect::<Vec<_>>();
|
let diagnostics = tx.try_iter().collect::<Vec<_>>();
|
||||||
|
@ -1,12 +1,12 @@
|
|||||||
use checker::eval;
|
use checker::eval;
|
||||||
use errors::DriverError;
|
use errors::DriverError;
|
||||||
use kind_pass::{desugar, erasure, expand};
|
use kind_pass::{desugar, erasure, expand};
|
||||||
use kind_report::{report::FileCache};
|
use kind_report::report::FileCache;
|
||||||
use kind_span::SyntaxCtxIndex;
|
use kind_span::SyntaxCtxIndex;
|
||||||
|
|
||||||
use kind_tree::{backend, concrete, desugared, untyped};
|
use kind_tree::{backend, concrete, desugared, untyped};
|
||||||
use session::Session;
|
use session::Session;
|
||||||
use std::path::{PathBuf};
|
use std::path::PathBuf;
|
||||||
|
|
||||||
use kind_checker as checker;
|
use kind_checker as checker;
|
||||||
|
|
||||||
@ -53,7 +53,6 @@ pub fn to_book(session: &mut Session, path: &PathBuf) -> Option<concrete::Book>
|
|||||||
pub fn erase_book(
|
pub fn erase_book(
|
||||||
session: &mut Session,
|
session: &mut Session,
|
||||||
path: &PathBuf,
|
path: &PathBuf,
|
||||||
entrypoint: &[String],
|
|
||||||
) -> Option<untyped::Book> {
|
) -> Option<untyped::Book> {
|
||||||
let concrete_book = to_book(session, path)?;
|
let concrete_book = to_book(session, path)?;
|
||||||
let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?;
|
let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?;
|
||||||
@ -84,7 +83,6 @@ pub fn compile_book_to_kdl(
|
|||||||
let concrete_book = to_book(session, path)?;
|
let concrete_book = to_book(session, path)?;
|
||||||
let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?;
|
let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?;
|
||||||
let book = erasure::erase_book(&desugared_book, session.diagnostic_sender.clone())?;
|
let book = erasure::erase_book(&desugared_book, session.diagnostic_sender.clone())?;
|
||||||
println!("{} {}", book.entrs.len(), book.names.len());
|
|
||||||
kind_target_kdl::compile_book(book, session.diagnostic_sender.clone(), namespace)
|
kind_target_kdl::compile_book(book, session.diagnostic_sender.clone(), namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,12 +23,7 @@ impl<'a> DesugarState<'a> {
|
|||||||
literal: &expr::Literal,
|
literal: &expr::Literal,
|
||||||
) -> Box<desugared::Expr> {
|
) -> Box<desugared::Expr> {
|
||||||
match literal {
|
match literal {
|
||||||
Literal::Number(kind_tree::Number::U120(num)) => {
|
Literal::Number(kind_tree::Number::U120(num)) => desugared::Expr::num120(range, *num),
|
||||||
if !self.check_implementation("U120.new", range, Sugar::U120) {
|
|
||||||
return desugared::Expr::err(range);
|
|
||||||
}
|
|
||||||
desugared::Expr::num120(range, *num)
|
|
||||||
}
|
|
||||||
Literal::String(string) => {
|
Literal::String(string) => {
|
||||||
if !self.check_implementation("String.cons", range, Sugar::String)
|
if !self.check_implementation("String.cons", range, Sugar::String)
|
||||||
|| !self.check_implementation("String.nil", range, Sugar::String)
|
|| !self.check_implementation("String.nil", range, Sugar::String)
|
||||||
|
@ -106,7 +106,16 @@ impl<'a> ErasureState<'a> {
|
|||||||
entrypoints.push(id);
|
entrypoints.push(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kdl specific things.
|
||||||
for entr in book.entrs.values() {
|
for entr in book.entrs.values() {
|
||||||
|
if let Some(name) = &entr.attrs.kdl_state {
|
||||||
|
if book.entrs.contains_key(name.to_str()) {
|
||||||
|
let id = self.get_edge_or_create(&name.to_qualified_ident());
|
||||||
|
self.set_relevance(id, Relevance::Relevant, name.range);
|
||||||
|
entrypoints.push(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if entr.attrs.kdl_run {
|
if entr.attrs.kdl_run {
|
||||||
let id = self.get_edge_or_create(&entr.name);
|
let id = self.get_edge_or_create(&entr.name);
|
||||||
self.set_relevance(id, Relevance::Relevant, entr.name.range);
|
self.set_relevance(id, Relevance::Relevant, entr.name.range);
|
||||||
|
@ -9,7 +9,6 @@ pub enum Sugar {
|
|||||||
Pair,
|
Pair,
|
||||||
BoolIf,
|
BoolIf,
|
||||||
String,
|
String,
|
||||||
U120,
|
|
||||||
Match(String),
|
Match(String),
|
||||||
Open(String),
|
Open(String),
|
||||||
}
|
}
|
||||||
@ -203,7 +202,6 @@ impl Diagnostic for PassError {
|
|||||||
Sugar::Pair => "You must implement 'Sigma' and 'Sigma.new' in order to use the sigma notation.".to_string(),
|
Sugar::Pair => "You must implement 'Sigma' and 'Sigma.new' in order to use the sigma notation.".to_string(),
|
||||||
Sugar::BoolIf => "You must implement 'Bool.if' in order to use the if notation.".to_string(),
|
Sugar::BoolIf => "You must implement 'Bool.if' in order to use the if notation.".to_string(),
|
||||||
Sugar::String => "You must implement 'String.cons' in order to use the string notation.".to_string(),
|
Sugar::String => "You must implement 'String.cons' in order to use the string notation.".to_string(),
|
||||||
Sugar::U120 => "You must implement 'U120.new' in order to use the u120 notation.".to_string(),
|
|
||||||
Sugar::Match(name) => format!("You must implement '{}.match' in order to use the match notation (or derive match with #derive[match]).", name),
|
Sugar::Match(name) => format!("You must implement '{}.match' in order to use the match notation (or derive match with #derive[match]).", name),
|
||||||
Sugar::Open(name) => format!("You must implement '{}.open' in order to use the open notation (or derive open with #derive[open]).", name),
|
Sugar::Open(name) => format!("You must implement '{}.open' in order to use the open notation (or derive open with #derive[open]).", name),
|
||||||
}],
|
}],
|
||||||
|
@ -17,6 +17,7 @@ pub fn compile_book(book: untyped::Book) -> File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_term(expr: &untyped::Expr) -> Box<Term> {
|
pub fn compile_term(expr: &untyped::Expr) -> Box<Term> {
|
||||||
|
use kind_tree::Number;
|
||||||
use untyped::ExprKind::*;
|
use untyped::ExprKind::*;
|
||||||
match &expr.data {
|
match &expr.data {
|
||||||
Var { name } => Box::new(Term::Var {
|
Var { name } => Box::new(Term::Var {
|
||||||
@ -41,10 +42,14 @@ pub fn compile_term(expr: &untyped::Expr) -> Box<Term> {
|
|||||||
expr: compile_term(val),
|
expr: compile_term(val),
|
||||||
body: compile_term(next),
|
body: compile_term(next),
|
||||||
}),
|
}),
|
||||||
Num { num: kind_tree::Number::U60(numb) } => Box::new(Term::U6O {
|
Num {
|
||||||
|
num: Number::U60(numb),
|
||||||
|
} => Box::new(Term::U6O {
|
||||||
numb: u60::new(*numb),
|
numb: u60::new(*numb),
|
||||||
}),
|
}),
|
||||||
Num { num: kind_tree::Number::U120(numb) } => {
|
Num {
|
||||||
|
num: Number::U120(numb),
|
||||||
|
} => {
|
||||||
let hi = Box::new(Term::U6O {
|
let hi = Box::new(Term::U6O {
|
||||||
numb: u60::new((numb >> 60) as u64),
|
numb: u60::new((numb >> 60) as u64),
|
||||||
});
|
});
|
||||||
|
@ -11,11 +11,13 @@ pub use kindelia_lang::ast as kdl;
|
|||||||
use crate::errors::KdlError;
|
use crate::errors::KdlError;
|
||||||
|
|
||||||
pub const KDL_NAME_LEN: usize = 12;
|
pub const KDL_NAME_LEN: usize = 12;
|
||||||
|
const U60_MAX: kdl::U120 = kdl::U120(0xFFFFFFFFFFFFFFF);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File {
|
pub struct File {
|
||||||
funs: LinkedHashMap<String, kdl::Statement>,
|
pub ctrs: LinkedHashMap<String, kdl::Statement>,
|
||||||
runs: Vec<kdl::Statement>,
|
pub funs: LinkedHashMap<String, kdl::Statement>,
|
||||||
|
pub runs: Vec<kdl::Statement>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CompileCtx<'a> {
|
pub struct CompileCtx<'a> {
|
||||||
@ -32,6 +34,7 @@ impl<'a> CompileCtx<'a> {
|
|||||||
pub fn new(book: &'a untyped::Book, sender: Sender<Box<dyn Diagnostic>>) -> CompileCtx<'a> {
|
pub fn new(book: &'a untyped::Book, sender: Sender<Box<dyn Diagnostic>>) -> CompileCtx<'a> {
|
||||||
CompileCtx {
|
CompileCtx {
|
||||||
file: File {
|
file: File {
|
||||||
|
ctrs: Default::default(),
|
||||||
funs: Default::default(),
|
funs: Default::default(),
|
||||||
runs: Default::default(),
|
runs: Default::default(),
|
||||||
},
|
},
|
||||||
@ -130,7 +133,7 @@ pub fn compile_rule(ctx: &mut CompileCtx, rule: &untyped::Rule) -> kindelia_lang
|
|||||||
let arg = compile_expr(ctx, pat);
|
let arg = compile_expr(ctx, pat);
|
||||||
args.push(arg);
|
args.push(arg);
|
||||||
}
|
}
|
||||||
let lhs = kdl::Term::fun(name, args);
|
let lhs = kdl::Term::ctr(name, args);
|
||||||
let rhs = compile_expr(ctx, &rule.body);
|
let rhs = compile_expr(ctx, &rule.body);
|
||||||
let rule = kdl::Rule { lhs, rhs };
|
let rule = kdl::Rule { lhs, rhs };
|
||||||
rule
|
rule
|
||||||
@ -143,51 +146,178 @@ pub fn err_term() -> kindelia_lang::ast::Term {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn compile_expr(ctx: &mut CompileCtx, expr: &untyped::Expr) -> kindelia_lang::ast::Term {
|
pub fn compile_expr(ctx: &mut CompileCtx, expr: &untyped::Expr) -> kindelia_lang::ast::Term {
|
||||||
use crate::untyped::ExprKind::*;
|
use crate::untyped::ExprKind as From;
|
||||||
use kdl::Term as T;
|
use kdl::Term as To;
|
||||||
match &expr.data {
|
match &expr.data {
|
||||||
App { fun, args } => {
|
From::App { fun, args } => {
|
||||||
let mut expr = compile_expr(ctx, fun);
|
let mut expr = compile_expr(ctx, fun);
|
||||||
for binding in args {
|
for binding in args {
|
||||||
let body = compile_expr(ctx, &binding);
|
let body = compile_expr(ctx, &binding);
|
||||||
expr = T::App {
|
expr = To::App {
|
||||||
func: Box::new(expr),
|
func: Box::new(expr),
|
||||||
argm: Box::new(body),
|
argm: Box::new(body),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
expr
|
expr
|
||||||
}
|
}
|
||||||
Binary { op, left, right } => {
|
From::Binary { op, left, right } => {
|
||||||
// TODO: Special compilation for U60 ops
|
use kind_tree::Operator as Op;
|
||||||
let oper = compile_oper(op);
|
let oper = compile_oper(op);
|
||||||
let val0 = Box::new(compile_expr(ctx, left));
|
match op {
|
||||||
let val1 = Box::new(compile_expr(ctx, right));
|
// These operations occupy more bits on overflow
|
||||||
T::Op2 { oper, val0, val1 }
|
// So we truncate them
|
||||||
|
Op::Add | Op::Sub | Op::Mul => {
|
||||||
|
let val0 = Box::new(compile_expr(ctx, left));
|
||||||
|
let val1 = Box::new(compile_expr(ctx, right));
|
||||||
|
let expr = Box::new(To::Op2 { oper, val0, val1 });
|
||||||
|
let trunc = Box::new(To::Num { numb: U60_MAX });
|
||||||
|
To::Op2 {
|
||||||
|
oper: kdl::Oper::And,
|
||||||
|
val0: expr,
|
||||||
|
val1: trunc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// These operations need to wrap around every 60 bits
|
||||||
|
// Eg: (<< n 60) = n
|
||||||
|
Op::Shl | Op::Shr => {
|
||||||
|
let val0 = Box::new(compile_expr(ctx, left));
|
||||||
|
let right = Box::new(compile_expr(ctx, right));
|
||||||
|
let sixty = Box::new(To::Num {
|
||||||
|
numb: kdl::U120(60),
|
||||||
|
});
|
||||||
|
let val1 = Box::new(To::Op2 {
|
||||||
|
oper: kdl::Oper::Mod,
|
||||||
|
val0: right,
|
||||||
|
val1: sixty,
|
||||||
|
});
|
||||||
|
To::Op2 { oper, val0, val1 }
|
||||||
|
}
|
||||||
|
// Other operations don't overflow
|
||||||
|
// Div, Mod, And, Or, Xor, Eql, Neq, Gtn, Gte, Ltn, Lte
|
||||||
|
_ => {
|
||||||
|
let val0 = Box::new(compile_expr(ctx, left));
|
||||||
|
let val1 = Box::new(compile_expr(ctx, right));
|
||||||
|
To::Op2 { oper, val0, val1 }
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ctr { name, args } => {
|
From::Ctr { name, args } => {
|
||||||
|
match name.to_str() {
|
||||||
|
// Special compilation for some numeric functions
|
||||||
|
// They have no rules because they're compilation defined,
|
||||||
|
// so they've been initially interpreted as Ctr
|
||||||
|
|
||||||
|
// Add with no boundary check is just a normal add
|
||||||
|
"U60.add_unsafe" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Add,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
// U60s are already stored in 120 bits
|
||||||
|
"U60.to_u120" => compile_expr(ctx, &args[0]),
|
||||||
|
|
||||||
|
// Truncate to 60 bits
|
||||||
|
"U120.to_u60" => To::Op2 {
|
||||||
|
oper: kdl::Oper::And,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(To::Num { numb: U60_MAX }),
|
||||||
|
},
|
||||||
|
// Compilation for U120 numeric operations
|
||||||
|
"U120.add" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Add,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.sub" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Add,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.mul" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Mul,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.div" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Div,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.mod" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Mod,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.num_equal" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Eql,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.num_not_equal" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Neq,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.shift_left" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Shl,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.shift_right" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Shr,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.num_less_than" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Ltn,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.num_less_equal" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Lte,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.num_greater_than" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Gtn,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.num_greater_equal" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Gte,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.bitwise_and" => To::Op2 {
|
||||||
|
oper: kdl::Oper::And,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.bitwise_or" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Or,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
"U120.bitwise_xor" => To::Op2 {
|
||||||
|
oper: kdl::Oper::Xor,
|
||||||
|
val0: Box::new(compile_expr(ctx, &args[0])),
|
||||||
|
val1: Box::new(compile_expr(ctx, &args[1])),
|
||||||
|
},
|
||||||
|
|
||||||
|
// All other constructors have a normal compilation
|
||||||
|
_ => {
|
||||||
|
let name = ctx.kdl_names.get(name.to_str()).unwrap().clone();
|
||||||
|
let args = args.iter().map(|x| compile_expr(ctx, &x)).collect();
|
||||||
|
To::Ctr { name, args }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
From::Fun { name, args } => {
|
||||||
let name = ctx.kdl_names.get(name.to_str()).unwrap().clone();
|
let name = ctx.kdl_names.get(name.to_str()).unwrap().clone();
|
||||||
let mut new_args = Vec::new();
|
let args = args.iter().map(|x| compile_expr(ctx, x)).collect();
|
||||||
for arg in args {
|
To::Fun { name, args }
|
||||||
new_args.push(compile_expr(ctx, &arg));
|
|
||||||
}
|
|
||||||
T::Ctr {
|
|
||||||
name,
|
|
||||||
args: new_args,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Fun { name, args } => {
|
From::Lambda {
|
||||||
// TODO: Special compilation for U60 and U120 ops
|
|
||||||
let name = ctx.kdl_names.get(name.to_str()).unwrap().clone();
|
|
||||||
let mut new_args = Vec::new();
|
|
||||||
for arg in args {
|
|
||||||
new_args.push(compile_expr(ctx, &arg));
|
|
||||||
}
|
|
||||||
T::Fun {
|
|
||||||
name,
|
|
||||||
args: new_args,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Lambda {
|
|
||||||
param,
|
param,
|
||||||
body,
|
body,
|
||||||
erased: _,
|
erased: _,
|
||||||
@ -195,44 +325,44 @@ pub fn compile_expr(ctx: &mut CompileCtx, expr: &untyped::Expr) -> kindelia_lang
|
|||||||
let name = kdl::Name::from_str(param.to_str());
|
let name = kdl::Name::from_str(param.to_str());
|
||||||
if let Ok(name) = name {
|
if let Ok(name) = name {
|
||||||
let body = Box::new(compile_expr(ctx, &body));
|
let body = Box::new(compile_expr(ctx, &body));
|
||||||
T::Lam { name, body }
|
To::Lam { name, body }
|
||||||
} else {
|
} else {
|
||||||
ctx.send_err(Box::new(KdlError::InvalidVarName(param.range)));
|
ctx.send_err(Box::new(KdlError::InvalidVarName(param.range)));
|
||||||
err_term()
|
err_term()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Let { name, val, next } => {
|
From::Let { name, val, next } => {
|
||||||
let res_name = kdl::Name::from_str(name.to_str());
|
let res_name = kdl::Name::from_str(name.to_str());
|
||||||
if let Ok(name) = res_name {
|
if let Ok(name) = res_name {
|
||||||
let expr = Box::new(compile_expr(ctx, &val));
|
let expr = Box::new(compile_expr(ctx, &val));
|
||||||
let func = Box::new(T::Lam { name, body: expr });
|
let func = Box::new(To::Lam { name, body: expr });
|
||||||
let argm = Box::new(compile_expr(ctx, next));
|
let argm = Box::new(compile_expr(ctx, next));
|
||||||
T::App { func, argm }
|
To::App { func, argm }
|
||||||
} else {
|
} else {
|
||||||
ctx.send_err(Box::new(KdlError::InvalidVarName(name.range)));
|
ctx.send_err(Box::new(KdlError::InvalidVarName(name.range)));
|
||||||
err_term()
|
err_term()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Num {
|
From::Num {
|
||||||
num: Number::U60(numb),
|
num: Number::U60(numb),
|
||||||
} => T::Num {
|
} => To::Num {
|
||||||
numb: kdl::U120(*numb as u128),
|
numb: kdl::U120(*numb as u128),
|
||||||
},
|
},
|
||||||
Num {
|
From::Num {
|
||||||
num: Number::U120(numb),
|
num: Number::U120(numb),
|
||||||
} => T::Num {
|
} => To::Num {
|
||||||
numb: kdl::U120(*numb),
|
numb: kdl::U120(*numb),
|
||||||
},
|
},
|
||||||
Var { name } => {
|
From::Var { name } => {
|
||||||
let res_name = kdl::Name::from_str(name.to_str());
|
let res_name = kdl::Name::from_str(name.to_str());
|
||||||
if let Ok(name) = res_name {
|
if let Ok(name) = res_name {
|
||||||
T::Var { name }
|
To::Var { name }
|
||||||
} else {
|
} else {
|
||||||
ctx.send_err(Box::new(KdlError::InvalidVarName(name.range)));
|
ctx.send_err(Box::new(KdlError::InvalidVarName(name.range)));
|
||||||
err_term()
|
err_term()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Str { val } => {
|
From::Str { val } => {
|
||||||
let nil = kdl::Term::Ctr {
|
let nil = kdl::Term::Ctr {
|
||||||
name: ctx.kdl_names.get("String.nil").unwrap().clone(),
|
name: ctx.kdl_names.get("String.nil").unwrap().clone(),
|
||||||
args: vec![],
|
args: vec![],
|
||||||
@ -252,7 +382,7 @@ pub fn compile_expr(ctx: &mut CompileCtx, expr: &untyped::Expr) -> kindelia_lang
|
|||||||
|
|
||||||
val.chars().rfold(nil, |rest, chr| cons(chr as u128, rest))
|
val.chars().rfold(nil, |rest, chr| cons(chr as u128, rest))
|
||||||
}
|
}
|
||||||
Err => unreachable!("Should not have errors inside generation"),
|
From::Err => unreachable!("Should not have errors inside generation"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,13 +415,15 @@ pub fn compile_entry(ctx: &mut CompileCtx, entry: &untyped::Entry) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if entry.rules.len() == 0 {
|
if entry.rules.len() == 0 {
|
||||||
|
// Functions with no rules become Ctr
|
||||||
let sttm = kdl::Statement::Ctr {
|
let sttm = kdl::Statement::Ctr {
|
||||||
name,
|
name,
|
||||||
args,
|
args,
|
||||||
sign: None,
|
sign: None,
|
||||||
};
|
};
|
||||||
ctx.file.funs.insert(entry.name.to_string(), sttm);
|
ctx.file.ctrs.insert(entry.name.to_string(), sttm);
|
||||||
} else {
|
} else {
|
||||||
|
// Functions with rules become Fun
|
||||||
let rules = entry
|
let rules = entry
|
||||||
.rules
|
.rules
|
||||||
.iter()
|
.iter()
|
||||||
@ -334,6 +466,14 @@ pub fn compile_entry(ctx: &mut CompileCtx, entry: &untyped::Entry) {
|
|||||||
|
|
||||||
impl Display for File {
|
impl Display for File {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
for ctr in &self.ctrs {
|
||||||
|
writeln!(f, "{}", ctr.1)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.ctrs.len() > 0 && self.funs.len() > 0 {
|
||||||
|
writeln!(f)?;
|
||||||
|
}
|
||||||
|
|
||||||
for fun in &self.funs {
|
for fun in &self.funs {
|
||||||
writeln!(f, "{}", fun.1)?;
|
writeln!(f, "{}", fun.1)?;
|
||||||
}
|
}
|
||||||
@ -345,24 +485,24 @@ impl Display for File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn compile_oper(oper: &kind_tree::Operator) -> kdl::Oper {
|
fn compile_oper(oper: &kind_tree::Operator) -> kdl::Oper {
|
||||||
use kdl::Oper as T;
|
use kdl::Oper as To;
|
||||||
use kind_tree::Operator as F;
|
use kind_tree::Operator as From;
|
||||||
match oper {
|
match oper {
|
||||||
F::Add => T::Add,
|
From::Add => To::Add,
|
||||||
F::Sub => T::Sub,
|
From::Sub => To::Sub,
|
||||||
F::Mul => T::Mul,
|
From::Mul => To::Mul,
|
||||||
F::Div => T::Div,
|
From::Div => To::Div,
|
||||||
F::Mod => T::Mod,
|
From::Mod => To::Mod,
|
||||||
F::Shl => T::Shl,
|
From::Shl => To::Shl,
|
||||||
F::Shr => T::Shr,
|
From::Shr => To::Shr,
|
||||||
F::Eql => T::Eql,
|
From::Eql => To::Eql,
|
||||||
F::Neq => T::Neq,
|
From::Neq => To::Neq,
|
||||||
F::Ltn => T::Ltn,
|
From::Ltn => To::Ltn,
|
||||||
F::Lte => T::Lte,
|
From::Lte => To::Lte,
|
||||||
F::Gte => T::Gte,
|
From::Gte => To::Gte,
|
||||||
F::Gtn => T::Gtn,
|
From::Gtn => To::Gtn,
|
||||||
F::And => T::And,
|
From::And => To::And,
|
||||||
F::Xor => T::Xor,
|
From::Xor => To::Xor,
|
||||||
F::Or => T::Or,
|
From::Or => To::Or,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use kind_report::data::{Diagnostic, DiagnosticFrame, Severity, Marker, Color};
|
use kind_report::data::{Color, Diagnostic, DiagnosticFrame, Marker, Severity};
|
||||||
use kind_span::Range;
|
use kind_span::Range;
|
||||||
|
|
||||||
pub enum KdlError {
|
pub enum KdlError {
|
||||||
@ -15,7 +15,6 @@ impl Diagnostic for KdlError {
|
|||||||
KdlError::ShouldNotHaveArguments(range) => Some(range.ctx),
|
KdlError::ShouldNotHaveArguments(range) => Some(range.ctx),
|
||||||
KdlError::ShouldHaveOnlyOneRule(range) => Some(range.ctx),
|
KdlError::ShouldHaveOnlyOneRule(range) => Some(range.ctx),
|
||||||
KdlError::NoInitEntry(range) => Some(range.ctx),
|
KdlError::NoInitEntry(range) => Some(range.ctx),
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,4 +78,4 @@ impl Diagnostic for KdlError {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
use kind_span::Range;
|
use kind_span::Range;
|
||||||
use kind_tree::symbol::{Ident, QualifiedIdent};
|
use kind_tree::symbol::{Ident, QualifiedIdent};
|
||||||
use kind_tree::untyped::{self, Entry, Expr, ExprKind, Rule, Book};
|
use kind_tree::untyped::{self, Book, Entry, Expr, ExprKind, Rule};
|
||||||
use linked_hash_map::LinkedHashMap;
|
use linked_hash_map::LinkedHashMap;
|
||||||
|
|
||||||
use crate::subst::subst;
|
use crate::subst::subst;
|
||||||
@ -94,7 +94,9 @@ fn split_rule(
|
|||||||
Expr::var(name)
|
Expr::var(name)
|
||||||
}
|
}
|
||||||
ExprKind::Var { .. } => field.clone(),
|
ExprKind::Var { .. } => field.clone(),
|
||||||
_ => panic!("Internal Error: Cannot use this kind of expression during flattening"),
|
_ => panic!(
|
||||||
|
"Internal Error: Cannot use this kind of expression during flattening"
|
||||||
|
),
|
||||||
};
|
};
|
||||||
new_pat_args.push(arg.clone());
|
new_pat_args.push(arg.clone());
|
||||||
old_rule_body_args.push(arg);
|
old_rule_body_args.push(arg);
|
||||||
@ -170,7 +172,9 @@ fn split_rule(
|
|||||||
|
|
||||||
assert!(!new_entry_rules.is_empty());
|
assert!(!new_entry_rules.is_empty());
|
||||||
|
|
||||||
let new_entry_args = (0..new_entry_rules[0].pats.len()).map(|n| (format!("x{}", n), Range::ghost_range(), false)).collect();
|
let new_entry_args = (0..new_entry_rules[0].pats.len())
|
||||||
|
.map(|n| (format!("x{}", n), Range::ghost_range(), false))
|
||||||
|
.collect();
|
||||||
|
|
||||||
let new_entry = Entry {
|
let new_entry = Entry {
|
||||||
name: new_entry_name,
|
name: new_entry_name,
|
||||||
@ -186,7 +190,7 @@ fn split_rule(
|
|||||||
|
|
||||||
fn flatten_entry(entry: &Entry) -> Vec<Entry> {
|
fn flatten_entry(entry: &Entry) -> Vec<Entry> {
|
||||||
let mut name_count = 0;
|
let mut name_count = 0;
|
||||||
|
|
||||||
let mut skip: FxHashSet<usize> = FxHashSet::default();
|
let mut skip: FxHashSet<usize> = FxHashSet::default();
|
||||||
let mut new_entries: Vec<Entry> = Vec::new();
|
let mut new_entries: Vec<Entry> = Vec::new();
|
||||||
let mut old_entry_rules: Vec<Rule> = Vec::new();
|
let mut old_entry_rules: Vec<Rule> = Vec::new();
|
||||||
@ -195,7 +199,8 @@ fn flatten_entry(entry: &Entry) -> Vec<Entry> {
|
|||||||
if !skip.contains(&i) {
|
if !skip.contains(&i) {
|
||||||
let rule = &entry.rules[i];
|
let rule = &entry.rules[i];
|
||||||
if must_split(rule) {
|
if must_split(rule) {
|
||||||
let (old_rule, split_entries) = split_rule(rule, &entry, i, &mut name_count, &mut skip);
|
let (old_rule, split_entries) =
|
||||||
|
split_rule(rule, &entry, i, &mut name_count, &mut skip);
|
||||||
old_entry_rules.push(old_rule);
|
old_entry_rules.push(old_rule);
|
||||||
new_entries.extend(split_entries);
|
new_entries.extend(split_entries);
|
||||||
} else {
|
} else {
|
||||||
@ -216,7 +221,7 @@ fn flatten_entry(entry: &Entry) -> Vec<Entry> {
|
|||||||
new_entries
|
new_entries
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flatten(book: untyped::Book) -> untyped::Book {
|
pub fn flatten(book: untyped::Book) -> untyped::Book {
|
||||||
let mut book = book;
|
let mut book = book;
|
||||||
let mut names = FxHashMap::default();
|
let mut names = FxHashMap::default();
|
||||||
let mut entrs = LinkedHashMap::default();
|
let mut entrs = LinkedHashMap::default();
|
||||||
|
@ -2,16 +2,29 @@ use std::sync::mpsc::Sender;
|
|||||||
|
|
||||||
use flatten::flatten;
|
use flatten::flatten;
|
||||||
use kind_report::data::Diagnostic;
|
use kind_report::data::Diagnostic;
|
||||||
use kind_tree::{untyped};
|
use kind_tree::untyped;
|
||||||
|
|
||||||
pub use compile::File;
|
pub use compile::File;
|
||||||
|
|
||||||
mod compile;
|
mod compile;
|
||||||
mod flatten;
|
|
||||||
mod subst;
|
|
||||||
mod errors;
|
mod errors;
|
||||||
|
mod flatten;
|
||||||
|
mod linearize;
|
||||||
|
mod subst;
|
||||||
|
|
||||||
pub fn compile_book(book: untyped::Book, sender: Sender<Box<dyn Diagnostic>>, namespace: &str) -> Option<compile::File> {
|
pub fn compile_book(
|
||||||
|
book: untyped::Book,
|
||||||
|
sender: Sender<Box<dyn Diagnostic>>,
|
||||||
|
namespace: &str,
|
||||||
|
) -> Option<compile::File> {
|
||||||
|
// TODO: Inlining
|
||||||
|
// TODO: Remove kdl_states (maybe check if they're ever called?)
|
||||||
|
// TODO: Don't erase kdl_state functions
|
||||||
|
// TODO: Convert to some sort of Kindelia.Contract
|
||||||
let flattened = flatten(book);
|
let flattened = flatten(book);
|
||||||
compile::compile_book(&flattened, sender, namespace)
|
|
||||||
|
|
||||||
|
let file = compile::compile_book(&flattened, sender, namespace)?;
|
||||||
|
let file = linearize::linearize_file(file);
|
||||||
|
Some(file)
|
||||||
}
|
}
|
||||||
|
329
crates/kind-target-kdl/src/linearize.rs
Normal file
329
crates/kind-target-kdl/src/linearize.rs
Normal file
@ -0,0 +1,329 @@
|
|||||||
|
// This modules makes all variable usages linear and with a unique name. That has the following effect:
|
||||||
|
// - All variables are renamed to have a global unique name.
|
||||||
|
// - All variables are linearized.
|
||||||
|
// - If they're used more than once, dups are inserted.
|
||||||
|
// - If they're used once, nothing changes.
|
||||||
|
// - If they're never used, their name is changed to "*"
|
||||||
|
// Example:
|
||||||
|
// - sanitizing: `(Foo a b) = (+ a a)`
|
||||||
|
// - results in: `(Foo x0 *) = dup x0.0 x0.1 = x0; (+ x0.0 x0.1)`
|
||||||
|
// The algorithm was copied from the hvm
|
||||||
|
|
||||||
|
// TODO: This is inserting unneeded `let`s for all linear rule variables
|
||||||
|
|
||||||
|
use crate::File;
|
||||||
|
use fxhash::FxHashMap;
|
||||||
|
use kindelia_lang::ast::{Func, Name, Rule, Statement, Term};
|
||||||
|
use linked_hash_map::LinkedHashMap;
|
||||||
|
|
||||||
|
pub struct LinearizeCtx {
|
||||||
|
uses: FxHashMap<Name, u64>,
|
||||||
|
name_table: LinkedHashMap<Name, Name>,
|
||||||
|
name_count: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LinearizeCtx {
|
||||||
|
fn create_name(&mut self) -> Name {
|
||||||
|
let name = Name::from_str(&format!("x{}", self.name_count)).unwrap();
|
||||||
|
self.name_count += 1;
|
||||||
|
name
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
LinearizeCtx {
|
||||||
|
uses: Default::default(),
|
||||||
|
name_table: Default::default(),
|
||||||
|
name_count: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass through the lhs of the function generating new names
|
||||||
|
// for every variable found in the style described before with
|
||||||
|
// the fresh function. Also checks if rule's left side is valid.
|
||||||
|
fn create_param_names(&mut self, rule: &Rule) {
|
||||||
|
if let Term::Ctr { name: _, args } = &rule.lhs {
|
||||||
|
for arg in args {
|
||||||
|
match arg {
|
||||||
|
Term::Var { name } => {
|
||||||
|
let new_name = self.create_name();
|
||||||
|
self.name_table.insert(name.clone(), new_name);
|
||||||
|
}
|
||||||
|
Term::Ctr { name: _, args } => {
|
||||||
|
for arg in args {
|
||||||
|
if let Term::Var { name } = arg {
|
||||||
|
let new_name = self.create_name();
|
||||||
|
self.name_table.insert(name.clone(), new_name);
|
||||||
|
} else {
|
||||||
|
unreachable!(); // We expect a flat rule
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Num { .. } => (),
|
||||||
|
_ => unreachable!(
|
||||||
|
"Invalid left-hand side parameter. Expected Var, Ctr or Num, got {:?}",
|
||||||
|
arg
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!(
|
||||||
|
"Invalid left-hand side term. Expected Ctr, got {:?}",
|
||||||
|
rule.lhs
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linearize_file(file: File) -> File {
|
||||||
|
let mut runs = Vec::new();
|
||||||
|
for stmt in file.runs {
|
||||||
|
if let Statement::Run { expr, sign: _ } = stmt {
|
||||||
|
let expr = linearize_term_independent(&expr);
|
||||||
|
let stmt = Statement::Run {
|
||||||
|
expr: *expr,
|
||||||
|
sign: None,
|
||||||
|
};
|
||||||
|
runs.push(stmt);
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut funs: LinkedHashMap<_, _> = Default::default();
|
||||||
|
for (kind_name, stmt) in file.funs {
|
||||||
|
if let Statement::Fun {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
func,
|
||||||
|
init,
|
||||||
|
sign: _,
|
||||||
|
} = stmt
|
||||||
|
{
|
||||||
|
let init = init.map(|x| *linearize_term_independent(&x));
|
||||||
|
let mut rules: Vec<_> = Default::default();
|
||||||
|
for rule in func.rules {
|
||||||
|
let rule = linearize_rule(rule);
|
||||||
|
rules.push(rule);
|
||||||
|
}
|
||||||
|
let func = Func { rules };
|
||||||
|
let stmt = Statement::Fun {
|
||||||
|
name,
|
||||||
|
args,
|
||||||
|
func,
|
||||||
|
init,
|
||||||
|
sign: None,
|
||||||
|
};
|
||||||
|
funs.insert(kind_name, stmt);
|
||||||
|
} else {
|
||||||
|
unreachable!("Expected list of Funs, found {:?}", stmt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let ctrs = file.ctrs;
|
||||||
|
File { ctrs, funs, runs }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linearize_rule(rule: Rule) -> Rule {
|
||||||
|
let mut ctx = LinearizeCtx::new();
|
||||||
|
ctx.create_param_names(&rule);
|
||||||
|
let mut rhs = linearize_term(&mut ctx, &rule.rhs, false);
|
||||||
|
let lhs = linearize_term(&mut ctx, &rule.lhs, true);
|
||||||
|
let vals: Vec<Name> = ctx.name_table.values().map(Name::clone).collect();
|
||||||
|
for val in vals {
|
||||||
|
let expr = Box::new(Term::Var { name: val.clone() });
|
||||||
|
rhs = dup_var(&mut ctx, &val, expr, rhs);
|
||||||
|
}
|
||||||
|
Rule {
|
||||||
|
lhs: *lhs,
|
||||||
|
rhs: *rhs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn linearize_term(ctx: &mut LinearizeCtx, term: &Term, lhs: bool) -> Box<Term> {
|
||||||
|
let term = match term {
|
||||||
|
Term::Var { name } => {
|
||||||
|
if lhs {
|
||||||
|
let mut name = ctx.name_table.get(name).unwrap_or(name).clone();
|
||||||
|
rename_erased(ctx, &mut name);
|
||||||
|
Term::Var { name }
|
||||||
|
} else {
|
||||||
|
// create a var with the name generated before
|
||||||
|
// concatenated with '.{{times_used}}'
|
||||||
|
if let Some(name) = ctx.name_table.get(name) {
|
||||||
|
let used = *ctx
|
||||||
|
.uses
|
||||||
|
.entry(name.clone())
|
||||||
|
.and_modify(|x| *x += 1)
|
||||||
|
.or_insert(1);
|
||||||
|
let name = Name::from_str(&format!("{}.{}", name, used - 1)).unwrap(); // TODO: Think if this errs or not
|
||||||
|
Term::Var { name }
|
||||||
|
} else {
|
||||||
|
unreachable!("Unbound variable '{}' in kdl compilation", name.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Dup {
|
||||||
|
nam0,
|
||||||
|
nam1,
|
||||||
|
expr,
|
||||||
|
body,
|
||||||
|
} => {
|
||||||
|
let new_nam0 = ctx.create_name();
|
||||||
|
let new_nam1 = ctx.create_name();
|
||||||
|
let expr = linearize_term(ctx, expr, lhs);
|
||||||
|
let got_0 = ctx.name_table.remove(nam0);
|
||||||
|
let got_1 = ctx.name_table.remove(nam0);
|
||||||
|
ctx.name_table.insert(nam0.clone(), new_nam0.clone());
|
||||||
|
ctx.name_table.insert(nam1.clone(), new_nam1.clone());
|
||||||
|
let body = linearize_term(ctx, body, lhs);
|
||||||
|
ctx.name_table.remove(nam0);
|
||||||
|
if let Some(x) = got_0 {
|
||||||
|
ctx.name_table.insert(nam0.clone(), x);
|
||||||
|
}
|
||||||
|
ctx.name_table.remove(nam1);
|
||||||
|
if let Some(x) = got_1 {
|
||||||
|
ctx.name_table.insert(nam1.clone(), x);
|
||||||
|
}
|
||||||
|
let nam0 = Name::from_str(&format!("{}{}", new_nam0, ".0")).unwrap();
|
||||||
|
let nam1 = Name::from_str(&format!("{}{}", new_nam1, ".0")).unwrap();
|
||||||
|
Term::Dup {
|
||||||
|
nam0,
|
||||||
|
nam1,
|
||||||
|
expr,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Lam { name, body } => {
|
||||||
|
let mut new_name = ctx.create_name();
|
||||||
|
let got_name = ctx.name_table.remove(name);
|
||||||
|
ctx.name_table.insert(name.clone(), new_name.clone());
|
||||||
|
let body = linearize_term(ctx, body, lhs);
|
||||||
|
ctx.name_table.remove(name);
|
||||||
|
if let Some(x) = got_name {
|
||||||
|
ctx.name_table.insert(name.clone(), x);
|
||||||
|
}
|
||||||
|
let expr = Box::new(Term::Var {
|
||||||
|
name: new_name.clone(),
|
||||||
|
});
|
||||||
|
let body = dup_var(ctx, &new_name, expr, body);
|
||||||
|
rename_erased(ctx, &mut new_name);
|
||||||
|
Term::Lam {
|
||||||
|
name: new_name,
|
||||||
|
body,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::App { func, argm } => {
|
||||||
|
let func = linearize_term(ctx, func, lhs);
|
||||||
|
let argm = linearize_term(ctx, argm, lhs);
|
||||||
|
Term::App { func, argm }
|
||||||
|
}
|
||||||
|
Term::Ctr { name, args } => {
|
||||||
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
for arg in args {
|
||||||
|
let arg = linearize_term(ctx, arg, lhs);
|
||||||
|
new_args.push(*arg);
|
||||||
|
}
|
||||||
|
Term::Ctr {
|
||||||
|
name: name.clone(),
|
||||||
|
args: new_args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Fun { name, args } => {
|
||||||
|
let mut new_args = Vec::with_capacity(args.len());
|
||||||
|
for arg in args {
|
||||||
|
let arg = linearize_term(ctx, arg, lhs);
|
||||||
|
new_args.push(*arg);
|
||||||
|
}
|
||||||
|
Term::Fun {
|
||||||
|
name: name.clone(),
|
||||||
|
args: new_args,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Term::Num { numb } => Term::Num { numb: *numb },
|
||||||
|
Term::Op2 { oper, val0, val1 } => {
|
||||||
|
let val0 = linearize_term(ctx, val0, lhs);
|
||||||
|
let val1 = linearize_term(ctx, val1, lhs);
|
||||||
|
Term::Op2 {
|
||||||
|
oper: *oper,
|
||||||
|
val0,
|
||||||
|
val1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Box::new(term)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Linearize a term that is not part of a rule, so it doesn't need a shared context
|
||||||
|
pub fn linearize_term_independent(term: &Term) -> Box<Term> {
|
||||||
|
linearize_term(&mut LinearizeCtx::new(), term, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename_erased(ctx: &LinearizeCtx, name: &mut Name) {
|
||||||
|
if ctx.uses.get(name).copied() <= Some(0) {
|
||||||
|
*name = Name::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Duplicates all variables that are used more than once.
|
||||||
|
// The process is done generating auxiliary variables and
|
||||||
|
// applying dup on them.
|
||||||
|
pub fn dup_var(ctx: &mut LinearizeCtx, name: &Name, expr: Box<Term>, body: Box<Term>) -> Box<Term> {
|
||||||
|
if let Some(amount) = ctx.uses.get(name).copied() {
|
||||||
|
match amount {
|
||||||
|
// if not used nothing is done
|
||||||
|
0 => body,
|
||||||
|
// if used once just make a let (lambda then app)
|
||||||
|
1 => {
|
||||||
|
let name = Name::from_str(&format!("{}.0", name)).unwrap(); // TODO: handle err
|
||||||
|
let func = Box::new(Term::Lam { name, body: expr });
|
||||||
|
let term = Term::App { func, argm: body };
|
||||||
|
Box::new(term)
|
||||||
|
}
|
||||||
|
// if used more than once, duplicate
|
||||||
|
_ => {
|
||||||
|
let dup_times = amount - 1;
|
||||||
|
let aux_amount = amount - 2; // quantity of aux variables
|
||||||
|
let mut vars = vec![];
|
||||||
|
// generate name for duplicated variables
|
||||||
|
for i in (aux_amount..(dup_times * 2)).rev() {
|
||||||
|
let i = i - aux_amount; // moved to 0,1,..
|
||||||
|
let key = Name::from_str(&format!("{}.{}", name, i)).unwrap();
|
||||||
|
vars.push(key);
|
||||||
|
}
|
||||||
|
// generate name for aux variables
|
||||||
|
for i in (0..aux_amount).rev() {
|
||||||
|
let key = Name::from_str(&format!("c.{}", i)).unwrap();
|
||||||
|
vars.push(key);
|
||||||
|
}
|
||||||
|
// use aux variables to duplicate the variable
|
||||||
|
let term = Term::Dup {
|
||||||
|
nam0: vars.pop().unwrap(),
|
||||||
|
nam1: vars.pop().unwrap(),
|
||||||
|
expr,
|
||||||
|
body: dup_var_go(1, dup_times, body, &mut vars),
|
||||||
|
};
|
||||||
|
Box::new(term)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive aux function to duplicate one variable
|
||||||
|
// an amount of times
|
||||||
|
fn dup_var_go(idx: u64, dup_times: u64, body: Box<Term>, vars: &mut Vec<Name>) -> Box<Term> {
|
||||||
|
if idx == dup_times {
|
||||||
|
body
|
||||||
|
} else {
|
||||||
|
let nam0 = vars.pop().unwrap();
|
||||||
|
let nam1 = vars.pop().unwrap();
|
||||||
|
let var_name = Name::from_str(&format!("c.{}", idx - 1)).unwrap();
|
||||||
|
let expr = Box::new(Term::Var { name: var_name });
|
||||||
|
let dup = Term::Dup {
|
||||||
|
nam0,
|
||||||
|
nam1,
|
||||||
|
expr,
|
||||||
|
body: dup_var_go(idx + 1, dup_times, body, vars),
|
||||||
|
};
|
||||||
|
Box::new(dup)
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
use kind_tree::{untyped::Expr, symbol::Ident};
|
use kind_tree::{symbol::Ident, untyped::Expr};
|
||||||
|
|
||||||
pub fn subst(term: &mut Expr, from: &Ident, to: &Expr) {
|
pub fn subst(term: &mut Expr, from: &Ident, to: &Expr) {
|
||||||
use kind_tree::untyped::ExprKind::*;
|
use kind_tree::untyped::ExprKind::*;
|
||||||
@ -24,19 +24,19 @@ pub fn subst(term: &mut Expr, from: &Ident, to: &Expr) {
|
|||||||
subst(next, from, to);
|
subst(next, from, to);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Binary { op: _, left, right } => {
|
Binary { op: _, left, right } => {
|
||||||
subst(left, from, to);
|
subst(left, from, to);
|
||||||
subst(right, from, to);
|
subst(right, from, to);
|
||||||
}
|
}
|
||||||
|
|
||||||
Lambda { param, body, .. } if param.to_str() != from.to_str() => subst(body, from, to),
|
Lambda { param, body, .. } if param.to_str() != from.to_str() => subst(body, from, to),
|
||||||
|
|
||||||
Num { .. } => (),
|
Num { .. } => (),
|
||||||
Str { .. } => (),
|
Str { .. } => (),
|
||||||
Var { .. } => (),
|
Var { .. } => (),
|
||||||
Lambda { .. } => (),
|
Lambda { .. } => (),
|
||||||
|
|
||||||
Err => unreachable!("Err should not be used inside the compiledr"),
|
Err => unreachable!("Err should not be used inside the compiledr"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -268,7 +268,9 @@ impl Display for Book {
|
|||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||||
for entr in self.entrs.values() {
|
for entr in self.entrs.values() {
|
||||||
if !entr.rules.is_empty() {
|
if !entr.rules.is_empty() {
|
||||||
writeln!(f, "{}\n", entr)?;
|
writeln!(f, "{}", entr)?;
|
||||||
|
} else {
|
||||||
|
writeln!(f, "ctr {}", entr.name)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
Loading…
Reference in New Issue
Block a user