From 42d8baf03d248081e04c01fb825cc002553337ca Mon Sep 17 00:00:00 2001 From: felipegchi Date: Fri, 25 Nov 2022 14:41:07 -0300 Subject: [PATCH] refactor: a lot of refactoring and implementation of flattening --- crates/kind-checker/src/compiler/mod.rs | 191 +++++---- crates/kind-checker/src/lib.rs | 2 +- crates/kind-checker/src/report.rs | 44 ++- crates/kind-cli/src/lib.rs | 59 +-- .../suite/checker/derive/fail/Repeated.golden | 32 +- .../suite/checker/derive/fail/Repeated.kind2 | 1 + .../checker/derive/fail/WrongU120.golden | 24 ++ .../suite/checker/derive/fail/WrongU120.kind2 | 6 + .../checker/derive/fail/WrongU120Eq.golden | 14 + .../checker/derive/fail/WrongU120Eq.kind2 | 12 + crates/kind-cli/tests/suite/eval/User.golden | 2 +- crates/kind-cli/tests/suite/eval/User.kind2 | 18 +- crates/kind-derive/src/lib.rs | 2 +- crates/kind-derive/src/matching.rs | 2 + crates/kind-derive/src/open.rs | 1 + crates/kind-driver/Cargo.toml | 3 + crates/kind-driver/src/lib.rs | 55 ++- crates/kind-driver/src/resolution.rs | 8 +- crates/kind-driver/src/session.rs | 2 +- crates/kind-parser/src/lexer/literals.rs | 8 + crates/kind-parser/src/lexer/tokens.rs | 4 +- crates/kind-parser/src/top_level/mod.rs | 2 + crates/kind-parser/src/top_level/type_decl.rs | 17 +- crates/kind-pass/src/desugar/app.rs | 21 +- crates/kind-pass/src/desugar/attributes.rs | 26 +- crates/kind-pass/src/desugar/destruct.rs | 9 +- crates/kind-pass/src/desugar/mod.rs | 7 +- crates/kind-pass/src/desugar/top_level.rs | 77 ++-- crates/kind-pass/src/erasure/mod.rs | 265 +++++++------ crates/kind-pass/src/errors.rs | 11 +- crates/kind-pass/src/unbound/mod.rs | 4 +- crates/kind-report/src/data.rs | 1 + crates/kind-report/src/report.rs | 34 +- crates/kind-span/src/lib.rs | 74 +--- crates/kind-target-hvm/src/lib.rs | 57 ++- crates/kind-target-kdl/Cargo.toml | 1 + crates/kind-target-kdl/src/compile.rs | 368 ++++++++++++++++++ crates/kind-target-kdl/src/errors.rs | 82 ++++ crates/kind-target-kdl/src/flatten.rs | 235 +++++++++++ crates/kind-target-kdl/src/lib.rs | 248 +----------- crates/kind-target-kdl/src/subst.rs | 42 ++ crates/kind-tree/src/concrete/mod.rs | 8 +- crates/kind-tree/src/desugared/mod.rs | 289 ++++++++------ crates/kind-tree/src/lib.rs | 15 + crates/kind-tree/src/symbol.rs | 13 +- crates/kind-tree/src/untyped/mod.rs | 331 ++++++++++++++++ 46 files changed, 1894 insertions(+), 833 deletions(-) create mode 100644 crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.golden create mode 100644 crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.kind2 create mode 100644 crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.golden create mode 100644 crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.kind2 create mode 100644 crates/kind-target-kdl/src/compile.rs create mode 100644 crates/kind-target-kdl/src/errors.rs create mode 100644 crates/kind-target-kdl/src/flatten.rs create mode 100644 crates/kind-target-kdl/src/subst.rs create mode 100644 crates/kind-tree/src/untyped/mod.rs diff --git a/crates/kind-checker/src/compiler/mod.rs b/crates/kind-checker/src/compiler/mod.rs index 39422840..ef3ffa4f 100644 --- a/crates/kind-checker/src/compiler/mod.rs +++ b/crates/kind-checker/src/compiler/mod.rs @@ -5,7 +5,7 @@ use self::tags::EvalTag; use self::tags::{operator_to_constructor, TermTag}; use hvm::syntax::Term; -use kind_span::Span; +use kind_span::Range; use kind_tree::desugared::{self, Book, Expr}; use kind_tree::symbol::{Ident, QualifiedIdent}; @@ -90,9 +90,9 @@ fn mk_ctr_name_from_str(ident: &str) -> Box { mk_single_ctr(format!("{}.", ident)) } -fn span_to_num(span: Span) -> Box { +fn range_to_num(range: Range) -> Box { Box::new(Term::U6O { - numb: u60::new(span.encode().0), + numb: u60::new(range.encode().0), }) } @@ -100,7 +100,7 @@ fn set_origin(ident: &Ident) -> Box { mk_lifted_ctr( "Kind.Term.set_origin".to_owned(), vec![ - span_to_num(Span::Locatable(ident.range)), + range_to_num(ident.range), mk_var(ident.to_str()), ], ) @@ -113,30 +113,18 @@ fn lam(name: &Ident, body: Box) -> Box { }) } -fn desugar_str(input: &str, span: Span) -> Box { - let nil = QualifiedIdent::new_static("String.nil", None, span.to_range().unwrap()); - let cons = QualifiedIdent::new_static("String.cons", None, span.to_range().unwrap()); - input.chars().rfold( - Box::new(desugared::Expr { - data: desugared::ExprKind::Ctr(nil, vec![]), - span, - }), - |right, chr| { - Box::new(desugared::Expr { - data: desugared::ExprKind::Ctr( - cons.clone(), - vec![ - Box::new(desugared::Expr { - data: desugared::ExprKind::Num(kind_tree::Number::U60(chr as u64)), - span, - }), - right, - ], - ), - span, - }) - }, - ) +fn desugar_str(input: &str, range: Range) -> Box { + let nil = QualifiedIdent::new_static("String.nil", None, range); + let cons = QualifiedIdent::new_static("String.cons", None, range); + input + .chars() + .rfold(desugared::Expr::ctr(range, nil, vec![]), |right, chr| { + desugared::Expr::ctr( + range, + cons.clone(), + vec![desugared::Expr::num60(range, chr as u64), right], + ) + }) } fn codegen_str(input: &str) -> Box { @@ -163,68 +151,89 @@ fn codegen_all_expr( ) -> Box { use kind_tree::desugared::ExprKind::*; match &expr.data { - Typ => mk_lifted_ctr(eval_ctr(quote, TermTag::Typ), vec![span_to_num(expr.span)]), - NumType(kind_tree::NumType::U60) => { - mk_lifted_ctr(eval_ctr(quote, TermTag::U60), vec![span_to_num(expr.span)]) - } - NumType(kind_tree::NumType::U120) => todo!(), - Var(ident) => { + 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)]), + NumType { + typ: kind_tree::NumType::U120, + } => mk_lifted_ctr( + eval_ctr(quote, TermTag::Ctr(0)), + vec![ + mk_ctr_name_from_str("U120"), + if lhs { + mk_var("orig") + } else { + range_to_num(expr.range) + }, + ], + ), + Var { name } => { if quote && !lhs { - set_origin(ident) + set_origin(name) } else if lhs_rule { *num += 1; mk_lifted_ctr( eval_ctr(quote, TermTag::Var), vec![ - span_to_num(expr.span), - mk_u60(ident.encode()), + range_to_num(expr.range), + mk_u60(name.encode()), mk_u60((*num - 1) as u64), ], ) } else { - mk_var(ident.to_str()) + mk_var(name.to_str()) } } - All(name, typ, body, _erased) => mk_lifted_ctr( + All { + param, + typ, + body, + erased: _, + } => mk_lifted_ctr( eval_ctr(quote, TermTag::All), vec![ - span_to_num(expr.span), - mk_u60(name.encode()), + range_to_num(expr.range), + mk_u60(param.encode()), codegen_all_expr(lhs_rule, lhs, num, quote, typ), - lam(name, codegen_all_expr(lhs_rule, lhs, num, quote, body)), + lam(param, codegen_all_expr(lhs_rule, lhs, num, quote, body)), ], ), - Lambda(name, body, _erased) => mk_lifted_ctr( + Lambda { + param, + body, + erased: _, + } => mk_lifted_ctr( eval_ctr(quote, TermTag::Lambda), vec![ - span_to_num(expr.span), - mk_u60(name.encode()), - lam(name, codegen_all_expr(lhs_rule, lhs, num, quote, body)), + range_to_num(expr.range), + mk_u60(param.encode()), + lam(param, codegen_all_expr(lhs_rule, lhs, num, quote, body)), ], ), - App(head, spine) => spine.iter().fold( - codegen_all_expr(lhs_rule, lhs, num, quote, head), + App { fun, args } => args.iter().fold( + codegen_all_expr(lhs_rule, lhs, num, quote, fun), |left, right| { mk_lifted_ctr( eval_ctr(quote, TermTag::App), vec![ - span_to_num(expr.span), + range_to_num(expr.range), left, codegen_all_expr(lhs_rule, lhs, num, quote, &right.data), ], ) }, ), - Ctr(name, spine) => mk_lifted_ctr( - eval_ctr(quote, TermTag::Ctr(spine.len())), + Ctr { name, args } => mk_lifted_ctr( + eval_ctr(quote, TermTag::Ctr(args.len())), vec_preppend![ mk_ctr_name(name), - if lhs { mk_var("orig") } else { span_to_num(expr.span) }; - spine.iter().cloned().map(|x| codegen_all_expr(lhs_rule, lhs, num, quote, &x)).collect::>>() + if lhs { mk_var("orig") } else { range_to_num(expr.range) }; + args.iter().cloned().map(|x| codegen_all_expr(lhs_rule, lhs, num, quote, &x)).collect::>>() ], ), - Fun(name, spine) => { - let new_spine: Vec> = spine + Fun { name, args } => { + let new_spine: Vec> = args .iter() .cloned() .map(|x| codegen_all_expr(lhs_rule, lhs, num, quote, &x)) @@ -234,7 +243,7 @@ fn codegen_all_expr( eval_ctr(quote, TermTag::Fun(new_spine.len())), vec_preppend![ mk_ctr_name(name), - span_to_num(expr.span); + range_to_num(expr.range); new_spine ], ) @@ -242,59 +251,81 @@ fn codegen_all_expr( mk_ctr( TermTag::HoasF(name.to_string()).to_string(), vec_preppend![ - span_to_num(expr.span); + range_to_num(expr.range); new_spine ], ) } } - Let(name, val, body) => mk_ctr( + Let { name, val, next } => mk_ctr( eval_ctr(quote, TermTag::Let), vec![ - span_to_num(expr.span), + range_to_num(expr.range), mk_u60(name.encode()), codegen_all_expr(lhs_rule, lhs, num, quote, val), - lam(name, codegen_all_expr(lhs_rule, lhs, num, quote, body)), + lam(name, codegen_all_expr(lhs_rule, lhs, num, quote, next)), ], ), - Ann(val, typ) => mk_ctr( + Ann { expr, typ } => mk_ctr( eval_ctr(quote, TermTag::Ann), vec![ - span_to_num(expr.span), - codegen_all_expr(lhs_rule, lhs, num, quote, val), + range_to_num(expr.range), + codegen_all_expr(lhs_rule, lhs, num, quote, expr), codegen_all_expr(lhs_rule, lhs, num, quote, typ), ], ), - Sub(name, idx, rdx, inside) => mk_ctr( + Sub { + name, + indx, + redx, + expr, + } => mk_ctr( eval_ctr(quote, TermTag::Sub), vec![ - span_to_num(expr.span), + range_to_num(expr.range), mk_u60(name.encode()), - mk_u60(*idx as u64), - mk_u60(*rdx as u64), - codegen_all_expr(lhs_rule, lhs, num, quote, inside), + mk_u60(*indx as u64), + mk_u60(*redx as u64), + codegen_all_expr(lhs_rule, lhs, num, quote, expr), ], ), - Num(kind_tree::Number::U60(n)) => mk_lifted_ctr( + Num { + num: kind_tree::Number::U60(n), + } => mk_lifted_ctr( eval_ctr(quote, TermTag::Num), - vec![span_to_num(expr.span), mk_u60(*n)], + vec![range_to_num(expr.range), mk_u60(*n)], ), - Num(kind_tree::Number::U120(_)) => todo!(), - Binary(operator, left, right) => mk_lifted_ctr( + Num { + num: kind_tree::Number::U120(numb), + } => { + let new = QualifiedIdent::new_static("U120.new", None, expr.range); + + let expr = desugared::Expr::ctr( + expr.range, + new, + vec![ + desugared::Expr::num60(expr.range, (numb >> 60) as u64), + desugared::Expr::num60(expr.range, (numb & 0xFFFFFFFFFFFFFFF) as u64), + ], + ); + + codegen_all_expr(lhs_rule, lhs, num, quote, &expr) + } + Binary { op, left, right } => mk_lifted_ctr( eval_ctr(quote, TermTag::Binary), vec![ - span_to_num(expr.span), - mk_single_ctr(operator_to_constructor(*operator).to_owned()), + range_to_num(expr.range), + mk_single_ctr(operator_to_constructor(*op).to_owned()), codegen_all_expr(lhs_rule, lhs, num, quote, left), codegen_all_expr(lhs_rule, lhs, num, quote, right), ], ), - Hole(num) => mk_lifted_ctr( + Hole { num } => mk_lifted_ctr( eval_ctr(quote, TermTag::Hole), - vec![span_to_num(expr.span), mk_u60(*num)], + vec![range_to_num(expr.range), mk_u60(*num)], ), - Str(str) => codegen_all_expr(lhs_rule, lhs, num, quote, &desugar_str(str, expr.span)), - Hlp(_) => mk_lifted_ctr(eval_ctr(quote, TermTag::Hlp), vec![span_to_num(expr.span)]), + 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)]), Err => panic!("Internal Error: Was not expecting an ERR node inside the HVM checker"), } } @@ -313,7 +344,7 @@ fn codegen_type(args: &[desugared::Argument], typ: &desugared::Expr) -> Box Box { - let file = compiler::codegen_book(book, Vec::new()); + let file = gen_checker(book, Vec::new()); let file = language::syntax::read_file(&file.to_string()).unwrap(); diff --git a/crates/kind-checker/src/report.rs b/crates/kind-checker/src/report.rs index 57c3c5f8..5206cdd2 100644 --- a/crates/kind-checker/src/report.rs +++ b/crates/kind-checker/src/report.rs @@ -1,10 +1,10 @@ //! Transforms a answer from the type checker in //! a Expr of the kind-tree package. -use kind_span::{EncodedSpan, Range}; +use kind_span::{EncodedRange, Range}; use kind_tree::backend::Term; use kind_tree::symbol::{Ident, QualifiedIdent}; -use kind_tree::{desugared, Operator}; +use kind_tree::{desugared, Number, Operator}; use crate::errors::TypeError; use desugared::Expr; @@ -24,7 +24,7 @@ macro_rules! match_opt { } fn parse_orig(term: &Term) -> Result { - match_opt!(term, Term::U6O { numb } => EncodedSpan(*numb).to_range()) + match_opt!(term, Term::U6O { numb } => EncodedRange(*numb).to_range()) } fn parse_num(term: &Term) -> Result { @@ -135,17 +135,33 @@ fn parse_all_expr( erased: false, }], )), - "Kind.Quoted.ctr" => Ok(Expr::ctr( - parse_orig(&args[1])?, - parse_qualified(&args[0])?, - { - let mut res = Vec::new(); - for arg in parse_list(&args[2])? { - res.push(parse_all_expr(names.clone(), &arg)?); + "Kind.Quoted.ctr" => { + let name = parse_qualified(&args[0])?; + let orig = parse_orig(&args[1])?; + let mut res = Vec::new(); + for arg in parse_list(&args[2])? { + res.push(parse_all_expr(names.clone(), &arg)?); + } + + if name.to_str() == "U120.new" && res.len() == 2 { + match (&res[0].data, &res[1].data) { + ( + desugared::ExprKind::Num { + num: Number::U60(hi), + }, + desugared::ExprKind::Num { + num: Number::U60(lo), + }, + ) => { + let num = (*hi as u128) << 60 | *lo as u128; + Ok(Expr::num120(orig, num)) + } + _ => Ok(Expr::ctr(orig, name, res)), } - res - }, - )), + } else { + Ok(Expr::ctr(orig, name, res)) + } + } "Kind.Quoted.fun" => Ok(Expr::fun( parse_orig(&args[1])?, parse_qualified(&args[0])?, @@ -224,7 +240,7 @@ fn parse_type_error(expr: &Term) -> Result { let ls = parse_list(&args[0])?; let entries = ls.iter().flat_map(|x| transform_entry(x)); let ctx = Context(entries.collect()); - let orig = match_opt!(*args[1], Term::U6O { numb } => EncodedSpan(numb).to_range())?; + let orig = match_opt!(*args[1], Term::U6O { numb } => EncodedRange(numb).to_range())?; match name.as_str() { "Kind.Error.Quoted.unbound_variable" => Ok(TypeError::UnboundVariable(ctx, orig)), "Kind.Error.Quoted.cant_infer_hole" => Ok(TypeError::CantInferHole(ctx, orig)), diff --git a/crates/kind-cli/src/lib.rs b/crates/kind-cli/src/lib.rs index 8c38b495..6cf1d122 100644 --- a/crates/kind-cli/src/lib.rs +++ b/crates/kind-cli/src/lib.rs @@ -86,15 +86,6 @@ pub enum Command { /// Compiles a file to HVM (.hvm) #[clap(aliases = &["hvm"])] ToHVM { file: String }, - - /// Watch for file changes and then - /// check when some file change. - #[clap(aliases = &["w"])] - Watch { file: String }, - - /// Read eval print loop - #[clap(aliases = &["re"])] - Repl, } /// Helper structure to use stderr as fmt::Write @@ -127,6 +118,7 @@ pub fn compile_in_session( render_config: RenderConfig, root: PathBuf, file: String, + compiled: bool, fun: &mut dyn FnMut(&mut Session) -> Option, ) -> Option { let (rx, tx) = std::sync::mpsc::channel(); @@ -148,7 +140,15 @@ pub fn compile_in_session( let diagnostics = tx.try_iter().collect::>>(); if diagnostics.is_empty() && res.is_some() { - render_to_stderr(&render_config, &session, &Log::Checked(start.elapsed())); + render_to_stderr( + &render_config, + &session, + &if compiled { + Log::Compiled(start.elapsed()) + } else { + Log::Checked(start.elapsed()) + }, + ); eprintln!(); res } else { @@ -175,12 +175,12 @@ pub fn run_cli(config: Cli) { match config.command { Command::Check { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), false, &mut |session| { driver::type_check_book(session, &PathBuf::from(file.clone())) }); } Command::ToHVM { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { let book = driver::erase_book(session, &PathBuf::from(file.clone()), &entrypoints)?; Some(driver::compile_book_to_hvm(book)) }) @@ -190,7 +190,7 @@ pub fn run_cli(config: Cli) { }); } Command::Run { file } => { - let res = compile_in_session(render_config, root, file.clone(), &mut |session| { + let res = compile_in_session(render_config, root, file.clone(), true, &mut |session| { let book = driver::erase_book( session, &PathBuf::from(file.clone()), @@ -208,7 +208,7 @@ pub fn run_cli(config: Cli) { } } Command::Show { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { driver::to_book(session, &PathBuf::from(file.clone())) }) .map(|res| { @@ -217,7 +217,7 @@ pub fn run_cli(config: Cli) { }); } Command::ToKindCore { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { driver::desugar_book(session, &PathBuf::from(file.clone())) }) .map(|res| { @@ -226,7 +226,7 @@ pub fn run_cli(config: Cli) { }); } Command::Erase { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { driver::erase_book(session, &PathBuf::from(file.clone()), &entrypoints) }) .map(|res| { @@ -235,7 +235,7 @@ pub fn run_cli(config: Cli) { }); } Command::GenChecker { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { driver::check_erasure_book(session, &PathBuf::from(file.clone())) }) .map(|res| { @@ -244,22 +244,25 @@ pub fn run_cli(config: Cli) { }); } Command::Eval { file } => { - compile_in_session(render_config, root, file.clone(), &mut |session| { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { let book = driver::desugar_book(session, &PathBuf::from(file.clone()))?; - driver::check_main_entry(session, &book)?; + driver::check_main_desugared_entry(session, &book)?; Some(book) }) .map(|res| println!("{}", driver::eval_in_checker(&res))); } - Command::Watch { file: _ } => { - todo!() + Command::ToKDL { file, namespace } => { + compile_in_session(render_config, root, file.clone(), true, &mut |session| { + driver::compile_book_to_kdl( + &PathBuf::from(file.clone()), + session, + &namespace.clone().unwrap_or("".to_string()), + ) + }) + .map(|res| { + println!("{}", res); + res + }); } - Command::Repl => { - todo!() - } - Command::ToKDL { - file: _, - namespace: _, - } => todo!(), } } diff --git a/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.golden b/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.golden index d4a592a8..8b46e6b5 100644 --- a/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.golden +++ b/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.golden @@ -1,16 +1,24 @@ - ERROR Cannot find the definition 'User.new.open'. + ERROR Repeated named variable - /--[tests/suite/checker/derive/fail/Repeated.kind2:10:9] + /--[tests/suite/checker/derive/fail/Repeated.kind2:12:19] | - 9 | Main = - 10 | let User.new (ttt = e) e .. = User.new 2 4 1 - | v------- - | \Here! - : - 11 | let User.new (ttt = f) ttt = User.new 6 7 3 - | v------- - | \Here! - 12 | e + 11 | let User.new (ttt = e) e .. = User.new 2 4 1 + 12 | let User.new (ttt = f) ttt = User.new 6 7 3 + | v-- v-- + | | \Second occurence + | \First occurence + 13 | e - Hint: Maybe you're looking for 'User.new' + + ERROR The case is not covering all the values inside of it! + + /--[tests/suite/checker/derive/fail/Repeated.kind2:12:9] + | + 11 | let User.new (ttt = e) e .. = User.new 2 4 1 + 12 | let User.new (ttt = f) ttt = User.new 6 7 3 + | v------- + | \This is the incomplete case + 13 | e + + Hint: Need variables for 'e', 'name' diff --git a/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.kind2 b/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.kind2 index 8d522862..10b041f8 100644 --- a/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.kind2 +++ b/crates/kind-cli/tests/suite/checker/derive/fail/Repeated.kind2 @@ -1,3 +1,4 @@ +#derive[open] record User { constructor new name : U60 diff --git a/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.golden b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.golden new file mode 100644 index 00000000..76ca4020 --- /dev/null +++ b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.golden @@ -0,0 +1,24 @@ + ERROR Required functions are not implemented for this type. + + /--[tests/suite/checker/derive/fail/WrongU120.kind2:5:12] + | + 4 | + 5 | Teste : Eq 123u120 124u120 + | v------ + | \You cannot use this expression! + 6 | Teste = Eq.rfl + + Hint: You must implement 'U120.new' in order to use the u120 notation. + + ERROR Required functions are not implemented for this type. + + /--[tests/suite/checker/derive/fail/WrongU120.kind2:5:20] + | + 4 | + 5 | Teste : Eq 123u120 124u120 + | v------ + | \You cannot use this expression! + 6 | Teste = Eq.rfl + + Hint: You must implement 'U120.new' in order to use the u120 notation. + diff --git a/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.kind2 b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.kind2 new file mode 100644 index 00000000..efcd5df9 --- /dev/null +++ b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120.kind2 @@ -0,0 +1,6 @@ +type Eq (a: t) ~ (b: t) { + rfl: Eq t a a +} + +Teste : Eq 123u120 124u120 +Teste = Eq.rfl \ No newline at end of file diff --git a/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.golden b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.golden new file mode 100644 index 00000000..dcaed33d --- /dev/null +++ b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.golden @@ -0,0 +1,14 @@ + ERROR Type mismatch + + * Got : (Eq _ 123u120 123u120) + * Expected : (Eq _ 123u120 124u120) + + + /--[tests/suite/checker/derive/fail/WrongU120Eq.kind2:12:9] + | + 11 | Teste : Eq 123u120 124u120 + 12 | Teste = Eq.rfl + | v----- + | \Here! + + diff --git a/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.kind2 b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.kind2 new file mode 100644 index 00000000..a27ae812 --- /dev/null +++ b/crates/kind-cli/tests/suite/checker/derive/fail/WrongU120Eq.kind2 @@ -0,0 +1,12 @@ +type Eq (a: t) ~ (b: t) { + rfl: Eq t a a +} + +record U120 { + constructor new + low : U60 + high : U60 +} + +Teste : Eq 123u120 124u120 +Teste = Eq.rfl \ No newline at end of file diff --git a/crates/kind-cli/tests/suite/eval/User.golden b/crates/kind-cli/tests/suite/eval/User.golden index bf0d87ab..9fb8a725 100644 --- a/crates/kind-cli/tests/suite/eval/User.golden +++ b/crates/kind-cli/tests/suite/eval/User.golden @@ -1 +1 @@ -4 \ No newline at end of file +(Teste []) \ No newline at end of file diff --git a/crates/kind-cli/tests/suite/eval/User.kind2 b/crates/kind-cli/tests/suite/eval/User.kind2 index b0449764..66950e1d 100644 --- a/crates/kind-cli/tests/suite/eval/User.kind2 +++ b/crates/kind-cli/tests/suite/eval/User.kind2 @@ -1,13 +1,11 @@ -#derive[open] -record User { - constructor new - name : U60 - ttt : U60 - e : U60 +type List (t: Type) { + cons (x: t) (xs: List t) + nil } +Teste (n: List U60) : U60 +Teste (List.cons 2 xs) = 2 + +#kdl_run Main : U60 -Main = - let User.new (ttt = e) name .. = User.new 2 4 1 - let User.new (ttt = f) .. = User.new 6 7 3 - e +Main = Teste List.nil \ No newline at end of file diff --git a/crates/kind-derive/src/lib.rs b/crates/kind-derive/src/lib.rs index f142aa3a..06fb4eee 100644 --- a/crates/kind-derive/src/lib.rs +++ b/crates/kind-derive/src/lib.rs @@ -3,4 +3,4 @@ pub mod errors; pub mod matching; pub mod open; -pub mod subst; \ No newline at end of file +pub mod subst; diff --git a/crates/kind-derive/src/matching.rs b/crates/kind-derive/src/matching.rs index ac979c66..2eac3d88 100644 --- a/crates/kind-derive/src/matching.rs +++ b/crates/kind-derive/src/matching.rs @@ -209,6 +209,7 @@ pub fn derive_match( rules: vec![], range, attrs: Vec::new(), + generated_by: Some(sum.name.to_string().clone()), }; return (entry, errs); } @@ -382,6 +383,7 @@ pub fn derive_match( rules, range, attrs: Vec::new(), + generated_by: Some(sum.name.to_string().clone()), }; (entry, errs) diff --git a/crates/kind-derive/src/open.rs b/crates/kind-derive/src/open.rs index 55d8903a..7364161d 100644 --- a/crates/kind-derive/src/open.rs +++ b/crates/kind-derive/src/open.rs @@ -157,6 +157,7 @@ pub fn derive_open(range: Range, rec: &RecordDecl) -> concrete::Entry { rules, range, attrs: Vec::new(), + generated_by: Some(rec.name.to_string().clone()), }; entry diff --git a/crates/kind-driver/Cargo.toml b/crates/kind-driver/Cargo.toml index a50d4210..3e25b1be 100644 --- a/crates/kind-driver/Cargo.toml +++ b/crates/kind-driver/Cargo.toml @@ -12,7 +12,10 @@ kind-span = { path = "../kind-span" } kind-report = { path = "../kind-report" } kind-checker = { path = "../kind-checker" } kind-pass = { path = "../kind-pass" } + kind-target-hvm = { path = "../kind-target-hvm" } +kind-target-kdl = { path = "../kind-target-kdl" } + hvm = { git = "https://github.com/Kindelia/HVM.git" } strsim = "0.10.0" diff --git a/crates/kind-driver/src/lib.rs b/crates/kind-driver/src/lib.rs index 08324582..c6269542 100644 --- a/crates/kind-driver/src/lib.rs +++ b/crates/kind-driver/src/lib.rs @@ -2,12 +2,15 @@ use checker::eval; use errors::DriverError; use fxhash::FxHashSet; use kind_pass::{desugar, erasure, expand}; -use kind_report::report::FileCache; +use kind_report::{data::Diagnostic, report::FileCache}; use kind_span::SyntaxCtxIndex; -use kind_tree::{backend, concrete, desugared}; +use kind_tree::{backend, concrete, desugared, untyped}; use session::Session; -use std::path::PathBuf; +use std::{ + path::{Path, PathBuf}, + sync::mpsc::Sender, +}; use kind_checker as checker; @@ -22,7 +25,7 @@ impl FileCache for Session { } } -pub fn type_check_book(session: &mut Session, path: &PathBuf) -> Option { +pub fn type_check_book(session: &mut Session, path: &PathBuf) -> Option { let concrete_book = to_book(session, path)?; let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?; @@ -61,7 +64,7 @@ pub fn erase_book( session: &mut Session, path: &PathBuf, entrypoint: &[String], -) -> Option { +) -> Option { let concrete_book = to_book(session, path)?; let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?; erasure::erase_book( @@ -87,11 +90,47 @@ pub fn check_erasure_book(session: &mut Session, path: &PathBuf) -> Option backend::File { +pub fn compile_book_to_hvm(book: untyped::Book) -> backend::File { kind_target_hvm::compile_book(book) } -pub fn check_main_entry(session: &mut Session, book: &desugared::Book) -> Option<()> { +pub fn compile_book_to_kdl( + path: &PathBuf, + session: &mut Session, + namespace: &str, +) -> Option { + let concrete_book = to_book(session, path)?; + let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?; + + let entrypoints = desugared_book + .entrs + .iter() + .filter(|x| x.1.attrs.kdl_run) + .map(|x| x.0.clone()) + .collect::>(); + + let book = erasure::erase_book( + desugared_book, + session.diagnostic_sender.clone(), + FxHashSet::from_iter(entrypoints), + )?; + + kind_target_kdl::compile_book(book, session.diagnostic_sender.clone(), namespace) +} + +pub fn check_main_entry(session: &mut Session, book: &untyped::Book) -> Option<()> { + if !book.entrs.contains_key("Main") { + session + .diagnostic_sender + .send(Box::new(DriverError::ThereIsntAMain)) + .unwrap(); + None + } else { + Some(()) + } +} + +pub fn check_main_desugared_entry(session: &mut Session, book: &desugared::Book) -> Option<()> { if !book.entrs.contains_key("Main") { session .diagnostic_sender @@ -113,5 +152,5 @@ pub fn eval_in_checker(book: &desugared::Book) -> Box { } pub fn generate_checker(book: &desugared::Book) -> String { - checker::gen_checker(book, book.names.keys().cloned().collect()) + checker::gen_checker(book, book.entrs.keys().cloned().collect()) } diff --git a/crates/kind-driver/src/resolution.rs b/crates/kind-driver/src/resolution.rs index 70c2bd10..dfc15572 100644 --- a/crates/kind-driver/src/resolution.rs +++ b/crates/kind-driver/src/resolution.rs @@ -227,7 +227,7 @@ fn parse_and_store_book_by_path<'a>( } module_to_book(&mut failed, session, module, book); - + for idents in state.unbound_top_level.values() { failed |= parse_and_store_book_by_identifier(session, &idents.iter().nth(0).unwrap(), book); } @@ -270,7 +270,11 @@ pub fn check_unbound_top_level(session: &mut Session, book: &mut Book) -> bool { unbound::get_book_unbound(session.diagnostic_sender.clone(), book, true); for (_, unbound) in unbound_tops { - let res: Vec = unbound.iter().map(|x| x.to_ident()).collect(); + let res: Vec = unbound + .iter() + .filter(|x| !x.generated) + .map(|x| x.to_ident()) + .collect(); if !res.is_empty() { unbound_variable(session, &book, &res); failed = true; diff --git a/crates/kind-driver/src/session.rs b/crates/kind-driver/src/session.rs index 49c3555e..bae636d9 100644 --- a/crates/kind-driver/src/session.rs +++ b/crates/kind-driver/src/session.rs @@ -13,7 +13,7 @@ use kind_report::data::Diagnostic; pub struct Session { pub loaded_paths: Vec>, pub loaded_sources: Vec, - + pub loaded_paths_map: FxHashMap, /// It will be useful in the future diff --git a/crates/kind-parser/src/lexer/literals.rs b/crates/kind-parser/src/lexer/literals.rs index 44ccba8f..0bd173e9 100644 --- a/crates/kind-parser/src/lexer/literals.rs +++ b/crates/kind-parser/src/lexer/literals.rs @@ -76,6 +76,14 @@ impl<'a> Lexer<'a> { ) }; match self.peekable.peek() { + Some('n' | 'N') => { + self.next_char(); + if let Ok(res) = u128::from_str_radix(&num.replace('_', ""), base) { + (Token::Nat(res), self.mk_range(num_start)) + } else { + make_num_err(self) + } + } Some('U' | 'u') => { self.next_char(); let type_ = self.accumulate_while(&|x| x.is_ascii_digit()); diff --git a/crates/kind-parser/src/lexer/tokens.rs b/crates/kind-parser/src/lexer/tokens.rs index 13e2c704..b6d1a961 100644 --- a/crates/kind-parser/src/lexer/tokens.rs +++ b/crates/kind-parser/src/lexer/tokens.rs @@ -48,6 +48,7 @@ pub enum Token { Str(String), Num60(u64), Num120(u128), + Nat(u128), Float(u64, u64), Hole, @@ -130,7 +131,7 @@ impl fmt::Display for Token { Token::FatArrow => write!(f, "=>"), Token::Dollar => write!(f, "$"), Token::Comma => write!(f, ","), - Token::RightArrow => write!(f, "<-"), + Token::RightArrow => write!(f, "->"), Token::DotDot => write!(f, ".."), Token::Dot => write!(f, "."), Token::Tilde => write!(f, "~"), @@ -143,6 +144,7 @@ impl fmt::Display for Token { Token::Str(s) => write!(f, "\"{}\"", s), Token::Num60(n) => write!(f, "{}", n), Token::Num120(n) => write!(f, "{}u120", n), + Token::Nat(n) => write!(f, "{}n", n), Token::Float(start, end) => write!(f, "{}.{}", start, end), Token::Hole => write!(f, "_"), Token::Plus => write!(f, "+"), diff --git a/crates/kind-parser/src/top_level/mod.rs b/crates/kind-parser/src/top_level/mod.rs index 507f2c7a..7a2e5bb6 100644 --- a/crates/kind-parser/src/top_level/mod.rs +++ b/crates/kind-parser/src/top_level/mod.rs @@ -210,6 +210,7 @@ impl<'a> Parser<'a> { rules, attrs, range: start.mix(end), + generated_by: None, }) } else { let mut rules = Vec::new(); @@ -235,6 +236,7 @@ impl<'a> Parser<'a> { rules, attrs, range: start.mix(end), + generated_by: None, }) } } diff --git a/crates/kind-parser/src/top_level/type_decl.rs b/crates/kind-parser/src/top_level/type_decl.rs index a509dd60..7fcc1641 100644 --- a/crates/kind-parser/src/top_level/type_decl.rs +++ b/crates/kind-parser/src/top_level/type_decl.rs @@ -1,4 +1,5 @@ use kind_tree::concrete::{Attribute, Constructor, RecordDecl, SumTypeDecl, Telescope}; +use kind_tree::symbol::Ident; use crate::errors::SyntaxDiagnostic; use crate::lexer::tokens::Token; @@ -6,6 +7,7 @@ use crate::state::Parser; impl<'a> Parser<'a> { pub fn parse_constructor(&mut self) -> Result { + let attrs = self.parse_attrs()?; let docs = self.parse_docs()?; let name = self.parse_any_id()?; let args = self.parse_arguments()?; @@ -20,6 +22,7 @@ impl<'a> Parser<'a> { Ok(Constructor { name, + attrs, docs, args: Telescope::new(args), typ, @@ -70,6 +73,7 @@ impl<'a> Parser<'a> { attrs: Vec, ) -> Result { self.eat_id("record")?; + let name = self.parse_upper_id()?; let parameters = self.parse_arguments()?; @@ -77,11 +81,17 @@ impl<'a> Parser<'a> { let range = self.range(); self.eat_variant(Token::LBrace)?; - self.eat_id("constructor")?; - let constructor = self.parse_id()?; + let cons_attrs = self.parse_attrs()?; - self.check_and_eat(Token::Comma); + let constructor = if self.check_actual_id("constructor") { + self.eat_id("constructor")?; + let res = self.parse_id()?; + self.check_and_eat(Token::Comma); + res + } else { + Ident::new("new".to_string(), name.range) + }; let mut fields = vec![]; @@ -102,6 +112,7 @@ impl<'a> Parser<'a> { parameters: Telescope::new(parameters), fields, attrs, + cons_attrs, }) } } diff --git a/crates/kind-pass/src/desugar/app.rs b/crates/kind-pass/src/desugar/app.rs index 130b8695..ad306b00 100644 --- a/crates/kind-pass/src/desugar/app.rs +++ b/crates/kind-pass/src/desugar/app.rs @@ -1,5 +1,5 @@ use fxhash::FxHashMap; -use kind_span::{Locatable, Range, Span}; +use kind_span::{Locatable, Range}; use kind_tree::concrete::expr::Expr; use kind_tree::concrete::{Binding, ExprKind}; @@ -93,7 +93,10 @@ impl<'a> DesugarState<'a> { if entry.arguments[i].hidden { // It's not expected that positional arguments require the range so // it's the reason why we are using a terrible "ghost range" - arguments[i] = Some((Range::ghost_range(), self.gen_hole_expr(Range::ghost_range()))) + arguments[i] = Some(( + Range::ghost_range(), + self.gen_hole_expr(Range::ghost_range()), + )) } } } else if entry.arguments.len() != args.len() { @@ -154,7 +157,7 @@ impl<'a> DesugarState<'a> { if arguments.iter().any(|x| x.is_none()) { return Box::new(desugared::Expr { data: desugared::ExprKind::Err, - span: Span::Locatable(range), + range, }); } @@ -162,11 +165,17 @@ impl<'a> DesugarState<'a> { Box::new(desugared::Expr { data: if entry.is_ctr { - desugared::ExprKind::Ctr(name.clone(), new_spine) + desugared::ExprKind::Ctr { + name: name.clone(), + args: new_spine, + } } else { - desugared::ExprKind::Fun(name.clone(), new_spine) + desugared::ExprKind::Fun { + name: name.clone(), + args: new_spine, + } }, - span: Span::Locatable(range), + range, }) } ExprKind::App { fun, args } => { diff --git a/crates/kind-pass/src/desugar/attributes.rs b/crates/kind-pass/src/desugar/attributes.rs index 4c381d53..99b72d49 100644 --- a/crates/kind-pass/src/desugar/attributes.rs +++ b/crates/kind-pass/src/desugar/attributes.rs @@ -1,7 +1,5 @@ -use kind_tree::{ - concrete::{self, Attribute, AttributeStyle}, - desugared, -}; +use kind_tree::concrete::{self, Attribute, AttributeStyle}; +use kind_tree::Attributes; use crate::errors::PassError; @@ -32,11 +30,8 @@ impl<'a> DesugarState<'a> { }; } - pub fn desugar_attributes( - &mut self, - attrs: &[concrete::Attribute], - ) -> Vec { - let mut vec = Vec::new(); + pub fn desugar_attributes(&mut self, attrs: &[concrete::Attribute]) -> Attributes { + let mut attributes: Attributes = Default::default(); for attr in attrs { match attr.name.to_str() { @@ -46,23 +41,23 @@ impl<'a> DesugarState<'a> { "inline" => { self.args_should_be_empty(attr); self.attr_without_value(attr); - vec.push(desugared::Attribute::Inline); + attributes.inlined = true; } "kdl_run" => { self.args_should_be_empty(attr); self.attr_without_value(attr); - vec.push(desugared::Attribute::KdlRun); + attributes.kdl_run = true; } "kdl_erase" => { self.args_should_be_empty(attr); self.attr_without_value(attr); - vec.push(desugared::Attribute::KdlErase); + attributes.kdl_erase = true; } "kdl_name" => { self.args_should_be_empty(attr); match &attr.value { Some(AttributeStyle::Ident(_, ident)) => { - vec.push(desugared::Attribute::KdlState(ident.clone())); + attributes.kdl_name = Some(ident.clone()); } Some(_) => self.attr_invalid_argument(attr), None => self.attr_expects_a_value(attr), @@ -72,7 +67,7 @@ impl<'a> DesugarState<'a> { self.args_should_be_empty(attr); match &attr.value { Some(AttributeStyle::Ident(_, ident)) => { - vec.push(desugared::Attribute::KdlState(ident.clone())); + attributes.kdl_state = Some(ident.clone()); } Some(_) => self.attr_invalid_argument(attr), None => self.attr_expects_a_value(attr), @@ -81,6 +76,7 @@ impl<'a> DesugarState<'a> { _ => self.send_err(PassError::AttributeDoesNotExists(attr.range)), } } - vec + + attributes } } diff --git a/crates/kind-pass/src/desugar/destruct.rs b/crates/kind-pass/src/desugar/destruct.rs index 68d1c8bb..809f18b1 100644 --- a/crates/kind-pass/src/desugar/destruct.rs +++ b/crates/kind-pass/src/desugar/destruct.rs @@ -249,15 +249,16 @@ impl<'a> DesugarState<'a> { } else { let mut idx: Vec = sum.indices.iter().map(|x| x.name.clone()).collect(); idx.push(Ident::generate("val_")); - idx.iter().rfold(self.gen_hole_expr(match_.typ.range), |expr, l| { - desugared::Expr::lambda(l.range, l.clone(), expr, false) - }) + idx.iter() + .rfold(self.gen_hole_expr(match_.typ.range), |expr, l| { + desugared::Expr::lambda(l.range, l.clone(), expr, false) + }) }; let prefix = [self.desugar_expr(&match_.scrutinizer), motive]; self.mk_desugared_fun( - match_.typ.range, + range, match_id, [prefix.as_slice(), lambdas.as_slice()].concat(), false, diff --git a/crates/kind-pass/src/desugar/mod.rs b/crates/kind-pass/src/desugar/mod.rs index cb9ad5ca..d7a2cfb8 100644 --- a/crates/kind-pass/src/desugar/mod.rs +++ b/crates/kind-pass/src/desugar/mod.rs @@ -9,7 +9,7 @@ use std::sync::mpsc::Sender; use kind_report::data::Diagnostic; -use kind_span::{Range, Span}; +use kind_span::Range; use kind_tree::{ concrete::{self}, desugared, @@ -65,10 +65,7 @@ impl<'a> DesugarState<'a> { } fn gen_hole_expr(&mut self, range: Range) -> Box { - Box::new(desugared::Expr { - data: desugared::ExprKind::Hole(self.gen_hole()), - span: Span::Locatable(range), - }) + desugared::Expr::hole(range, self.gen_hole()) } fn send_err(&mut self, err: PassError) { diff --git a/crates/kind-pass/src/desugar/top_level.rs b/crates/kind-pass/src/desugar/top_level.rs index f08e4b4e..e6d528f9 100644 --- a/crates/kind-pass/src/desugar/top_level.rs +++ b/crates/kind-pass/src/desugar/top_level.rs @@ -1,4 +1,4 @@ -use kind_span::{Range, Span}; +use kind_span::Range; use kind_tree::concrete::{self, Telescope}; use kind_tree::desugared::{self, ExprKind}; use kind_tree::symbol::QualifiedIdent; @@ -41,7 +41,7 @@ impl<'a> DesugarState<'a> { erased: argument.erased, name: argument.name.clone(), typ, - span: argument.range, + range: argument.range, } } @@ -55,9 +55,9 @@ impl<'a> DesugarState<'a> { let type_constructor = desugared::Entry { name: sum_type.name.clone(), args: desugared_params.extend(&desugared_indices).to_vec(), - typ: desugared::Expr::generate_expr(desugared::ExprKind::Typ), + typ: desugared::Expr::typ(sum_type.name.range), rules: Vec::new(), - span: Span::Locatable(sum_type.name.range), + range: sum_type.name.range, attrs: self.desugar_attributes(&sum_type.attrs), }; @@ -85,16 +85,16 @@ impl<'a> DesugarState<'a> { Some(expr) => { let res = self.desugar_expr(&expr); match &res.data { - ExprKind::Ctr(name, spine) + ExprKind::Ctr { name, args } if name.to_string() == sum_type.name.to_string() => { for (i, parameter) in sum_type.parameters.iter().enumerate() { - match &spine[i].data { - ExprKind::Var(name) + match &args[i].data { + ExprKind::Var { name } if name.to_string() == parameter.name.to_string() => {} _ => { self.send_err(PassError::ShouldBeAParameter( - spine[i].span, + Some(args[i].range), parameter.range, )); } @@ -129,8 +129,8 @@ impl<'a> DesugarState<'a> { .concat(), typ, rules: Vec::new(), - attrs: Vec::new(), - span: Span::Locatable(cons.name.range), + attrs: self.desugar_attributes(&cons.attrs), + range: cons.name.range, }; self.new_book @@ -147,9 +147,9 @@ impl<'a> DesugarState<'a> { let type_constructor = desugared::Entry { name: rec_type.name.clone(), args: desugared_params.clone().to_vec(), - typ: desugared::Expr::generate_expr(desugared::ExprKind::Typ), + typ: desugared::Expr::typ(rec_type.name.range), rules: Vec::new(), - span: Span::Locatable(rec_type.name.range), + range: rec_type.name.range, attrs: self.desugar_attributes(&rec_type.attrs), }; @@ -162,43 +162,32 @@ impl<'a> DesugarState<'a> { let args = [irrelevant_params.as_slice()] .concat() .iter() - .map(|x| { - Box::new(desugared::Expr { - data: desugared::ExprKind::Var(x.name.clone()), - span: Span::Generated, - }) - }) + .map(|x| desugared::Expr::var(x.name.clone())) .collect::>>(); - let typ = Box::new(desugared::Expr { - data: desugared::ExprKind::Ctr(rec_type.name.clone(), args), - span: Span::Generated, - }); + let typ = desugared::Expr::ctr(rec_type.name.range, rec_type.name.clone(), args); let cons_ident = rec_type.name.add_segment(rec_type.constructor.to_str()); + let fields_args = rec_type + .fields + .iter() + .map(|(ident, _docs, ty)| { + desugared::Argument::from_field( + ident, + self.desugar_expr(ty), + ident.range.mix(ty.range), + ) + }) + .collect::>(); + let data_constructor = desugared::Entry { name: cons_ident.clone(), - args: [ - irrelevant_params.as_slice(), - rec_type - .fields - .iter() - .map(|(ident, _docs, ty)| { - desugared::Argument::from_field( - ident, - self.desugar_expr(ty), - ident.range.mix(ty.range), - ) - }) - .collect::>() - .as_slice(), - ] - .concat(), + args: [irrelevant_params.as_slice(), fields_args.as_slice()].concat(), typ, rules: Vec::new(), - span: Span::Locatable(rec_type.constructor.range), - attrs: Vec::new(), + range: rec_type.constructor.range, + attrs: self.desugar_attributes(&rec_type.cons_attrs), }; self.new_book @@ -329,7 +318,7 @@ impl<'a> DesugarState<'a> { name: rule.name.clone(), pats, body: self.desugar_expr(&rule.body), - span: Span::Locatable(rule.range), + range: rule.range, } } else if pats.len() == args.len() - hidden { let mut res_pats = Vec::new(); @@ -345,7 +334,7 @@ impl<'a> DesugarState<'a> { name: rule.name.clone(), pats: res_pats, body: self.desugar_expr(&rule.body), - span: Span::Locatable(rule.range), + range: rule.range, } } else { self.send_err(PassError::RuleWithIncorrectArity( @@ -359,7 +348,7 @@ impl<'a> DesugarState<'a> { name: rule.name.clone(), pats, body: self.desugar_expr(&rule.body), - span: Span::Locatable(rule.range), + range: rule.range, } } } @@ -377,7 +366,7 @@ impl<'a> DesugarState<'a> { name: entry.name.clone(), args: entry.args.map(|x| self.desugar_argument(x)).to_vec(), typ: self.desugar_expr(&entry.typ), - span: entry.range.to_span(), + range: entry.range, attrs: self.desugar_attributes(&entry.attrs), rules, }; diff --git a/crates/kind-pass/src/erasure/mod.rs b/crates/kind-pass/src/erasure/mod.rs index ea2dc81b..84cbd049 100644 --- a/crates/kind-pass/src/erasure/mod.rs +++ b/crates/kind-pass/src/erasure/mod.rs @@ -16,12 +16,10 @@ use std::sync::mpsc::Sender; use fxhash::{FxHashMap, FxHashSet}; use kind_report::data::Diagnostic; - use kind_span::Range; -use kind_tree::{ - desugared::{self, Book, Entry, Expr, ExprKind, Rule}, - symbol::QualifiedIdent, -}; +use kind_tree::desugared::{Book, Entry, Expr, Rule}; +use kind_tree::symbol::QualifiedIdent; +use kind_tree::{untyped, Number}; use crate::errors::PassError; @@ -46,7 +44,7 @@ pub fn erase_book( book: Book, errs: Sender>, entrypoint: FxHashSet, -) -> Option { +) -> Option { let mut state = ErasureState { errs, book: &book, @@ -56,7 +54,7 @@ pub fn erase_book( failed: false, }; - let mut new_book = Book { + let mut new_book = untyped::Book { holes: book.holes, ..Default::default() }; @@ -82,6 +80,9 @@ pub fn erase_book( for (name, (_, relev)) in &state.names { if let Some(Relevance::Relevant) = state.normalize(*relev) { if let Some(res) = entries.remove(name) { + new_book + .names + .insert(name.to_string(), new_book.entrs.len()); new_book.entrs.insert(name.to_string(), res); } } @@ -99,20 +100,21 @@ impl<'a> ErasureState<'a> { local_relevance } + pub fn send_err(&mut self, err: Box) { + self.errs.send(err).unwrap(); + self.failed = true; + } pub fn err_irrelevant( &mut self, declared_val: Option, used: Range, declared_ty: Option, ) { - self.errs - .send(Box::new(PassError::CannotUseIrrelevant( - declared_val, - used, - declared_ty, - ))) - .unwrap(); - self.failed = true; + self.send_err(Box::new(PassError::CannotUseIrrelevant( + declared_val, + used, + declared_ty, + ))); } pub fn get_normal(&self, hole: usize, visited: &mut FxHashSet) -> Option { @@ -195,7 +197,7 @@ impl<'a> ErasureState<'a> { on: (Option, Relevance), name: &QualifiedIdent, spine: &Vec>, - ) -> Vec> { + ) -> Vec> { let fun = match self.names.get(name.to_str()) { Some(res) => *res, None => self.new_hole(name.range, name.to_string()), @@ -213,7 +215,7 @@ impl<'a> ErasureState<'a> { .zip(erased) .map(|(elem, arg)| { let relev = if arg.erased { - (Some(arg.span), Relevance::Irrelevant) + (Some(arg.range), Relevance::Irrelevant) } else { on.clone() }; @@ -224,161 +226,169 @@ impl<'a> ErasureState<'a> { .collect() } - pub fn erase_pat(&mut self, on: (Option, Relevance), pat: &Expr) -> Box { + pub fn erase_pat(&mut self, on: (Option, Relevance), pat: &Expr) -> Box { use kind_tree::desugared::ExprKind::*; match &pat.data { - Num(_) | Str(_) => Box::new(pat.clone()), - Var(name) => { + Num { + num: Number::U60(n), + } => untyped::Expr::num60(pat.range, *n), + Num { + num: Number::U120(n), + } => untyped::Expr::num120(pat.range, *n), + Var { name } => { self.ctx.insert(name.to_string(), (name.range, on)); - Box::new(pat.clone()) + untyped::Expr::var(name.clone()) } - Fun(name, spine) | Ctr(name, spine) if on.1 == Relevance::Irrelevant => { - let range = pat.span.to_range().unwrap_or_else(|| name.range.clone()); + Fun { name, args } | Ctr { name, args } if on.1 == Relevance::Irrelevant => { + let range = pat.range.clone(); self.errs .send(Box::new(PassError::CannotPatternMatchOnErased(range))) .unwrap(); self.failed = true; - self.erase_pat_spine(on, &name, spine); - desugared::Expr::err(range) + self.erase_pat_spine(on, &name, args); + untyped::Expr::err(range) } - Fun(name, spine) => { - let spine = self.erase_pat_spine(on, &name, spine); - Box::new(Expr { - span: pat.span.clone(), - data: ExprKind::Fun(name.clone(), spine), - }) + Fun { name, args } => { + let args = self.erase_pat_spine(on, &name, args); + untyped::Expr::fun(pat.range.clone(), name.clone(), args) } - Ctr(name, spine) => { - let spine = self.erase_pat_spine(on, &name, spine); - Box::new(Expr { - span: pat.span.clone(), - data: ExprKind::Ctr(name.clone(), spine), - }) + Ctr { name, args } => { + let args = self.erase_pat_spine(on, &name, args); + untyped::Expr::ctr(pat.range.clone(), name.clone(), args) } res => panic!("Internal Error: Not a pattern {:?}", res), } } - pub fn erase_expr(&mut self, on: &(Option, Relevance), expr: &Expr) -> Box { + pub fn erase_expr( + &mut self, + on: &(Option, Relevance), + expr: &Expr, + ) -> Box { use kind_tree::desugared::ExprKind::*; match &expr.data { - Num(_) | Str(_) => Box::new(expr.clone()), - Typ | NumType(_) | Err | Hole(_) | Hlp(_) => { - let span = expr.span.to_range().unwrap(); - if !self.unify(span, *on, (None, Relevance::Irrelevant), false) { - self.err_irrelevant(None, span, None) + Typ | NumType { .. } | Err | Hole { .. } | Hlp(_) => { + if !self.unify(expr.range, *on, (None, Relevance::Irrelevant), false) { + self.err_irrelevant(None, expr.range, None) } - Box::new(expr.clone()) + // Used as sentinel value because all of these constructions + // should not end in the generated tree. + untyped::Expr::err(expr.range) } - Var(name) => { + Num { + num: Number::U60(n), + } => untyped::Expr::num60(expr.range, *n), + Num { + num: Number::U120(n), + } => untyped::Expr::num120(expr.range, *n), + Str { val } => untyped::Expr::str(expr.range, val.clone()), + Var { name } => { let relev = self.ctx.get(name.to_str()).unwrap(); let declared_ty = (relev.1).0; let declared_val = relev.0; if !self.unify(name.range, *on, relev.1, false) { self.err_irrelevant(Some(declared_val), name.range, declared_ty) } - Box::new(expr.clone()) + untyped::Expr::var(name.clone()) } - All(name, typ, body, _erased) => { - let span = expr.span.to_range().unwrap_or_else(|| name.range.clone()); - if !self.unify(span, *on, (None, Relevance::Irrelevant), false) { - self.err_irrelevant(None, span, None) + All { + param, typ, body, .. + } => { + if !self.unify(expr.range, *on, (None, Relevance::Irrelevant), false) { + self.err_irrelevant(None, expr.range, None) } let ctx = self.ctx.clone(); // Relevant inside the context that is it's being used? - self.ctx.insert(name.to_string(), (name.range, *on)); + self.ctx.insert(param.to_string(), (param.range, *on)); self.erase_expr(&(on.0, Relevance::Irrelevant), typ); self.erase_expr(&(on.0, Relevance::Irrelevant), body); self.ctx = ctx; - Box::new(expr.clone()) + // Used as sentinel value because "All" should not end in a tree. + untyped::Expr::err(expr.range) } - Lambda(name, body, erased) => { + Lambda { + param, + body, + erased, + } => { let ctx = self.ctx.clone(); + if *erased { self.ctx.insert( - name.to_string(), - (name.range, (None, Relevance::Irrelevant)), + param.to_string(), + (param.range, (None, Relevance::Irrelevant)), ); } else { - self.ctx.insert(name.to_string(), (name.range, *on)); + self.ctx.insert(param.to_string(), (param.range, *on)); } + let body = self.erase_expr(on, body); self.ctx = ctx; if *erased { body } else { - Box::new(Expr { - span: expr.span, - data: ExprKind::Lambda(name.clone(), body, *erased), - }) + untyped::Expr::lambda(expr.range, param.clone(), body, *erased) } } - Let(name, val, body) => { + Let { name, val, next } => { let ctx = self.ctx.clone(); self.ctx.insert(name.to_string(), (name.range, *on)); let val = self.erase_expr(on, val); - let body = self.erase_expr(on, body); + let next = self.erase_expr(on, next); self.ctx = ctx; - Box::new(Expr { - span: expr.span, - data: ExprKind::Let(name.clone(), val, body), - }) + untyped::Expr::let_(expr.range, name.clone(), val, next) } - App(head, spine) => { - let head = self.erase_expr(on, head); - let spine = spine + App { fun, args } => { + let head = self.erase_expr(on, fun); + let spine = args .iter() .map(|x| { let on = if x.erased { - let span = expr.span.to_range().unwrap(); + let span = expr.range; if !self.unify(span, *on, (None, Relevance::Irrelevant), false) { self.err_irrelevant(None, span, None) } - (x.data.span.to_range(), Relevance::Irrelevant) + (Some(x.data.range), Relevance::Irrelevant) } else { on.clone() }; - desugared::AppBinding { - data: self.erase_expr(&on, &x.data), - erased: x.erased, - } + (x.erased, self.erase_expr(&on, &x.data)) }) - .filter(|x| !x.erased) + .filter(|x| !x.0) + .map(|x| x.1) .collect(); - Box::new(Expr { - span: expr.span, - data: ExprKind::App(head, spine), - }) - } - Fun(head, spine) => { - let args = self.book.entrs.get(head.to_str()).unwrap().args.iter(); - let fun = match self.names.get(head.to_str()) { + untyped::Expr::app(expr.range, head, spine) + } + Fun { name, args } => { + let spine = self.book.entrs.get(name.to_str()).unwrap().args.iter(); + + let fun = match self.names.get(name.to_str()) { Some(res) => *res, - None => self.new_hole(head.range, head.to_string()), + None => self.new_hole(name.range, name.to_string()), }; - if !self.unify(head.range, *on, fun, true) { - self.err_irrelevant(None, head.range, None) + if !self.unify(name.range, *on, fun, true) { + self.err_irrelevant(None, name.range, None) } - let spine = spine + let spine = args .iter() - .zip(args) + .zip(spine) .map(|(expr, arg)| { if arg.erased { ( - self.erase_expr(&(Some(arg.span), Relevance::Irrelevant), expr), + self.erase_expr(&(Some(arg.range), Relevance::Irrelevant), expr), arg, ) } else { @@ -387,30 +397,31 @@ impl<'a> ErasureState<'a> { }) .filter(|(_, arg)| !arg.erased); - Box::new(Expr { - span: expr.span, - data: ExprKind::Fun(head.clone(), spine.map(|(expr, _)| expr).collect()), - }) + untyped::Expr::fun( + expr.range, + name.clone(), + spine.map(|(expr, _)| expr).collect(), + ) } - Ctr(head, spine) => { - let args = self.book.entrs.get(head.to_str()).unwrap().args.iter(); + Ctr { name, args } => { + let spine = self.book.entrs.get(name.to_str()).unwrap().args.iter(); - let fun = match self.names.get(&head.to_string()) { + let fun = match self.names.get(&name.to_string()) { Some(res) => *res, - None => self.new_hole(head.range, head.to_string()), + None => self.new_hole(name.range, name.to_string()), }; - if !self.unify(head.range, *on, fun, true) { - self.err_irrelevant(None, head.range, None) + if !self.unify(name.range, *on, fun, true) { + self.err_irrelevant(None, name.range, None) } - let spine = spine + let spine = args .iter() - .zip(args) + .zip(spine) .map(|(expr, arg)| { if arg.erased { ( - self.erase_expr(&(Some(arg.span), Relevance::Irrelevant), expr), + self.erase_expr(&(Some(arg.range), Relevance::Irrelevant), expr), arg, ) } else { @@ -419,21 +430,24 @@ impl<'a> ErasureState<'a> { }) .filter(|(_, arg)| !arg.erased); - Box::new(Expr { - span: expr.span, - data: ExprKind::Ctr(head.clone(), spine.map(|(expr, _)| expr).collect()), - }) + untyped::Expr::ctr( + expr.range, + name.clone(), + spine.map(|(expr, _)| expr).collect(), + ) } - Ann(relev, irrel) => { - let res = self.erase_expr(on, relev); - self.erase_expr(&(None, Relevance::Irrelevant), irrel); + Ann { expr, typ } => { + let res = self.erase_expr(on, expr); + self.erase_expr(&(None, Relevance::Irrelevant), typ); res } - Sub(_, _, _, expr) => self.erase_expr(on, expr), - Binary(op, left, right) => Box::new(Expr { - span: expr.span, - data: ExprKind::Binary(*op, self.erase_expr(on, left), self.erase_expr(on, right)), - }), + Sub { expr, .. } => self.erase_expr(on, expr), + Binary { op, left, right } => untyped::Expr::binary( + expr.range, + *op, + self.erase_expr(on, left), + self.erase_expr(on, right), + ), } } @@ -442,7 +456,7 @@ impl<'a> ErasureState<'a> { place: &(Option, Relevance), args: Vec<(Range, bool)>, rule: &Rule, - ) -> Rule { + ) -> untyped::Rule { let ctx = self.ctx.clone(); let new_pats: Vec<_> = args @@ -472,34 +486,35 @@ impl<'a> ErasureState<'a> { self.ctx = ctx; - Rule { + untyped::Rule { name: rule.name.clone(), pats: new_pats, body, - span: rule.span, + range: rule.range, } } - pub fn erase_entry(&mut self, entry: &Entry) -> Box { + pub fn erase_entry(&mut self, entry: &Entry) -> Box { let place = if let Some(res) = self.names.get(entry.name.to_str()) { *res } else { self.new_hole(entry.name.range, entry.name.to_string()) }; - let args: Vec<(Range, bool)> = entry.args.iter().map(|x| (x.span, x.erased)).collect(); + let args: Vec<(Range, bool)> = entry.args.iter().map(|x| (x.range, x.erased)).collect(); + let rules = entry .rules .iter() .map(|rule| self.erase_rule(&place, args.clone(), rule)) - .collect::>(); - Box::new(Entry { + .collect::>(); + + Box::new(untyped::Entry { name: entry.name.clone(), - args: entry.args.clone(), - typ: entry.typ.clone(), + args: entry.args.iter().filter(|x| !x.erased).map(|x| (x.name.to_string(), x.range, false)).collect(), rules, attrs: entry.attrs.clone(), - span: entry.span, + range: entry.range, }) } } diff --git a/crates/kind-pass/src/errors.rs b/crates/kind-pass/src/errors.rs index 78dbb3b2..6cdc4735 100644 --- a/crates/kind-pass/src/errors.rs +++ b/crates/kind-pass/src/errors.rs @@ -1,5 +1,5 @@ use kind_report::data::{Color, Diagnostic, DiagnosticFrame, Marker, Severity}; -use kind_span::{Range, Span, SyntaxCtxIndex}; +use kind_span::{Range, SyntaxCtxIndex}; use kind_tree::symbol::Ident; pub enum Sugar { @@ -32,7 +32,7 @@ pub enum PassError { CannotUseIrrelevant(Option, Range, Option), CannotFindAlias(String, Range), NotATypeConstructor(Range, Range), - ShouldBeAParameter(Span, Range), + ShouldBeAParameter(Option, Range), NoFieldCoverage(Range, Vec), CannotPatternMatchOnErased(Range), UnboundVariable(Vec, Vec), @@ -427,15 +427,14 @@ impl Diagnostic for PassError { PassError::ShouldBeAParameter(error_range, declaration_range) => { let mut positions = vec![]; - match error_range { - Span::Generated => (), - Span::Locatable(error_range) => positions.push(Marker { + if let Some(error_range) = error_range { + positions.push(Marker { position: *error_range, color: Color::Fst, text: "This expression is not the parameter".to_string(), no_code: false, main: true, - }), + }) } positions.push(Marker { diff --git a/crates/kind-pass/src/unbound/mod.rs b/crates/kind-pass/src/unbound/mod.rs index e38dc6b4..4d3b0167 100644 --- a/crates/kind-pass/src/unbound/mod.rs +++ b/crates/kind-pass/src/unbound/mod.rs @@ -490,8 +490,8 @@ impl Visitor for UnboundCollector { } ExprKind::Hole => {} ExprKind::Do { typ, sttm } => { - self.visit_qualified_ident(&mut typ.add_segment("pure")); - self.visit_qualified_ident(&mut typ.add_segment("bind")); + self.visit_qualified_ident(&mut typ.add_segment("pure").to_generated()); + self.visit_qualified_ident(&mut typ.add_segment("bind").to_generated()); self.visit_sttm(sttm) } ExprKind::If { cond, then_, else_ } => { diff --git a/crates/kind-report/src/data.rs b/crates/kind-report/src/data.rs index 04a2e7ad..a7269be6 100644 --- a/crates/kind-report/src/data.rs +++ b/crates/kind-report/src/data.rs @@ -55,6 +55,7 @@ pub struct DiagnosticFrame { pub enum Log { Checking(String), Checked(Duration), + Compiled(Duration), Failed(Duration), } pub trait Diagnostic { diff --git a/crates/kind-report/src/report.rs b/crates/kind-report/src/report.rs index 2c6aaca7..e3719010 100644 --- a/crates/kind-report/src/report.rs +++ b/crates/kind-report/src/report.rs @@ -93,13 +93,14 @@ fn get_colorizer(color: &Color) -> &dyn Fn(T) -> Paint { fn colorize_code<'a, T: Write + Sized>( markers: &mut [&(Point, Point, &Marker)], code_line: &'a str, + modify: &dyn Fn(&str) -> String, fmt: &mut T, ) -> std::fmt::Result { markers.sort_by(|x, y| x.0.column.cmp(&y.0.column)); let mut start = 0; for marker in markers { if start < marker.0.column { - write!(fmt, "{}", &code_line[start..marker.0.column])?; + write!(fmt, "{}", modify(&code_line[start..marker.0.column]))?; start = marker.0.column; } @@ -117,7 +118,7 @@ fn colorize_code<'a, T: Write + Sized>( } if start < code_line.len() { - write!(fmt, "{}", &code_line[start..code_line.len()])?; + write!(fmt, "{}", modify(&code_line[start..code_line.len()]))?; } writeln!(fmt)?; Ok(()) @@ -164,6 +165,8 @@ fn mark_inlined( } } writeln!(fmt)?; + + // Pretty print the marker for i in 0..inline_markers.len() { write!( fmt, @@ -275,6 +278,7 @@ fn write_code_block<'a, T: Write + Sized>( } let code_lines: Vec<&'a str> = group_code.lines().collect(); + let mut lines = lines_set .iter() .filter(|x| **x < code_lines.len()) @@ -285,9 +289,12 @@ fn write_code_block<'a, T: Write + Sized>( let line = lines[i]; let mut prefix = " ".to_string(); let mut empty_vec = Vec::new(); + let row = markers_by_line.get_mut(line).unwrap_or(&mut empty_vec); + let mut inline_markers: Vec<&(Point, Point, &Marker)> = row.iter().filter(|x| x.0.line == x.1.line).collect(); + let mut current = None; for marker in &multi_line_markers { @@ -315,12 +322,15 @@ fn write_code_block<'a, T: Write + Sized>( prefix, )?; - if let Some(marker) = current { + let modify: Box String> = if let Some(marker) = current { prefix = format!(" {} ", get_colorizer(&marker.2.color)(config.chars.vbar)); - } + Box::new(|str: &str| get_colorizer(&marker.2.color)(str).to_string()) + } else { + Box::new(|str: &str| str.to_string()) + }; if !inline_markers.is_empty() { - colorize_code(&mut inline_markers, code_lines[*line], fmt)?; + colorize_code(&mut inline_markers, code_lines[*line], &modify, fmt)?; mark_inlined(&prefix, code_lines[*line], config, &mut inline_markers, fmt)?; if markers_by_line.contains_key(&(line + 1)) { writeln!( @@ -332,7 +342,7 @@ fn write_code_block<'a, T: Write + Sized>( )?; } } else { - writeln!(fmt, "{}", code_lines[*line])?; + writeln!(fmt, "{}", modify(code_lines[*line]))?; } if let Some(marker) = current { @@ -501,12 +511,20 @@ impl Report for Log { file ) } + Log::Compiled(duration) => { + writeln!( + fmt, + " {} All relevant terms compiled. took {:.2}s", + Paint::new(" COMPILED ").bg(yansi::Color::Green).bold(), + duration.as_secs_f32() + ) + } Log::Checked(duration) => { writeln!( fmt, - " {} took {}s", + " {} All terms checked. took {:.2}s", Paint::new(" CHECKED ").bg(yansi::Color::Green).bold(), - duration.as_secs() + duration.as_secs_f32() ) } Log::Failed(duration) => { diff --git a/crates/kind-span/src/lib.rs b/crates/kind-span/src/lib.rs index 40c91266..97f9a061 100644 --- a/crates/kind-span/src/lib.rs +++ b/crates/kind-span/src/lib.rs @@ -17,7 +17,7 @@ impl SyntaxCtxIndex { /// A span in the encoded format that is required by /// kind2. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] -pub struct EncodedSpan(pub u64); +pub struct EncodedRange(pub u64); /// Describes a position in a source code (syntax context). It's useful /// to generate error messages. @@ -28,13 +28,6 @@ pub struct Range { pub ctx: SyntaxCtxIndex, } -/// Range that can be generated. -#[derive(Clone, Debug, Copy, Hash, PartialEq, Eq)] -pub enum Span { - Generated, - Locatable(Range), -} - pub trait Locatable { fn locate(&self) -> Range; } @@ -49,10 +42,6 @@ impl Range { Range::new(Pos { index: 0 }, Pos { index: 0 }, SyntaxCtxIndex(0)) } - pub fn to_span(self) -> Span { - Span::Locatable(self) - } - /// Joins two ranges. It keeps the syntax context /// of the first one. #[inline] @@ -75,8 +64,8 @@ impl Range { } #[inline] - pub fn encode(&self) -> EncodedSpan { - EncodedSpan( + pub fn encode(&self) -> EncodedRange { + EncodedRange( ((self.ctx.0 as u64) << 48) | ((self.start.index as u64) & 0xFFFFFF) | (((self.end.index as u64) & 0xFFFFFF) << 24), @@ -84,53 +73,7 @@ impl Range { } } -impl Span { - #[inline] - pub fn new(range: Range) -> Span { - Span::Locatable(range) - } - - #[inline] - pub fn generate() -> Span { - Span::Generated - } - - pub fn to_range(&self) -> Option { - match self { - Span::Generated => None, - Span::Locatable(pos) => Some(pos.clone()), - } - } - - /// Join two spans and keeps the syntax context of the - /// first locatable range. If it's generated then it - /// will be ignored and the other span will be the canonical - /// position. - pub fn mix(&self, other: Span) -> Span { - match (self, &other) { - (Span::Generated, e) | (e, Span::Generated) => *e, - (Span::Locatable(start), Span::Locatable(end)) => Span::Locatable(start.mix(*end)), - } - } - - /// Set the syntax context of the span. - pub fn set_ctx(&mut self, ctx: SyntaxCtxIndex) { - match self { - Span::Generated => (), - Span::Locatable(span) => *span = span.set_ctx(ctx), - } - } - - // Serialize the span into a single u64. - pub fn encode(&self) -> EncodedSpan { - match self { - Span::Generated => EncodedSpan(0), - Span::Locatable(data) => data.encode(), - } - } -} - -impl EncodedSpan { +impl EncodedRange { /// Transforms a encoded span back into a range. pub fn to_range(&self) -> Range { Range { @@ -143,13 +86,4 @@ impl EncodedSpan { }, } } - - /// Transforms a encoded span back into a span. - pub fn to_span(&self) -> Span { - if self.0 == 0 { - Span::Generated - } else { - Span::Locatable(self.to_range()) - } - } } diff --git a/crates/kind-target-hvm/src/lib.rs b/crates/kind-target-hvm/src/lib.rs index 25eec1d0..8a657d95 100644 --- a/crates/kind-target-hvm/src/lib.rs +++ b/crates/kind-target-hvm/src/lib.rs @@ -2,10 +2,10 @@ use hvm::u60; use kind_tree::{ backend::{File, Rule, Term}, - desugared, + untyped, }; -pub fn compile_book(book: desugared::Book) -> File { +pub fn compile_book(book: untyped::Book) -> File { let mut file = File { rules: Default::default(), smaps: Default::default(), @@ -16,37 +16,35 @@ pub fn compile_book(book: desugared::Book) -> File { file } -pub fn compile_term(expr: &desugared::Expr) -> Box { - use desugared::ExprKind::*; +pub fn compile_term(expr: &untyped::Expr) -> Box { + use untyped::ExprKind::*; match &expr.data { - Var(name) => Box::new(Term::Var { + Var { name } => Box::new(Term::Var { name: name.to_string(), }), - Lambda(binder, body, _erased) => Box::new(Term::Lam { - name: binder.to_string(), + Lambda { param, body, .. } => Box::new(Term::Lam { + name: param.to_string(), body: compile_term(body), }), - App(head, spine) => spine.iter().fold(compile_term(head), |func, arg| { + App { fun, args } => args.iter().fold(compile_term(fun), |func, arg| { Box::new(Term::App { func, - argm: compile_term(&arg.data), + argm: compile_term(&arg), }) }), - Fun(head, spine) | Ctr(head, spine) => Box::new(Term::Ctr { - name: head.to_string(), - args: spine.iter().map(|x| compile_term(x)).collect(), - }), - Let(name, expr, body) => Box::new(Term::Let { + Fun { name, args } | Ctr { name, args } => Box::new(Term::Ctr { name: name.to_string(), - expr: compile_term(expr), - body: compile_term(body), + args: args.iter().map(|x| compile_term(x)).collect(), }), - Ann(left, _) => compile_term(left), - Sub(_, _, _, expr) => compile_term(expr), - Num(kind_tree::Number::U60(numb)) => Box::new(Term::U6O { + Let { name, val, next } => Box::new(Term::Let { + name: name.to_string(), + expr: compile_term(val), + body: compile_term(next), + }), + Num { num: kind_tree::Number::U60(numb) } => Box::new(Term::U6O { numb: u60::new(*numb), }), - Num(kind_tree::Number::U120(numb)) => { + Num { num: kind_tree::Number::U120(numb) } => { let hi = Box::new(Term::U6O { numb: u60::new((numb >> 60) as u64), }); @@ -58,11 +56,11 @@ pub fn compile_term(expr: &desugared::Expr) -> Box { args: vec![hi, lo], }) } - Binary(op, l, r) => Box::new(Term::Ctr { + Binary { op, left, right } => Box::new(Term::Ctr { name: op.to_string(), - args: vec![compile_term(l), compile_term(r)], + args: vec![compile_term(left), compile_term(right)], }), - Str(str) => { + Str { val } => { let nil = Box::new(Term::Ctr { name: String::from("String.nil"), args: vec![], @@ -75,18 +73,13 @@ pub fn compile_term(expr: &desugared::Expr) -> Box { }) }; - str.chars().rfold(nil, |rest, chr| cons(chr as u64, rest)) + val.chars().rfold(nil, |rest, chr| cons(chr as u64, rest)) } - Hole(_) => unreachable!("Internal Error: 'Hole' cannot be a relevant term"), - Typ => unreachable!("Internal Error: 'Typ' cannot be a relevant term"), - NumType(typ) => unreachable!("Internal Error: '{:?}' cannot be a relevant term", typ), - All(_, _, _, _) => unreachable!("Internal Error: 'All' cannot be a relevant term"), - Hlp(_) => unreachable!("Internal Error: 'Hlp' cannot be a relevant term"), - Err => unreachable!("Internal Error: 'Err' cannot be a relevant term"), + Err => unreachable!("Internal Error: 'ERR' cannot be a relevant term"), } } -fn compile_rule(rule: desugared::Rule) -> Rule { +fn compile_rule(rule: untyped::Rule) -> Rule { Rule { lhs: Box::new(Term::Ctr { name: rule.name.to_string(), @@ -96,7 +89,7 @@ fn compile_rule(rule: desugared::Rule) -> Rule { } } -fn compile_entry(file: &mut File, entry: desugared::Entry) { +fn compile_entry(file: &mut File, entry: untyped::Entry) { for rule in entry.rules { file.rules.push(compile_rule(rule)) } diff --git a/crates/kind-target-kdl/Cargo.toml b/crates/kind-target-kdl/Cargo.toml index f7e09289..edbc1cb0 100644 --- a/crates/kind-target-kdl/Cargo.toml +++ b/crates/kind-target-kdl/Cargo.toml @@ -13,4 +13,5 @@ kind-derive = { path = "../kind-derive" } kindelia_lang = { git = "https://github.com/developedby/Kindelia/", branch = "kdl-lang-crate" } linked-hash-map = "0.5.6" +tiny-keccak = "2.0.2" fxhash = "0.2.1" \ No newline at end of file diff --git a/crates/kind-target-kdl/src/compile.rs b/crates/kind-target-kdl/src/compile.rs new file mode 100644 index 00000000..6bcf4989 --- /dev/null +++ b/crates/kind-target-kdl/src/compile.rs @@ -0,0 +1,368 @@ +use std::{fmt::Display, sync::mpsc::Sender}; + +use fxhash::FxHashMap; +use kind_report::data::Diagnostic; +use kind_tree::{symbol::QualifiedIdent, untyped, Number}; +use linked_hash_map::LinkedHashMap; +use tiny_keccak::Hasher; + +pub use kindelia_lang::ast as kdl; + +use crate::errors::KdlError; + +pub const KDL_NAME_LEN: usize = 12; + +#[derive(Debug)] +pub struct File { + funs: LinkedHashMap, + runs: Vec, +} + +pub struct CompileCtx<'a> { + file: File, + kdl_names: FxHashMap, + kdl_states: Vec, + book: &'a untyped::Book, + + sender: Sender>, + failed: bool, +} + +impl<'a> CompileCtx<'a> { + pub fn new(book: &'a untyped::Book, sender: Sender>) -> CompileCtx<'a> { + CompileCtx { + file: File { + funs: Default::default(), + runs: Default::default(), + }, + kdl_names: Default::default(), + kdl_states: Default::default(), + book, + sender, + failed: false, + } + } + + pub fn send_err(&mut self, err: Box) { + self.sender.send(err).unwrap(); + self.failed = true; + } +} + +// Functions to generate a new name + +fn encode_base64_u8(num: u8) -> char { + match num { + 0..=9 => (num + b'0') as char, + 10..=35 => (num - 10 + b'A') as char, + 36..=61 => (num - 36 + b'a') as char, + 62.. => '_', + } +} + +fn u128_to_kdl_name(mut num: u128) -> String { + let mut encoded = [0 as char; 12]; + for i in 0..12 { + encoded[i] = encode_base64_u8((num & 0x3f) as u8); + num >>= 6; + } + encoded.into_iter().collect() +} + +fn keccak128(data: &[u8]) -> [u8; 16] { + let mut hasher = tiny_keccak::Keccak::v256(); + let mut output = [0u8; 16]; + hasher.update(data); + hasher.finalize(&mut output); + output +} + +fn name_shortener(name: &QualifiedIdent, namespace: &str) -> QualifiedIdent { + let max_fn_name = KDL_NAME_LEN - namespace.len(); + + if name.to_str().len() > max_fn_name { + let name_hash = keccak128(name.to_str().as_bytes()); + let name_hash = u128::from_le_bytes(name_hash); + let name_hash = u128_to_kdl_name(name_hash); + QualifiedIdent::new_static(&name_hash[..max_fn_name], None, name.range) + } else { + name.clone() + } +} + +pub fn compile_book( + book: &untyped::Book, + sender: Sender>, + namespace: &str, +) -> Option { + let mut ctx = CompileCtx::new(book, sender); + + for (name, entry) in &book.entrs { + let new_name = entry + .attrs + .kdl_name + .clone() + .map(|x| x.to_string()) + .unwrap_or_else(|| name_shortener(&entry.name, namespace).to_string()); + + if let Ok(new_name) = kdl::Name::from_str(&new_name) { + ctx.kdl_names.insert(name.clone(), new_name); + } else { + ctx.send_err(Box::new(KdlError::InvalidVarName(entry.name.range))); + } + } + + for (_name, entry) in &book.entrs { + compile_entry(&mut ctx, entry); + } + + if ctx.failed { + return None; + } + + Some(ctx.file) +} + +pub fn compile_rule(ctx: &mut CompileCtx, rule: &untyped::Rule) -> kindelia_lang::ast::Rule { + let name = ctx.kdl_names.get(rule.name.to_str()).unwrap().clone(); + let mut args = Vec::new(); + for pat in &rule.pats { + let arg = compile_expr(ctx, pat); + args.push(arg); + } + let lhs = kdl::Term::fun(name, args); + let rhs = compile_expr(ctx, &rule.body); + let rule = kdl::Rule { lhs, rhs }; + rule +} + +pub fn err_term() -> kindelia_lang::ast::Term { + kindelia_lang::ast::Term::Num { + numb: kindelia_lang::ast::U120::new(99999).unwrap(), + } +} + +pub fn compile_expr(ctx: &mut CompileCtx, expr: &untyped::Expr) -> kindelia_lang::ast::Term { + use crate::untyped::ExprKind::*; + use kdl::Term as T; + match &expr.data { + App { fun, args } => { + let mut expr = compile_expr(ctx, fun); + for binding in args { + let body = compile_expr(ctx, &binding); + expr = T::App { + func: Box::new(expr), + argm: Box::new(body), + }; + } + expr + } + Binary { op, left, right } => { + // TODO: Special compilation for U60 ops + let oper = compile_oper(op); + let val0 = Box::new(compile_expr(ctx, left)); + let val1 = Box::new(compile_expr(ctx, right)); + T::Op2 { oper, val0, val1 } + } + Ctr { 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)); + } + T::Ctr { + name, + args: new_args, + } + } + Fun { name, args } => { + // 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, + body, + erased: _, + } => { + 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 } + } else { + ctx.send_err(Box::new(KdlError::InvalidVarName(param.range))); + err_term() + } + } + 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 argm = Box::new(compile_expr(ctx, next)); + T::App { func, argm } + } else { + ctx.send_err(Box::new(KdlError::InvalidVarName(name.range))); + err_term() + } + } + Num { + num: Number::U60(numb), + } => T::Num { + numb: kdl::U120(*numb as u128), + }, + Num { + num: Number::U120(numb), + } => T::Num { + numb: kdl::U120(*numb), + }, + Var { name } => { + let res_name = kdl::Name::from_str(name.to_str()); + if let Ok(name) = res_name { + T::Var { name } + } else { + ctx.send_err(Box::new(KdlError::InvalidVarName(name.range))); + err_term() + } + } + Str { val } => { + let nil = kdl::Term::Ctr { + name: ctx.kdl_names.get("String.nil").unwrap().clone(), + args: vec![], + }; + + let cons_name = ctx.kdl_names.get("String.cons").unwrap().clone(); + + let cons = |numb: u128, next| kdl::Term::Ctr { + name: cons_name.clone(), + args: vec![ + kdl::Term::Num { + numb: kdl::U120::new(numb).unwrap(), + }, + next, + ], + }; + + val.chars().rfold(nil, |rest, chr| cons(chr as u128, rest)) + } + Err => unreachable!("Should not have errors inside generation"), + } +} + +pub fn compile_entry(ctx: &mut CompileCtx, entry: &untyped::Entry) { + if entry.attrs.kdl_erase { + return; + } + + if entry.attrs.kdl_run { + if !entry.args.is_empty() { + ctx.send_err(Box::new(KdlError::ShouldNotHaveArguments(entry.range))); + } else if entry.rules.len() != 1 { + ctx.send_err(Box::new(KdlError::ShouldHaveOnlyOneRule(entry.range))); + } else { + let expr = compile_expr(ctx, &entry.rules[0].body); + let statement = kdl::Statement::Run { expr, sign: None }; + ctx.file.runs.push(statement); + } + } else { + let name = ctx.kdl_names.get(entry.name.to_str()).cloned().unwrap(); + + let mut args = Vec::new(); + + for (name, range, _strictness) in &entry.args { + if let Ok(name) = kdl::Name::from_str(name) { + args.push(name) + } else { + ctx.send_err(Box::new(KdlError::InvalidVarName(*range))); + } + } + + if entry.rules.len() == 0 { + let sttm = kdl::Statement::Ctr { + name, + args, + sign: None, + }; + ctx.file.funs.insert(entry.name.to_string(), sttm); + } else { + let rules = entry + .rules + .iter() + .map(|rule| compile_rule(ctx, rule)) + .collect::>(); + let func = kdl::Func { rules }; + + let init = if let Some(state_name) = &entry.attrs.kdl_state { + let init_entry = ctx.book.entrs.get(state_name.to_str()); + if let Some(entry) = init_entry { + if !entry.args.is_empty() { + ctx.send_err(Box::new(KdlError::ShouldNotHaveArguments(entry.range))); + None + } else if entry.rules.len() != 1 { + ctx.send_err(Box::new(KdlError::ShouldHaveOnlyOneRule(entry.range))); + None + } else { + ctx.kdl_states.push(state_name.to_string()); + Some(compile_expr(ctx, &entry.rules[0].body)) + } + } else { + ctx.send_err(Box::new(KdlError::NoInitEntry(state_name.range))); + None + } + } else { + None + }; + + let sttm = kdl::Statement::Fun { + name, + args, + func, + init, + sign: None, + }; + ctx.file.funs.insert(entry.name.to_string(), sttm); + } + } +} + +impl Display for File { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + for fun in &self.funs { + writeln!(f, "{}", fun.1)?; + } + for run in &self.runs { + writeln!(f, "{}", run)?; + } + Ok(()) + } +} + +fn compile_oper(oper: &kind_tree::Operator) -> kdl::Oper { + use kdl::Oper as T; + use kind_tree::Operator as F; + 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, + } +} diff --git a/crates/kind-target-kdl/src/errors.rs b/crates/kind-target-kdl/src/errors.rs new file mode 100644 index 00000000..3b1938ba --- /dev/null +++ b/crates/kind-target-kdl/src/errors.rs @@ -0,0 +1,82 @@ +use kind_report::data::{Diagnostic, DiagnosticFrame, Severity, Marker, Color}; +use kind_span::Range; + +pub enum KdlError { + InvalidVarName(Range), + ShouldNotHaveArguments(Range), + ShouldHaveOnlyOneRule(Range), + NoInitEntry(Range), +} + +impl Diagnostic for KdlError { + fn get_syntax_ctx(&self) -> Option { + match self { + KdlError::InvalidVarName(range) => Some(range.ctx), + KdlError::ShouldNotHaveArguments(range) => Some(range.ctx), + KdlError::ShouldHaveOnlyOneRule(range) => Some(range.ctx), + KdlError::NoInitEntry(range) => Some(range.ctx), + + } + } + + fn to_diagnostic_frame(&self) -> kind_report::data::DiagnosticFrame { + match self { + KdlError::InvalidVarName(range) => DiagnosticFrame { + code: 600, + severity: Severity::Error, + title: "Invalid variable name for Kindelia.".to_string(), + subtitles: vec![], + hints: vec![], + positions: vec![Marker { + position: *range, + color: Color::Fst, + text: "Here!".to_string(), + no_code: false, + main: true, + }], + }, + KdlError::ShouldNotHaveArguments(range) => DiagnosticFrame { + code: 601, + severity: Severity::Error, + title: "This type of entry should not have arguments".to_string(), + subtitles: vec![], + hints: vec![], + positions: vec![Marker { + position: *range, + color: Color::Fst, + text: "Here!".to_string(), + no_code: false, + main: true, + }], + }, + KdlError::ShouldHaveOnlyOneRule(range) => DiagnosticFrame { + code: 603, + severity: Severity::Error, + title: "This entry should only have one rule.".to_string(), + subtitles: vec![], + hints: vec![], + positions: vec![Marker { + position: *range, + color: Color::Fst, + text: "Here!".to_string(), + no_code: false, + main: true, + }], + }, + KdlError::NoInitEntry(range) => DiagnosticFrame { + code: 604, + severity: Severity::Error, + title: "This entry have to have a init entry".to_string(), + subtitles: vec![], + hints: vec![], + positions: vec![Marker { + position: *range, + color: Color::Fst, + text: "Here!".to_string(), + no_code: false, + main: true, + }], + }, + } + } +} \ No newline at end of file diff --git a/crates/kind-target-kdl/src/flatten.rs b/crates/kind-target-kdl/src/flatten.rs new file mode 100644 index 00000000..7bb86f80 --- /dev/null +++ b/crates/kind-target-kdl/src/flatten.rs @@ -0,0 +1,235 @@ +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 linked_hash_map::LinkedHashMap; + +use crate::subst::subst; + +fn must_split(rule: &Rule) -> bool { + for pat in &rule.pats { + if let ExprKind::Ctr { args, .. } = &pat.data { + for arg in args { + if matches!(arg.data, ExprKind::Ctr { .. } | ExprKind::Num { .. }) { + return true; + } + } + } + } + false +} + +fn matches_together(a: &Rule, b: &Rule) -> (bool, bool) { + let mut same_shape = true; + + for (a_pat, b_pat) in a.pats.iter().zip(&b.pats) { + match (&a_pat.data, &b_pat.data) { + (ExprKind::Ctr { name: an, .. }, ExprKind::Ctr { name: bn, .. }) if an != bn => { + return (false, false); + } + (ExprKind::Num { num: a_numb }, ExprKind::Num { num: b_numb }) if a_numb != b_numb => { + return (false, false); + } + (ExprKind::Ctr { .. }, ExprKind::Num { .. }) => { + return (false, false); + } + (ExprKind::Num { .. }, ExprKind::Ctr { .. }) => { + return (false, false); + } + (ExprKind::Ctr { .. }, ExprKind::Var { .. }) => { + same_shape = false; + } + (ExprKind::Num { .. }, ExprKind::Var { .. }) => { + same_shape = false; + } + _ => {} + } + } + + (true, same_shape) +} + +fn split_rule( + rule: &Rule, + entry: &Entry, + i: usize, + name_count: &mut u64, + skip: &mut FxHashSet, +) -> (Rule, Vec) { + let num = *name_count; + *name_count += 1; + + let new_entry_name = QualifiedIdent::new_static( + &format!("{}{}_", entry.name.to_str(), num), + None, + entry.range, + ); + let mut new_entry_attrs = entry.attrs.clone(); + + new_entry_attrs.kdl_name = None; + + let mut new_entry_rules: Vec = Vec::new(); + let mut old_rule_pats: Vec> = Vec::new(); + let mut old_rule_body_args: Vec> = Vec::new(); + + let mut var_count = 0; + + for pat in &rule.pats { + match &pat.data { + ExprKind::Var { name } => { + old_rule_pats.push(pat.clone()); + old_rule_body_args.push(Expr::var(name.clone())); + } + ExprKind::Num { .. } => { + old_rule_pats.push(pat.clone()); + } + ExprKind::Ctr { name, args } => { + let mut new_pat_args = Vec::new(); + + for field in args { + let arg = match &field.data { + ExprKind::Ctr { .. } | ExprKind::Num { .. } => { + let name = Ident::new(format!(".x{}", var_count), field.range); + var_count += 1; + Expr::var(name) + } + ExprKind::Var { .. } => field.clone(), + _ => panic!("Internal Error: Cannot use this kind of expression during flattening"), + }; + new_pat_args.push(arg.clone()); + old_rule_body_args.push(arg); + } + + old_rule_pats.push(Expr::ctr(pat.range, name.clone(), new_pat_args)); + } + _ => unreachable!("Internal Error: Invalid constructor while decoding pats"), + } + } + + let old_rule_body = Expr::fun(rule.range, new_entry_name.clone(), old_rule_body_args); + + let old_rule = Rule { + name: entry.name.clone(), + pats: old_rule_pats, + body: old_rule_body, + range: rule.range, + }; + + for (j, other) in entry.rules.iter().enumerate().skip(i) { + let (compatible, same_shape) = matches_together(rule, other); + if compatible { + if same_shape { + skip.insert(j); + } + let mut new_rule_pats = Vec::new(); + let mut new_rule_body = other.body.clone(); + for (rule_pat, other_pat) in rule.pats.iter().zip(&other.pats) { + match (&rule_pat.data, &other_pat.data) { + (ExprKind::Ctr { .. }, ExprKind::Ctr { args: pat_args, .. }) => { + new_rule_pats.extend(pat_args.clone()); + } + (ExprKind::Ctr { name, args }, ExprKind::Var { name: opat_name }) => { + let mut new_ctr_args = vec![]; + + for _ in 0..args.len() { + let new_arg = + Expr::var(Ident::new(format!(".x{}", var_count), rule_pat.range)); + + var_count += 1; + + new_ctr_args.push(new_arg.clone()); + new_rule_pats.push(new_arg); + } + + let new_ctr = Expr::ctr(name.range, name.clone(), new_ctr_args); + + subst(&mut new_rule_body, &opat_name, &new_ctr); + } + (ExprKind::Var { .. }, _) => { + new_rule_pats.push(other_pat.clone()); + } + (ExprKind::Num { .. }, ExprKind::Num { .. }) => (), + (ExprKind::Num { .. }, ExprKind::Var { name }) => { + subst(&mut new_rule_body, &name, rule_pat); + } + _ => { + panic!("Internal error. Please report."); // not possible since it matches + } + } + } + + let new_rule = Rule { + name: new_entry_name.clone(), + pats: new_rule_pats, + body: new_rule_body, + range: new_entry_name.range, + }; + new_entry_rules.push(new_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 = Entry { + name: new_entry_name, + args: new_entry_args, + rules: new_entry_rules, + attrs: new_entry_attrs, + range: entry.range, + }; + + let new_split_entries = flatten_entry(&new_entry); + (old_rule, new_split_entries) +} + +fn flatten_entry(entry: &Entry) -> Vec { + let mut name_count = 0; + + let mut skip: FxHashSet = FxHashSet::default(); + let mut new_entries: Vec = Vec::new(); + let mut old_entry_rules: Vec = Vec::new(); + + for i in 0..entry.rules.len() { + 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); + old_entry_rules.push(old_rule); + new_entries.extend(split_entries); + } else { + old_entry_rules.push(entry.rules[i].clone()); + } + } + } + + let old_entry = Entry { + name: entry.name.clone(), + args: entry.args.clone(), + rules: old_entry_rules, + range: entry.range, + attrs: entry.attrs.clone(), + }; + + new_entries.push(old_entry); + new_entries +} + +pub fn flatten(book: untyped::Book) -> untyped::Book { + let mut book = book; + let mut names = FxHashMap::default(); + let mut entrs = LinkedHashMap::default(); + + for name in book.names.keys() { + let entry = book.entrs.remove(name).unwrap(); + for entry in flatten_entry(&entry) { + names.insert(entry.name.to_string(), entrs.len()); + entrs.insert(entry.name.to_string(), Box::new(entry)); + } + } + + let book = Book { names, entrs, holes: book.holes }; + + book +} diff --git a/crates/kind-target-kdl/src/lib.rs b/crates/kind-target-kdl/src/lib.rs index f9b4020a..2f7afe97 100644 --- a/crates/kind-target-kdl/src/lib.rs +++ b/crates/kind-target-kdl/src/lib.rs @@ -1,241 +1,17 @@ -use fxhash::FxHashMap; -use kind_tree::desugared as kind; -pub use kindelia_lang::ast as kdl; -use linked_hash_map::LinkedHashMap; +use std::sync::mpsc::Sender; -pub struct File { - funs: LinkedHashMap, - runs: Vec, -} +use flatten::flatten; +use kind_report::data::Diagnostic; +use kind_tree::{untyped}; -pub struct CompileCtx<'a> { - file: File, - kdl_names: FxHashMap, - kdl_states: Vec, - book: &'a kind::Book, -} +pub use compile::File; -pub fn compile_book(book: &kind::Book) -> File { - let mut ctx = CompileCtx { - file: File { - funs: Default::default(), - runs: Default::default(), - }, - kdl_names: Default::default(), - kdl_states: Default::default(), - book, - }; - for (_name, entry) in &book.entrs { - compile_entry(&mut ctx, entry); - } - ctx.file -} +mod compile; +mod flatten; +mod subst; +mod errors; -pub fn compile_entry(ctx: &mut CompileCtx, entry: &kind::Entry) { - let is_erased = entry - .attrs - .iter() - .find(|x| matches!(x, kind::Attribute::KdlErase)) - .is_some(); - if is_erased { - // Don't compile - return; - } - let is_run = entry - .attrs - .iter() - .find(|x| matches!(x, kind::Attribute::KdlRun)) - .is_some(); - if is_run { - // Compile as Run - if entry.args.len() != 0 { - todo!(); // run has args - } else if entry.rules.len() != 1 { - todo!(); // run doesn't have exactly 1 rule - } else { - let expr = compile_expr(ctx, &entry.rules[0].body); - let statement = kdl::Statement::Run { expr, sign: None }; - ctx.file.runs.push(statement); - } - } else { - // Shared between Ctr and Fun - let name = ctx.kdl_names.get(&entry.name.to_string()).unwrap().clone(); - let mut args = Vec::new(); - for arg in &entry.args { - let name = arg.name.to_str(); - if let Ok(name) = kdl::Name::from_str(name) { - args.push(name); - } else { - todo!(); // arg name not valid kdl name - } - } - if entry.rules.len() == 0 { - // Compile as Ctr - let stmt = kdl::Statement::Ctr { - name, - args, - sign: None, - }; - ctx.file.funs.insert(entry.name.to_string(), stmt); - } else { - // Compile as Fun - let mut rules = Vec::new(); - for rule in &entry.rules { - rules.push(compile_rule(ctx, rule)); - } - let func = kdl::Func { rules }; - let attr = entry - .attrs - .iter() - .find(|x| matches!(x, kind::Attribute::KdlState(_))); - let init = if let Some(kind::Attribute::KdlState(init_name)) = attr { - let init_entry = ctx.book.entrs.get(init_name.to_str()); - if let Some(init_entry) = init_entry { - // Has some initial state - if init_entry.args.len() != 0 { - todo!(); // state has args - } else if init_entry.rules.len() != 1 { - todo!(); // state doesn't have exactly 1 rule - } else { - ctx.kdl_states.push(init_name.to_string()); - let init = compile_expr(ctx, &init_entry.rules[0].body); - Some(init) - } - } else { - todo!(); // Init state not defined - } - } else { - todo!(); // Has no initial state - }; - let stmt = kdl::Statement::Fun { - name, - args, - func, - init, - sign: None, - }; - ctx.file.funs.insert(entry.name.to_string(), stmt); - } - } -} - -pub fn compile_rule(ctx: &mut CompileCtx, rule: &kind::Rule) -> kdl::Rule { - let name = ctx.kdl_names.get(rule.name.to_str()).unwrap().clone(); - let mut args = Vec::new(); - for pat in &rule.pats { - let arg = compile_expr(ctx, pat); - args.push(arg); - } - let lhs = kdl::Term::fun(name, args); - let rhs = compile_expr(ctx, &rule.body); - let rule = kdl::Rule { lhs, rhs }; - rule -} - -pub fn compile_expr(ctx: &mut CompileCtx, expr: &kind::Expr) -> kdl::Term { - use kdl::Term as T; - use kind::ExprKind as E; - match &expr.data { - E::App(head, spine) => { - let mut expr = compile_expr(ctx, head); - for binding in spine { - let body = compile_expr(ctx, &binding.data); - expr = T::App { - func: Box::new(expr), - argm: Box::new(body), - }; - } - expr - } - E::Binary(op, x0, x1) => { - // TODO: Special compilation for U60 ops - let oper = compile_oper(op); - let val0 = Box::new(compile_expr(ctx, x0)); - let val1 = Box::new(compile_expr(ctx, x1)); - T::Op2 { oper, val0, val1 } - } - E::Ctr(name, spine) => { - let name = ctx.kdl_names.get(name.to_str()).unwrap().clone(); - let mut args = Vec::new(); - for arg in spine { - args.push(compile_expr(ctx, &arg)); - } - T::Ctr { name, args } - } - E::Fun(name, spine) => { - // TODO: Special compilation for U60 and U120 ops - let name = ctx.kdl_names.get(name.to_str()).unwrap().clone(); - let mut args = Vec::new(); - for arg in spine { - args.push(compile_expr(ctx, &arg)); - } - T::Fun { name, args } - } - E::Lambda(name, body, _) => { - let name = kdl::Name::from_str(name.to_str()); - if let Ok(name) = name { - let body = Box::new(compile_expr(ctx, &body)); - T::Lam { name, body } - } else { - todo!(); // var name not valid - } - } - E::Let(name, expr, body) => { - let name = kdl::Name::from_str(name.to_str()); - if let Ok(name) = name { - let expr = Box::new(compile_expr(ctx, &expr)); - let func = Box::new(T::Lam { name, body: expr }); - let argm = Box::new(compile_expr(ctx, body)); - T::App { func, argm } - } else { - todo!(); // var name not valid - } - } - E::Num(kind::Number::U60(numb)) => T::Num { - numb: kdl::U120(*numb as u128), - }, - E::Num(kind::Number::U120(numb)) => T::Num { - numb: kdl::U120(*numb), - }, - E::Var(name) => { - let name = kdl::Name::from_str(name.to_str()); - if let Ok(name) = name { - T::Var { name } - } else { - todo!(); // var name not valid - } - } - E::All(..) => unreachable!(), - E::Ann(..) => unreachable!(), - E::Hlp(..) => unreachable!(), - E::Hole(..) => unreachable!(), - E::NumType(..) => unreachable!(), - E::Str(..) => unreachable!(), - E::Sub(..) => unreachable!(), - E::Typ => unreachable!(), - E::Err => unreachable!(), - } -} - -fn compile_oper(oper: &kind::Operator) -> kdl::Oper { - use kdl::Oper as T; - use kind::Operator as F; - 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, - } +pub fn compile_book(book: untyped::Book, sender: Sender>, namespace: &str) -> Option { + let flattened = flatten(book); + compile::compile_book(&flattened, sender, namespace) } diff --git a/crates/kind-target-kdl/src/subst.rs b/crates/kind-target-kdl/src/subst.rs new file mode 100644 index 00000000..4b2969a8 --- /dev/null +++ b/crates/kind-target-kdl/src/subst.rs @@ -0,0 +1,42 @@ +use kind_tree::{untyped::Expr, symbol::Ident}; + +pub fn subst(term: &mut Expr, from: &Ident, to: &Expr) { + use kind_tree::untyped::ExprKind::*; + match &mut term.data { + Var { name } if from.to_str() == name.to_str() => *term = to.clone(), + + App { fun, args } => { + subst(fun, from, to); + for arg in args { + subst(arg, from, to); + } + } + + Fun { args, .. } | Ctr { args, .. } => { + for arg in args { + subst(arg, from, to); + } + } + + Let { name, val, next } => { + subst(val, from, to); + if name.to_str() != from.to_str() { + subst(next, from, to); + } + } + + Binary { op: _, left, right } => { + subst(left, from, to); + subst(right, from, to); + } + + Lambda { param, body, .. } if param.to_str() != from.to_str() => subst(body, from, to), + + Num { .. } => (), + Str { .. } => (), + Var { .. } => (), + Lambda { .. } => (), + + Err => unreachable!("Err should not be used inside the compiledr"), + } +} diff --git a/crates/kind-tree/src/concrete/mod.rs b/crates/kind-tree/src/concrete/mod.rs index e9a5cbce..f0c5f362 100644 --- a/crates/kind-tree/src/concrete/mod.rs +++ b/crates/kind-tree/src/concrete/mod.rs @@ -120,6 +120,7 @@ pub struct Entry { pub rules: Vec>, pub range: Range, pub attrs: Vec, + pub generated_by: Option, } /// A single cosntructor inside the algebraic data @@ -128,6 +129,7 @@ pub struct Entry { pub struct Constructor { pub name: Ident, pub docs: Vec, + pub attrs: Vec, pub args: Telescope, pub typ: Option>, } @@ -153,6 +155,7 @@ pub struct RecordDecl { pub parameters: Telescope, pub fields: Vec<(Ident, Vec, Box)>, pub attrs: Vec, + pub cons_attrs: Vec, } /// All of the structures @@ -305,7 +308,10 @@ impl Display for Module { impl Display for Book { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { for entr in self.entries.values() { - write!(f, "{}", entr)?; + match entr { + TopLevel::Entry(entr) if entr.generated_by.is_some() => (), + _ => write!(f, "{}", entr)?, + } } Ok(()) } diff --git a/crates/kind-tree/src/desugared/mod.rs b/crates/kind-tree/src/desugared/mod.rs index d28d5b99..a5902986 100644 --- a/crates/kind-tree/src/desugared/mod.rs +++ b/crates/kind-tree/src/desugared/mod.rs @@ -4,10 +4,13 @@ use std::fmt::{Display, Error, Formatter}; use fxhash::FxHashMap; -use kind_span::{Range, Span}; +use kind_span::Range; use linked_hash_map::LinkedHashMap; -use crate::symbol::{Ident, QualifiedIdent}; +use crate::{ + symbol::{Ident, QualifiedIdent}, + Attributes, +}; pub use crate::{NumType, Number, Operator}; /// Just a vector of expressions. It is called spine because @@ -24,35 +27,60 @@ pub struct AppBinding { #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub enum ExprKind { /// Name of a variable - Var(Ident), + Var { name: Ident }, /// The dependent function space (e.g. (x : Int) -> y) - All(Ident, Box, Box, bool), + All { + param: Ident, + typ: Box, + body: Box, + erased: bool, + }, /// A anonymous function that receives one argument - Lambda(Ident, Box, bool), + Lambda { + param: Ident, + body: Box, + erased: bool, + }, /// Application of a expression to a spine of expressions - App(Box, Vec), + App { + fun: Box, + args: Vec, + }, /// Application of a function - Fun(QualifiedIdent, Spine), + Fun { name: QualifiedIdent, args: Spine }, /// Application of a Construtor - Ctr(QualifiedIdent, Spine), + Ctr { name: QualifiedIdent, args: Spine }, /// Declaration of a local variable - Let(Ident, Box, Box), + Let { + name: Ident, + val: Box, + next: Box, + }, /// Type ascription (x : y) - Ann(Box, Box), + Ann { expr: Box, typ: Box }, /// Substitution - Sub(Ident, usize, usize, Box), + Sub { + name: Ident, + indx: usize, + redx: usize, + expr: Box, + }, /// Type Literal Typ, /// Primitive numeric types - NumType(crate::NumType), + NumType { typ: crate::NumType }, /// Primitive numeric values - Num(crate::Number), + Num { num: crate::Number }, /// Very special constructor :) - Str(String), + Str { val: String }, /// Binary operation (e.g. 2 + 3) - Binary(Operator, Box, Box), + Binary { + op: Operator, + left: Box, + right: Box, + }, /// A expression open to unification (e.g. _) - Hole(u64), + Hole { num: u64 }, /// Help Hlp(Ident), /// Error node (It's useful as a sentinel value @@ -64,55 +92,66 @@ pub enum ExprKind { #[derive(Clone, Debug, Hash, PartialEq, Eq)] pub struct Expr { pub data: ExprKind, - pub span: Span, + pub range: Range, } impl Expr { - pub fn generate_expr(data: ExprKind) -> Box { + pub fn var(name: Ident) -> Box { Box::new(Expr { - data, - span: Span::Generated, - }) - } - - pub fn var(ident: Ident) -> Box { - Box::new(Expr { - span: Span::Locatable(ident.range), - data: ExprKind::Var(ident), + range: name.range, + data: ExprKind::Var { name }, }) } pub fn all( range: Range, - ident: Ident, + param: Ident, typ: Box, body: Box, erased: bool, ) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::All(ident, typ, body, erased), + range, + data: ExprKind::All { + param, + typ, + body, + erased, + }, }) } - pub fn sub(range: Range, ident: Ident, idx: usize, rdx: usize, body: Box) -> Box { + pub fn sub(range: Range, name: Ident, indx: usize, redx: usize, expr: Box) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Sub(ident, idx, rdx, body), + range, + data: ExprKind::Sub { + name, + indx, + redx, + expr, + }, }) } - pub fn lambda(range: Range, ident: Ident, body: Box, erased: bool) -> Box { + pub fn lambda(range: Range, param: Ident, body: Box, erased: bool) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Lambda(ident, body, erased), + range, + data: ExprKind::Lambda { + param, + body, + erased, + }, }) } pub fn identity_lambda(ident: Ident) -> Box { Box::new(Expr { - span: Span::Generated, - data: ExprKind::Lambda(ident.clone(), Self::var(ident), false), + range: ident.range, + data: ExprKind::Lambda { + param: ident.clone(), + body: Self::var(ident), + erased: false, + }, }) } @@ -126,100 +165,108 @@ impl Expr { }) } - pub fn app(range: Range, ident: Box, spine: Vec) -> Box { + pub fn app(range: Range, fun: Box, args: Vec) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::App(ident, spine), + range, + data: ExprKind::App { fun, args }, }) } - pub fn fun(range: Range, head: QualifiedIdent, spine: Vec>) -> Box { + pub fn fun(range: Range, name: QualifiedIdent, args: Vec>) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Fun(head, spine), + range: range.into(), + data: ExprKind::Fun { name, args }, }) } - pub fn ctr(range: Range, head: QualifiedIdent, spine: Vec>) -> Box { + pub fn ctr(range: Range, name: QualifiedIdent, args: Vec>) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Ctr(head, spine), + range: range.into(), + data: ExprKind::Ctr { name, args }, }) } - pub fn let_(range: Range, ident: Ident, val: Box, body: Box) -> Box { + pub fn let_(range: Range, name: Ident, val: Box, next: Box) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Let(ident, val, body), + range, + data: ExprKind::Let { name, val, next }, }) } - pub fn ann(range: Range, val: Box, typ: Box) -> Box { + pub fn ann(range: Range, expr: Box, typ: Box) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Ann(val, typ), + range, + data: ExprKind::Ann { expr, typ }, }) } pub fn typ(range: Range) -> Box { Box::new(Expr { - span: Span::Locatable(range), + range, data: ExprKind::Typ, }) } pub fn u60(range: Range) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::NumType(crate::NumType::U60), + range, + data: ExprKind::NumType { + typ: crate::NumType::U60, + }, }) } pub fn u120(range: Range) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::NumType(crate::NumType::U120), + range, + data: ExprKind::NumType { + typ: crate::NumType::U120, + }, }) } pub fn num60(range: Range, num: u64) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Num(crate::Number::U60(num)), + range, + data: ExprKind::Num { + num: crate::Number::U60(num), + }, }) } pub fn num120(range: Range, num: u128) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Num(crate::Number::U120(num)), + range, + data: ExprKind::Num { + num: crate::Number::U120(num), + }, }) } pub fn binary(range: Range, op: Operator, left: Box, right: Box) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Binary(op, left, right), + range, + data: ExprKind::Binary { op, left, right }, }) } pub fn hole(range: Range, num: u64) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Hole(num), + range, + data: ExprKind::Hole { num }, }) } - pub fn str(range: Range, str: String) -> Box { + pub fn str(range: Range, val: String) -> Box { Box::new(Expr { - span: Span::Locatable(range), - data: ExprKind::Str(str), + range, + data: ExprKind::Str { val }, }) } pub fn hlp(range: Range, hlp: Ident) -> Box { Box::new(Expr { - span: Span::Locatable(range), + range, data: ExprKind::Hlp(hlp), }) } @@ -227,7 +274,7 @@ impl Expr { pub fn err(range: Range) -> Box { Box::new(Expr { data: ExprKind::Err, - span: Span::Locatable(range), + range, }) } } @@ -244,7 +291,7 @@ pub struct Argument { pub erased: bool, pub name: Ident, pub typ: Box, - pub span: Range, + pub range: Range, } /// A rule is a equation that in the left-hand-side @@ -255,18 +302,7 @@ pub struct Rule { pub name: QualifiedIdent, pub pats: Vec>, pub body: Box, - pub span: Span, -} - -/// Attributes describes some compiler specific aspects -/// like inlining and derivations. -#[derive(Clone, Debug)] -pub enum Attribute { - Inline, - KdlRun, - KdlErase, - KdlName(Ident), - KdlState(Ident), + pub range: Range, } /// An entry describes a function that is typed @@ -279,8 +315,8 @@ pub struct Entry { pub args: Vec, pub typ: Box, pub rules: Vec, - pub attrs: Vec, - pub span: Span, + pub attrs: Attributes, + pub range: Range, } /// A book is a collection of desugared entries. @@ -294,20 +330,25 @@ pub struct Book { impl Expr { pub fn new_var(name: Ident) -> Expr { Expr { - span: Span::Generated, - data: ExprKind::Var(name), + range: name.range, + data: ExprKind::Var { name }, } } pub fn traverse_pi_types(&self) -> String { match &self.data { - ExprKind::All(binder, typ, body, erased) => { + ExprKind::All { + param, + typ, + body, + erased, + } => { let tilde = if *erased { "~" } else { "" }; - if binder.to_string().starts_with('_') { + if param.to_string().starts_with('_') { format!("{}{} -> {}", tilde, typ, body.traverse_pi_types()) } else { let body = body.traverse_pi_types(); - format!("({}{} : {}) -> {}", tilde, binder, typ, body) + format!("({}{} : {}) -> {}", tilde, param, typ, body) } } _ => format!("{}", self), @@ -330,38 +371,56 @@ impl Display for Expr { use ExprKind::*; match &self.data { Typ => write!(f, "Type"), - NumType(crate::NumType::U60) => write!(f, "U60"), - NumType(crate::NumType::U120) => write!(f, "U120"), - Str(n) => write!(f, "\"{}\"", n), - Num(crate::Number::U60(n)) => write!(f, "{}", n), - Num(crate::Number::U120(n)) => write!(f, "{}u120", n), - All(_, _, _, _) => write!(f, "({})", self.traverse_pi_types()), - Var(name) => write!(f, "{}", name), - Lambda(binder, body, false) => write!(f, "({} => {})", binder, body), - Lambda(binder, body, true) => write!(f, "(~{} => {})", binder, body), - Sub(name, _, redx, expr) => write!(f, "(## {}/{} {})", name, redx, expr), - App(head, spine) => write!( + NumType { + typ: crate::NumType::U60, + } => write!(f, "U60"), + NumType { + typ: crate::NumType::U120, + } => write!(f, "U120"), + Str { val } => write!(f, "\"{}\"", val), + Num { + num: crate::Number::U60(n), + } => write!(f, "{}", n), + Num { + num: crate::Number::U120(n), + } => write!(f, "{}u120", n), + All { .. } => write!(f, "({})", self.traverse_pi_types()), + Var { name } => write!(f, "{}", name), + Lambda { + param, + body, + erased: false, + } => write!(f, "({} => {})", param, body), + Lambda { + param, + body, + erased: true, + } => write!(f, "(~{} => {})", param, body), + Sub { + name, redx, expr, .. + } => write!(f, "(## {}/{} {})", name, redx, expr), + App { fun, args } => write!( f, "({}{})", - head, - spine.iter().map(|x| format!(" {}", x)).collect::() + fun, + args.iter().map(|x| format!(" {}", x)).collect::() ), - Fun(head, spine) | Ctr(head, spine) => { - if spine.is_empty() { - write!(f, "{}", head) + Fun { name, args } | Ctr { name, args } => { + if args.is_empty() { + write!(f, "{}", name) } else { write!( f, "({}{})", - head, - spine.iter().map(|x| format!(" {}", x)).collect::() + name, + args.iter().map(|x| format!(" {}", x)).collect::() ) } } - Let(name, expr, body) => write!(f, "(let {} = {}; {})", name, expr, body), - Ann(expr, typ) => write!(f, "({} :: {})", expr, typ), - Binary(op, expr, typ) => write!(f, "({} {} {})", op, expr, typ), - Hole(_) => write!(f, "_"), + Let { name, val, next } => write!(f, "(let {} = {}; {})", name, val, next), + Ann { expr, typ } => write!(f, "({} :: {})", expr, typ), + Binary { op, left, right } => write!(f, "({} {} {})", op, left, right), + Hole { .. } => write!(f, "_"), Hlp(name) => write!(f, "?{}", name), Err => write!(f, "ERR"), } @@ -424,7 +483,7 @@ impl Argument { erased: true, name: self.name.clone(), typ: self.typ.clone(), - span: self.span, + range: self.range, } } @@ -434,7 +493,7 @@ impl Argument { erased: false, name: name.clone(), typ, - span: range, + range, } } } diff --git a/crates/kind-tree/src/lib.rs b/crates/kind-tree/src/lib.rs index cdee07f6..66f4ab9b 100644 --- a/crates/kind-tree/src/lib.rs +++ b/crates/kind-tree/src/lib.rs @@ -11,12 +11,27 @@ pub mod concrete; /// The desugared AST. pub mod desugared; +/// The untyped AST. +pub mod untyped; + /// Describes symbols (identifiers) on the language. It will /// be really useful when we change the Symbol to take a number /// instead of a string due to optimizations. pub mod symbol; pub use hvm::syntax as backend; +use symbol::Ident; + +/// Attributes describes some compiler specific aspects +/// like inlining and derivations. +#[derive(Clone, Debug, Default)] +pub struct Attributes { + pub inlined: bool, + pub kdl_run: bool, + pub kdl_erase: bool, + pub kdl_name: Option, + pub kdl_state: Option, +} /// Enum of binary operators. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] diff --git a/crates/kind-tree/src/symbol.rs b/crates/kind-tree/src/symbol.rs index 886121db..6cda0214 100644 --- a/crates/kind-tree/src/symbol.rs +++ b/crates/kind-tree/src/symbol.rs @@ -51,7 +51,7 @@ pub struct Ident { pub struct QualifiedIdent { root: Symbol, aux: Option, - + pub range: Range, /// Flag that is useful to avoid unbound errors while @@ -182,6 +182,15 @@ impl Ident { old } + pub fn to_qualified_ident(&self) -> QualifiedIdent { + QualifiedIdent { + root: self.data.clone(), + aux: None, + range: self.range, + generated: false, + } + } + pub fn decode(num: u64) -> String { let mut num = num; let mut name = String::new(); @@ -245,7 +254,7 @@ impl Ident { pub fn generate(data: &str) -> Ident { Ident { - data: Symbol::new(data.to_string()), + data: Symbol::new(data.to_owned()), range: Range::ghost_range(), generated: true, } diff --git a/crates/kind-tree/src/untyped/mod.rs b/crates/kind-tree/src/untyped/mod.rs new file mode 100644 index 00000000..8aec6d48 --- /dev/null +++ b/crates/kind-tree/src/untyped/mod.rs @@ -0,0 +1,331 @@ +//! This module describes an unsugared and untyped tree +//! that is a IR + +use std::fmt::{Display, Error, Formatter}; + +use fxhash::FxHashMap; +use kind_span::Range; +use linked_hash_map::LinkedHashMap; + +use crate::{ + symbol::{Ident, QualifiedIdent}, + Attributes, +}; +pub use crate::{NumType, Number, Operator}; + +/// Just a vector of expressions. It is called spine because +/// it is usually in a form like (a b c d e) that can be interpret +/// as ((((a b) c) d) e) that looks like a spine. +pub type Spine = Vec>; + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub enum ExprKind { + /// Name of a variable + Var { + name: Ident, + }, + /// A anonymous function that receives one argument + Lambda { + param: Ident, + body: Box, + erased: bool, + }, + /// Application of a expression to a spine of expressions + App { + fun: Box, + args: Vec>, + }, + /// Application of a function + Fun { + name: QualifiedIdent, + args: Spine, + }, + /// Application of a Construtor + Ctr { + name: QualifiedIdent, + args: Spine, + }, + /// Declaration of a local variable + Let { + name: Ident, + val: Box, + next: Box, + }, + /// Primitive numeric values + Num { + num: crate::Number, + }, + /// Very special constructor :) + Str { + val: String, + }, + /// Binary operation (e.g. 2 + 3) + Binary { + op: Operator, + left: Box, + right: Box, + }, + + Err, +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct Expr { + pub data: ExprKind, + pub range: Range, +} + +impl Expr { + pub fn var(name: Ident) -> Box { + Box::new(Expr { + range: name.range, + data: ExprKind::Var { name }, + }) + } + + pub fn str(range: Range, val: String) -> Box { + Box::new(Expr { + range, + data: ExprKind::Str { val }, + }) + } + + pub fn lambda(range: Range, param: Ident, body: Box, erased: bool) -> Box { + Box::new(Expr { + range, + data: ExprKind::Lambda { + param, + body, + erased, + }, + }) + } + + pub fn fun(range: Range, name: QualifiedIdent, args: Vec>) -> Box { + Box::new(Expr { + range: range.into(), + data: ExprKind::Fun { name, args }, + }) + } + + pub fn app(range: Range, fun: Box, args: Vec>) -> Box { + Box::new(Expr { + range: range.into(), + data: ExprKind::App { fun, args }, + }) + } + + pub fn ctr(range: Range, name: QualifiedIdent, args: Vec>) -> Box { + Box::new(Expr { + range: range.into(), + data: ExprKind::Ctr { name, args }, + }) + } + + pub fn let_(range: Range, name: Ident, val: Box, next: Box) -> Box { + Box::new(Expr { + range, + data: ExprKind::Let { name, val, next }, + }) + } + + pub fn num60(range: Range, num: u64) -> Box { + Box::new(Expr { + range, + data: ExprKind::Num { + num: crate::Number::U60(num), + }, + }) + } + + pub fn num120(range: Range, num: u128) -> Box { + Box::new(Expr { + range, + data: ExprKind::Num { + num: crate::Number::U120(num), + }, + }) + } + + pub fn binary(range: Range, op: Operator, left: Box, right: Box) -> Box { + Box::new(Expr { + range, + data: ExprKind::Binary { op, left, right }, + }) + } + + pub fn err(range: Range) -> Box { + Box::new(Expr { + range, + data: ExprKind::Err, + }) + } +} + +/// An argument is a 'binding' of a name to a type +/// it has some other options like +/// eras: that express the erasure of this type when +/// compiled. +/// hide: that express a implicit argument (that will +/// be discovered through unification). +#[derive(Clone, Debug)] +pub struct Argument { + pub hidden: bool, + pub erased: bool, + pub name: Ident, + pub typ: Box, + pub range: Range, +} + +/// A rule is a equation that in the left-hand-side +/// contains a list of patterns @pats@ and on the +/// right hand side a value. +#[derive(Clone, Debug)] +pub struct Rule { + pub name: QualifiedIdent, + pub pats: Vec>, + pub body: Box, + pub range: Range, +} + +/// An entry describes a function that is typed +/// and has rules. The type of the function +/// consists of the arguments @args@ and the +/// return type @typ@. +#[derive(Clone, Debug)] +pub struct Entry { + pub name: QualifiedIdent, + pub args: Vec<(String, Range, bool)>, + pub rules: Vec, + pub attrs: Attributes, + pub range: Range, +} + +/// A book is a collection of desugared entries. +#[derive(Clone, Debug, Default)] +pub struct Book { + pub entrs: LinkedHashMap>, + pub names: FxHashMap, + pub holes: u64, +} + +impl Expr { + pub fn new_var(name: Ident) -> Expr { + Expr { + range: name.range, + data: ExprKind::Var { name }, + } + } +} + +impl Display for Expr { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + use ExprKind::*; + match &self.data { + Err => write!(f, "ERR"), + Str { val } => write!(f, "\"{}\"", val), + Num { + num: crate::Number::U60(n), + } => write!(f, "{}", n), + Num { + num: crate::Number::U120(n), + } => write!(f, "{}u120", n), + Var { name } => write!(f, "{}", name), + Lambda { + param, + body, + erased: false, + } => write!(f, "({} => {})", param, body), + Lambda { + param, + body, + erased: true, + } => write!(f, "(~{} => {})", param, body), + App { fun, args } => write!( + f, + "({}{})", + fun, + args.iter().map(|x| format!(" {}", x)).collect::() + ), + Fun { name, args } | Ctr { name, args } => { + if args.is_empty() { + write!(f, "{}", name) + } else { + write!( + f, + "({}{})", + name, + args.iter().map(|x| format!(" {}", x)).collect::() + ) + } + } + Let { name, val, next } => write!(f, "(let {} = {}; {})", name, val, next), + Binary { op, left, right } => write!(f, "({} {} {})", op, left, right), + } + } +} + +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)?; + } + } + Ok(()) + } +} + +impl Display for Argument { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + let (open, close) = match (self.erased, self.hidden) { + (false, false) => ("(", ")"), + (false, true) => ("+<", ">"), + (true, false) => ("-(", ")"), + (true, true) => ("<", ">"), + }; + write!(f, "{}{}: {}{}", open, self.name, self.typ, close) + } +} + +impl Display for Entry { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + for rule in &self.rules { + write!(f, "\n{}", rule)? + } + + Ok(()) + } +} + +impl Display for Rule { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + write!(f, "{}", self.name)?; + for pat in &self.pats { + write!(f, " {}", pat)?; + } + write!(f, " = {}", self.body) + } +} + +impl Argument { + pub fn to_irrelevant(&self) -> Argument { + Argument { + hidden: true, + erased: true, + name: self.name.clone(), + typ: self.typ.clone(), + range: self.range, + } + } + + pub fn from_field(name: &Ident, typ: Box, range: Range) -> Argument { + Argument { + hidden: false, + erased: false, + name: name.clone(), + typ, + range, + } + } +}