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> {
|
||||
mk_lifted_ctr(
|
||||
"Kind.Term.set_origin".to_owned(),
|
||||
vec![
|
||||
range_to_num(ident.range),
|
||||
mk_var(ident.to_str()),
|
||||
],
|
||||
vec![range_to_num(ident.range), mk_var(ident.to_str())],
|
||||
)
|
||||
}
|
||||
|
||||
@ -151,10 +148,16 @@ fn codegen_all_expr(
|
||||
) -> Box<Term> {
|
||||
use kind_tree::desugared::ExprKind::*;
|
||||
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 {
|
||||
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 {
|
||||
typ: kind_tree::NumType::U120,
|
||||
} => mk_lifted_ctr(
|
||||
@ -325,7 +328,10 @@ fn codegen_all_expr(
|
||||
vec![range_to_num(expr.range), mk_u60(*num)],
|
||||
),
|
||||
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"),
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ pub fn run_cli(config: Cli) {
|
||||
}
|
||||
Command::ToHVM { file } => {
|
||||
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))
|
||||
})
|
||||
.map(|res| {
|
||||
@ -194,7 +194,6 @@ pub fn run_cli(config: Cli) {
|
||||
let book = driver::erase_book(
|
||||
session,
|
||||
&PathBuf::from(file.clone()),
|
||||
&["Main".to_string()],
|
||||
)?;
|
||||
driver::check_main_entry(session, &book)?;
|
||||
Some(driver::compile_book_to_hvm(book))
|
||||
@ -227,7 +226,7 @@ pub fn run_cli(config: Cli) {
|
||||
}
|
||||
Command::Erase { file } => {
|
||||
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| {
|
||||
print!("{}", res);
|
||||
|
@ -75,7 +75,7 @@ fn test_eval() -> Result<(), Error> {
|
||||
let root = PathBuf::from(".");
|
||||
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);
|
||||
|
||||
let diagnostics = tx.try_iter().collect::<Vec<_>>();
|
||||
|
@ -1,12 +1,12 @@
|
||||
use checker::eval;
|
||||
use errors::DriverError;
|
||||
use kind_pass::{desugar, erasure, expand};
|
||||
use kind_report::{report::FileCache};
|
||||
use kind_report::report::FileCache;
|
||||
use kind_span::SyntaxCtxIndex;
|
||||
|
||||
use kind_tree::{backend, concrete, desugared, untyped};
|
||||
use session::Session;
|
||||
use std::path::{PathBuf};
|
||||
use std::path::PathBuf;
|
||||
|
||||
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(
|
||||
session: &mut Session,
|
||||
path: &PathBuf,
|
||||
entrypoint: &[String],
|
||||
) -> Option<untyped::Book> {
|
||||
let concrete_book = to_book(session, path)?;
|
||||
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 desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?;
|
||||
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)
|
||||
}
|
||||
|
||||
|
@ -23,12 +23,7 @@ impl<'a> DesugarState<'a> {
|
||||
literal: &expr::Literal,
|
||||
) -> Box<desugared::Expr> {
|
||||
match literal {
|
||||
Literal::Number(kind_tree::Number::U120(num)) => {
|
||||
if !self.check_implementation("U120.new", range, Sugar::U120) {
|
||||
return desugared::Expr::err(range);
|
||||
}
|
||||
desugared::Expr::num120(range, *num)
|
||||
}
|
||||
Literal::Number(kind_tree::Number::U120(num)) => desugared::Expr::num120(range, *num),
|
||||
Literal::String(string) => {
|
||||
if !self.check_implementation("String.cons", range, Sugar::String)
|
||||
|| !self.check_implementation("String.nil", range, Sugar::String)
|
||||
|
@ -106,7 +106,16 @@ impl<'a> ErasureState<'a> {
|
||||
entrypoints.push(id);
|
||||
}
|
||||
|
||||
// Kdl specific things.
|
||||
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 {
|
||||
let id = self.get_edge_or_create(&entr.name);
|
||||
self.set_relevance(id, Relevance::Relevant, entr.name.range);
|
||||
|
@ -9,7 +9,6 @@ pub enum Sugar {
|
||||
Pair,
|
||||
BoolIf,
|
||||
String,
|
||||
U120,
|
||||
Match(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::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::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::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> {
|
||||
use kind_tree::Number;
|
||||
use untyped::ExprKind::*;
|
||||
match &expr.data {
|
||||
Var { name } => Box::new(Term::Var {
|
||||
@ -41,10 +42,14 @@ pub fn compile_term(expr: &untyped::Expr) -> Box<Term> {
|
||||
expr: compile_term(val),
|
||||
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),
|
||||
}),
|
||||
Num { num: kind_tree::Number::U120(numb) } => {
|
||||
Num {
|
||||
num: Number::U120(numb),
|
||||
} => {
|
||||
let hi = Box::new(Term::U6O {
|
||||
numb: u60::new((numb >> 60) as u64),
|
||||
});
|
||||
|
@ -11,11 +11,13 @@ pub use kindelia_lang::ast as kdl;
|
||||
use crate::errors::KdlError;
|
||||
|
||||
pub const KDL_NAME_LEN: usize = 12;
|
||||
const U60_MAX: kdl::U120 = kdl::U120(0xFFFFFFFFFFFFFFF);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct File {
|
||||
funs: LinkedHashMap<String, kdl::Statement>,
|
||||
runs: Vec<kdl::Statement>,
|
||||
pub ctrs: LinkedHashMap<String, kdl::Statement>,
|
||||
pub funs: LinkedHashMap<String, kdl::Statement>,
|
||||
pub runs: Vec<kdl::Statement>,
|
||||
}
|
||||
|
||||
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> {
|
||||
CompileCtx {
|
||||
file: File {
|
||||
ctrs: Default::default(),
|
||||
funs: 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);
|
||||
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 rule = kdl::Rule { lhs, rhs };
|
||||
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 {
|
||||
use crate::untyped::ExprKind::*;
|
||||
use kdl::Term as T;
|
||||
use crate::untyped::ExprKind as From;
|
||||
use kdl::Term as To;
|
||||
match &expr.data {
|
||||
App { fun, args } => {
|
||||
From::App { fun, args } => {
|
||||
let mut expr = compile_expr(ctx, fun);
|
||||
for binding in args {
|
||||
let body = compile_expr(ctx, &binding);
|
||||
expr = T::App {
|
||||
expr = To::App {
|
||||
func: Box::new(expr),
|
||||
argm: Box::new(body),
|
||||
};
|
||||
}
|
||||
expr
|
||||
}
|
||||
Binary { op, left, right } => {
|
||||
// TODO: Special compilation for U60 ops
|
||||
From::Binary { op, left, right } => {
|
||||
use kind_tree::Operator as Op;
|
||||
let oper = compile_oper(op);
|
||||
match op {
|
||||
// These operations occupy more bits on overflow
|
||||
// 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));
|
||||
T::Op2 { oper, val0, val1 }
|
||||
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,
|
||||
}
|
||||
Ctr { name, args } => {
|
||||
}
|
||||
// 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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
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 mut new_args = Vec::new();
|
||||
for arg in args {
|
||||
new_args.push(compile_expr(ctx, &arg));
|
||||
}
|
||||
T::Ctr {
|
||||
name,
|
||||
args: new_args,
|
||||
let args = args.iter().map(|x| compile_expr(ctx, &x)).collect();
|
||||
To::Ctr { name, args }
|
||||
}
|
||||
}
|
||||
Fun { name, args } => {
|
||||
// TODO: Special compilation for U60 and U120 ops
|
||||
}
|
||||
From::Fun { name, args } => {
|
||||
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));
|
||||
let args = args.iter().map(|x| compile_expr(ctx, x)).collect();
|
||||
To::Fun { name, args }
|
||||
}
|
||||
T::Fun {
|
||||
name,
|
||||
args: new_args,
|
||||
}
|
||||
}
|
||||
Lambda {
|
||||
From::Lambda {
|
||||
param,
|
||||
body,
|
||||
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());
|
||||
if let Ok(name) = name {
|
||||
let body = Box::new(compile_expr(ctx, &body));
|
||||
T::Lam { name, body }
|
||||
To::Lam { name, body }
|
||||
} else {
|
||||
ctx.send_err(Box::new(KdlError::InvalidVarName(param.range)));
|
||||
err_term()
|
||||
}
|
||||
}
|
||||
Let { name, val, next } => {
|
||||
From::Let { name, val, next } => {
|
||||
let res_name = kdl::Name::from_str(name.to_str());
|
||||
if let Ok(name) = res_name {
|
||||
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));
|
||||
T::App { func, argm }
|
||||
To::App { func, argm }
|
||||
} else {
|
||||
ctx.send_err(Box::new(KdlError::InvalidVarName(name.range)));
|
||||
err_term()
|
||||
}
|
||||
}
|
||||
Num {
|
||||
From::Num {
|
||||
num: Number::U60(numb),
|
||||
} => T::Num {
|
||||
} => To::Num {
|
||||
numb: kdl::U120(*numb as u128),
|
||||
},
|
||||
Num {
|
||||
From::Num {
|
||||
num: Number::U120(numb),
|
||||
} => T::Num {
|
||||
} => To::Num {
|
||||
numb: kdl::U120(*numb),
|
||||
},
|
||||
Var { name } => {
|
||||
From::Var { name } => {
|
||||
let res_name = kdl::Name::from_str(name.to_str());
|
||||
if let Ok(name) = res_name {
|
||||
T::Var { name }
|
||||
To::Var { name }
|
||||
} else {
|
||||
ctx.send_err(Box::new(KdlError::InvalidVarName(name.range)));
|
||||
err_term()
|
||||
}
|
||||
}
|
||||
Str { val } => {
|
||||
From::Str { val } => {
|
||||
let nil = kdl::Term::Ctr {
|
||||
name: ctx.kdl_names.get("String.nil").unwrap().clone(),
|
||||
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))
|
||||
}
|
||||
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 {
|
||||
// Functions with no rules become Ctr
|
||||
let sttm = kdl::Statement::Ctr {
|
||||
name,
|
||||
args,
|
||||
sign: None,
|
||||
};
|
||||
ctx.file.funs.insert(entry.name.to_string(), sttm);
|
||||
ctx.file.ctrs.insert(entry.name.to_string(), sttm);
|
||||
} else {
|
||||
// Functions with rules become Fun
|
||||
let rules = entry
|
||||
.rules
|
||||
.iter()
|
||||
@ -334,6 +466,14 @@ pub fn compile_entry(ctx: &mut CompileCtx, entry: &untyped::Entry) {
|
||||
|
||||
impl Display for File {
|
||||
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 {
|
||||
writeln!(f, "{}", fun.1)?;
|
||||
}
|
||||
@ -345,24 +485,24 @@ impl Display for File {
|
||||
}
|
||||
|
||||
fn compile_oper(oper: &kind_tree::Operator) -> kdl::Oper {
|
||||
use kdl::Oper as T;
|
||||
use kind_tree::Operator as F;
|
||||
use kdl::Oper as To;
|
||||
use kind_tree::Operator as From;
|
||||
match oper {
|
||||
F::Add => T::Add,
|
||||
F::Sub => T::Sub,
|
||||
F::Mul => T::Mul,
|
||||
F::Div => T::Div,
|
||||
F::Mod => T::Mod,
|
||||
F::Shl => T::Shl,
|
||||
F::Shr => T::Shr,
|
||||
F::Eql => T::Eql,
|
||||
F::Neq => T::Neq,
|
||||
F::Ltn => T::Ltn,
|
||||
F::Lte => T::Lte,
|
||||
F::Gte => T::Gte,
|
||||
F::Gtn => T::Gtn,
|
||||
F::And => T::And,
|
||||
F::Xor => T::Xor,
|
||||
F::Or => T::Or,
|
||||
From::Add => To::Add,
|
||||
From::Sub => To::Sub,
|
||||
From::Mul => To::Mul,
|
||||
From::Div => To::Div,
|
||||
From::Mod => To::Mod,
|
||||
From::Shl => To::Shl,
|
||||
From::Shr => To::Shr,
|
||||
From::Eql => To::Eql,
|
||||
From::Neq => To::Neq,
|
||||
From::Ltn => To::Ltn,
|
||||
From::Lte => To::Lte,
|
||||
From::Gte => To::Gte,
|
||||
From::Gtn => To::Gtn,
|
||||
From::And => To::And,
|
||||
From::Xor => To::Xor,
|
||||
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;
|
||||
|
||||
pub enum KdlError {
|
||||
@ -15,7 +15,6 @@ impl Diagnostic for KdlError {
|
||||
KdlError::ShouldNotHaveArguments(range) => Some(range.ctx),
|
||||
KdlError::ShouldHaveOnlyOneRule(range) => Some(range.ctx),
|
||||
KdlError::NoInitEntry(range) => Some(range.ctx),
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
use fxhash::{FxHashMap, FxHashSet};
|
||||
use kind_span::Range;
|
||||
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 crate::subst::subst;
|
||||
@ -94,7 +94,9 @@ fn split_rule(
|
||||
Expr::var(name)
|
||||
}
|
||||
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());
|
||||
old_rule_body_args.push(arg);
|
||||
@ -170,7 +172,9 @@ fn split_rule(
|
||||
|
||||
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 {
|
||||
name: new_entry_name,
|
||||
@ -195,7 +199,8 @@ fn flatten_entry(entry: &Entry) -> Vec<Entry> {
|
||||
if !skip.contains(&i) {
|
||||
let rule = &entry.rules[i];
|
||||
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);
|
||||
new_entries.extend(split_entries);
|
||||
} else {
|
||||
|
@ -2,16 +2,29 @@ use std::sync::mpsc::Sender;
|
||||
|
||||
use flatten::flatten;
|
||||
use kind_report::data::Diagnostic;
|
||||
use kind_tree::{untyped};
|
||||
use kind_tree::untyped;
|
||||
|
||||
pub use compile::File;
|
||||
|
||||
mod compile;
|
||||
mod flatten;
|
||||
mod subst;
|
||||
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);
|
||||
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) {
|
||||
use kind_tree::untyped::ExprKind::*;
|
||||
|
@ -1 +0,0 @@
|
||||
|
@ -268,7 +268,9 @@ impl Display for Book {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
||||
for entr in self.entrs.values() {
|
||||
if !entr.rules.is_empty() {
|
||||
writeln!(f, "{}\n", entr)?;
|
||||
writeln!(f, "{}", entr)?;
|
||||
} else {
|
||||
writeln!(f, "ctr {}", entr.name)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
Loading…
Reference in New Issue
Block a user