From 4357ec00787d572fe2835998680b2b6d5f62c15c Mon Sep 17 00:00:00 2001 From: felipegchi Date: Mon, 28 Nov 2022 14:24:28 -0300 Subject: [PATCH] feat: simple inline --- crates/kind-cli/src/lib.rs | 1 - crates/kind-derive/Cargo.toml | 3 +- crates/kind-derive/src/subst.rs | 269 +++++++++++++++++++++++++- crates/kind-driver/src/lib.rs | 17 +- crates/kind-pass/src/inline/mod.rs | 96 +++++++++ crates/kind-pass/src/lib.rs | 1 + crates/kind-pass/src/unbound/mod.rs | 2 + crates/kind-pass/src/unbound/subst.rs | 101 ++++++++++ crates/kind-target-kdl/src/lib.rs | 3 - 9 files changed, 476 insertions(+), 17 deletions(-) create mode 100644 crates/kind-pass/src/inline/mod.rs create mode 100644 crates/kind-pass/src/unbound/subst.rs diff --git a/crates/kind-cli/src/lib.rs b/crates/kind-cli/src/lib.rs index 0fc47168..8eb146ca 100644 --- a/crates/kind-cli/src/lib.rs +++ b/crates/kind-cli/src/lib.rs @@ -149,7 +149,6 @@ pub fn compile_in_session( Log::Checked(start.elapsed()) }, ); - eprintln!(); res } else { render_to_stderr(&render_config, &session, &Log::Failed(start.elapsed())); diff --git a/crates/kind-derive/Cargo.toml b/crates/kind-derive/Cargo.toml index cd44e417..6757c1eb 100644 --- a/crates/kind-derive/Cargo.toml +++ b/crates/kind-derive/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" kind-span = { path = "../kind-span" } kind-tree = { path = "../kind-tree" } kind-report = { path = "../kind-report" } -fxhash = "0.2.1" \ No newline at end of file +fxhash = "0.2.1" +im = "*" \ No newline at end of file diff --git a/crates/kind-derive/src/subst.rs b/crates/kind-derive/src/subst.rs index 5627282b..62093be4 100644 --- a/crates/kind-derive/src/subst.rs +++ b/crates/kind-derive/src/subst.rs @@ -1,22 +1,275 @@ use fxhash::FxHashMap; -use kind_tree::{ - concrete::{expr::Expr, visitor::Visitor}, - symbol::Symbol, -}; + +use kind_span::Range; + +use kind_tree::concrete::expr::{Binding, Case, CaseBinding, Destruct, Expr, ExprKind, SttmKind}; +use kind_tree::concrete::pat::{Pat, PatIdent, PatKind}; +use kind_tree::concrete::visitor::Visitor; +use kind_tree::symbol::{Ident, QualifiedIdent, Symbol}; +use kind_tree::visit_vec; pub struct Subst<'a> { + pub context_vars: Vec<(Range, String)>, pub names: &'a FxHashMap, } impl<'a> Visitor for Subst<'a> { - fn visit_ident(&mut self, ident: &mut kind_tree::symbol::Ident) { - if let Some(res) = self.names.get(ident.to_str()) { - ident.data = Symbol::new(res.clone()); + fn visit_attr(&mut self, _: &mut kind_tree::concrete::Attribute) {} + + fn visit_ident(&mut self, ident: &mut Ident) { + let name = ident.to_str(); + if self.context_vars.iter().all(|x| x.1 != name) { + if let Some(res) = self.names.get(name) { + ident.data = Symbol::new(res.clone()) + } + } + } + + fn visit_pat_ident(&mut self, ident: &mut PatIdent) { + self.visit_ident(&mut ident.0) + } + + fn visit_destruct(&mut self, destruct: &mut Destruct) { + match destruct { + Destruct::Destruct(range, ty, bindings, _) => { + self.visit_qualified_ident( + &mut QualifiedIdent::add_segment(ty, "open").to_generated(), + ); + self.visit_range(range); + self.visit_qualified_ident(ty); + for bind in bindings { + self.visit_case_binding(bind) + } + } + Destruct::Ident(ident) => self.context_vars.push((ident.range, ident.to_string())), + } + } + + fn visit_sttm(&mut self, sttm: &mut kind_tree::concrete::expr::Sttm) { + match &mut sttm.data { + SttmKind::Ask(ident, val, next) => { + self.visit_expr(val); + let vars = self.context_vars.clone(); + self.visit_destruct(ident); + self.visit_sttm(next); + self.context_vars = vars; + } + SttmKind::Let(ident, val, next) => { + self.visit_expr(val); + let vars = self.context_vars.clone(); + self.visit_destruct(ident); + self.visit_sttm(next); + self.context_vars = vars; + } + SttmKind::Expr(expr, next) => { + self.visit_expr(expr); + self.visit_sttm(next); + } + SttmKind::Return(expr) => { + self.visit_expr(expr); + } + SttmKind::RetExpr(expr) => { + self.visit_expr(expr); + } + } + } + + fn visit_pat(&mut self, pat: &mut Pat) { + match &mut pat.data { + PatKind::Var(ident) => self.visit_pat_ident(ident), + PatKind::Str(_) => (), + PatKind::Num(_) => (), + PatKind::Hole => (), + PatKind::List(ls) => { + for pat in ls { + self.visit_pat(pat) + } + } + PatKind::Pair(fst, snd) => { + self.visit_pat(fst); + self.visit_pat(snd); + } + PatKind::App(t, ls) => { + self.visit_qualified_ident(t); + for pat in ls { + self.visit_pat(pat) + } + } + } + } + + fn visit_case_binding(&mut self, case_binding: &mut CaseBinding) { + match case_binding { + CaseBinding::Field(ident) | CaseBinding::Renamed(_, ident) => { + self.context_vars.push((ident.range, ident.to_string())) + } + } + } + + fn visit_case(&mut self, case: &mut Case) { + let vars = self.context_vars.clone(); + for binding in &mut case.bindings { + self.visit_case_binding(binding); + } + self.visit_expr(&mut case.value); + self.context_vars = vars; + } + + fn visit_match(&mut self, matcher: &mut kind_tree::concrete::expr::Match) { + self.visit_expr(&mut matcher.scrutinizer); + for case in &mut matcher.cases { + self.visit_case(case); + } + match &mut matcher.motive { + Some(x) => self.visit_expr(x), + None => (), + } + } + + fn visit_binding(&mut self, binding: &mut Binding) { + match binding { + Binding::Positional(e) => self.visit_expr(e), + Binding::Named(_, _, e) => self.visit_expr(e), + } + } + + fn visit_expr(&mut self, expr: &mut Expr) { + match &mut expr.data { + ExprKind::Var { name } => self.visit_ident(name), + ExprKind::Constr { name, args } => { + self.visit_qualified_ident(name); + visit_vec!(args.iter_mut(), arg => self.visit_binding(arg)); + } + ExprKind::All { + param: None, + typ, + body, + .. + } => { + self.visit_expr(typ); + self.visit_expr(body); + } + ExprKind::All { + param: Some(ident), + typ, + body, + .. + } => { + self.visit_expr(typ); + self.context_vars.push((ident.range, ident.to_string())); + self.visit_expr(body); + self.context_vars.pop(); + } + ExprKind::Lambda { + param, typ, body, .. + } => { + match typ { + Some(x) => self.visit_expr(x), + None => (), + } + self.context_vars.push((param.range, param.to_string())); + self.visit_expr(body); + self.context_vars.pop(); + } + ExprKind::App { fun, args } => { + self.visit_expr(fun); + visit_vec!(args.iter_mut(), arg => self.visit_expr(&mut arg.data)); + } + ExprKind::Ann { val, typ } => { + self.visit_expr(val); + self.visit_expr(typ); + } + ExprKind::Lit { lit } => self.visit_literal(lit), + ExprKind::Binary { op: _, fst, snd } => { + self.visit_expr(fst); + self.visit_expr(snd); + } + ExprKind::Let { name, val, next } => { + self.visit_expr(val); + let vars = self.context_vars.clone(); + self.visit_destruct(name); + self.visit_expr(next); + self.context_vars = vars; + } + ExprKind::Sigma { + param: None, + fst, + snd, + } => { + self.visit_qualified_ident(&mut QualifiedIdent::new_static( + "Sigma", None, expr.range, + )); + self.visit_expr(fst); + self.visit_expr(snd); + } + ExprKind::Sigma { + param: Some(ident), + fst, + snd, + } => { + self.visit_qualified_ident(&mut QualifiedIdent::new_static( + "Sigma", None, expr.range, + )); + self.visit_expr(fst); + self.context_vars.push((ident.range, ident.to_string())); + self.visit_expr(snd); + self.context_vars.pop(); + } + ExprKind::Match(matcher) => { + self.visit_qualified_ident(&mut matcher.typ.add_segment("match")); + self.visit_match(matcher) + } + ExprKind::Subst(subst) => { + self.visit_ident(&mut subst.name); + + if let Some(pos) = self + .context_vars + .iter() + .position(|x| x.1 == subst.name.to_string()) + { + subst.indx = pos; + } + + self.visit_expr(&mut subst.expr) + } + ExprKind::Hole => {} + ExprKind::Do { typ, sttm } => { + 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_ } => { + self.visit_qualified_ident(&mut QualifiedIdent::new_sugared( + "Bool", "if", expr.range, + )); + self.visit_expr(cond); + self.visit_expr(then_); + self.visit_expr(else_); + } + ExprKind::Pair { fst, snd } => { + self.visit_qualified_ident(&mut QualifiedIdent::new_sugared( + "Pair", "new", expr.range, + )); + self.visit_expr(fst); + self.visit_expr(snd); + } + ExprKind::List { args } => { + self.visit_qualified_ident(&mut QualifiedIdent::new_sugared( + "List", "nil", expr.range, + )); + self.visit_qualified_ident(&mut QualifiedIdent::new_sugared( + "List", "cons", expr.range, + )); + visit_vec!(args.iter_mut(), arg => self.visit_expr(arg)); + } } } } pub fn substitute_in_expr(expr: &mut Expr, names: &FxHashMap) { - let mut session = Subst { names }; + let mut session = Subst { + context_vars: Default::default(), + names, + }; session.visit_expr(expr) } diff --git a/crates/kind-driver/src/lib.rs b/crates/kind-driver/src/lib.rs index ac00b8dc..250a882c 100644 --- a/crates/kind-driver/src/lib.rs +++ b/crates/kind-driver/src/lib.rs @@ -1,6 +1,6 @@ use checker::eval; use errors::DriverError; -use kind_pass::{desugar, erasure, expand}; +use kind_pass::{desugar, erasure, expand, inline::inline_book}; use kind_report::report::FileCache; use kind_span::SyntaxCtxIndex; @@ -33,7 +33,10 @@ pub fn type_check_book(session: &mut Session, path: &PathBuf) -> Option Option { @@ -56,7 +59,10 @@ pub fn erase_book( ) -> Option { let concrete_book = to_book(session, path)?; let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?; - erasure::erase_book(&desugared_book, session.diagnostic_sender.clone()) + let mut book = erasure::erase_book(&desugared_book, session.diagnostic_sender.clone())?; + inline_book(&mut book); + Some(book) + } pub fn desugar_book(session: &mut Session, path: &PathBuf) -> Option { @@ -82,7 +88,10 @@ pub fn compile_book_to_kdl( ) -> Option { let concrete_book = to_book(session, path)?; let desugared_book = desugar::desugar_book(session.diagnostic_sender.clone(), &concrete_book)?; - let book = erasure::erase_book(&desugared_book, session.diagnostic_sender.clone())?; + let mut book = erasure::erase_book(&desugared_book, session.diagnostic_sender.clone())?; + + inline_book(&mut book); + kind_target_kdl::compile_book(book, session.diagnostic_sender.clone(), namespace) } diff --git a/crates/kind-pass/src/inline/mod.rs b/crates/kind-pass/src/inline/mod.rs new file mode 100644 index 00000000..2c5a7653 --- /dev/null +++ b/crates/kind-pass/src/inline/mod.rs @@ -0,0 +1,96 @@ +use fxhash::FxHashMap; +use kind_tree::untyped; + +use crate::unbound::subst::subst_on_expr; + +struct Inlinable { + names: Vec, + body: Box, +} +struct InlineState { + funs: FxHashMap +} + +fn inlinable(entry: &untyped::Entry) -> Option { + if entry.rules.len() == 1 { + let mut names = Vec::new(); + for pat in &entry.rules[0].pats { + match &pat.data { + untyped::ExprKind::Var { name } => { + names.push(name.to_string()) + }, + _ => return None + } + } + // TODO: Check if is recursive + Some(Inlinable { names, body: entry.rules[0].body.clone() }) + } else { + None + } +} + +pub fn inline_book(book: &mut untyped::Book) { + let mut funs = FxHashMap::default(); + + let mut to_remove = Vec::new(); + + for entr in book.entrs.values() { + if entr.attrs.inlined { + if let Some(inlinable) = inlinable(&entr) { + funs.insert(entr.name.to_string(), inlinable); + to_remove.push(entr.name.to_string()); + } + } + } + + for name in &to_remove { + book.entrs.remove(name); + } + + let mut state = InlineState { funs }; + + for entr in &mut book.entrs { + state.inline_entry(entr.1) + } +} + +impl InlineState { + fn inline_entry(&mut self, entry: &mut untyped::Entry) { + for rule in &mut entry.rules { + self.inline_expr(&mut rule.body) + } + } + + fn inline_expr(&mut self, expr: &mut Box) { + use untyped::ExprKind::*; + match &mut expr.data { + Lambda { body, .. } => self.inline_expr(body), + App { fun, args } => { + self.inline_expr(fun); + for arg in args { + self.inline_expr(arg); + } + } + Fun { name, args } | Ctr { name, args } => { + if let Some(inlinable) = self.funs.get(name.to_str()) { + let subst = FxHashMap::from_iter(inlinable.names.iter().cloned().zip(args.clone())); + *expr = inlinable.body.clone(); + subst_on_expr(expr, subst); + }else { + for arg in args { + self.inline_expr(arg); + } + } + } + Let { val, next, .. } => { + self.inline_expr(val); + self.inline_expr(next); + } + Binary { left, right, .. } => { + self.inline_expr(left); + self.inline_expr(right); + } + _ => (), + } + } +} diff --git a/crates/kind-pass/src/lib.rs b/crates/kind-pass/src/lib.rs index fd903051..3a0ae41e 100644 --- a/crates/kind-pass/src/lib.rs +++ b/crates/kind-pass/src/lib.rs @@ -9,3 +9,4 @@ pub mod erasure; mod errors; pub mod expand; pub mod unbound; +pub mod inline; diff --git a/crates/kind-pass/src/unbound/mod.rs b/crates/kind-pass/src/unbound/mod.rs index 4d3b0167..cb045b03 100644 --- a/crates/kind-pass/src/unbound/mod.rs +++ b/crates/kind-pass/src/unbound/mod.rs @@ -21,6 +21,8 @@ use kind_tree::{visit_opt, visit_vec}; use crate::errors::PassError; +pub mod subst; + pub struct UnboundCollector { pub errors: Sender>, diff --git a/crates/kind-pass/src/unbound/subst.rs b/crates/kind-pass/src/unbound/subst.rs new file mode 100644 index 00000000..2ceb480c --- /dev/null +++ b/crates/kind-pass/src/unbound/subst.rs @@ -0,0 +1,101 @@ +use fxhash::FxHashMap; +use kind_tree::untyped::*; + +pub struct Subst { + vars: FxHashMap>, + ctx: im::HashSet +} + +pub fn subst_on_expr(expr: &mut Box, vars: FxHashMap>) { + let mut state = Subst { + vars, + ctx: Default::default(), + }; + + state.subst_expr(expr) +} + +impl Subst { + pub fn subst_entry(&mut self, entry: &mut Entry) { + let backup = self.ctx.clone(); + + self.ctx = backup; + + for rule in &mut entry.rules { + self.subst_rule(rule); + } + + } + + pub fn subst_rule(&mut self, rule: &mut Rule) { + let backup = self.ctx.clone(); + + for pat in &mut rule.pats { + self.subst_expr(pat) + } + + self.subst_expr(&mut rule.body); + + self.ctx = backup; + } + + pub fn subst_pat(&mut self, expr: &mut Box) { + use ExprKind::*; + + match &mut expr.data { + Var { name } => { + self.ctx.insert(name.to_string()); + } + Fun { name: _, args } | Ctr { name: _, args } => { + for arg in args { + self.subst_pat(arg); + } + } + _ => () + } + } + + pub fn subst_expr(&mut self, expr: &mut Box) { + use ExprKind::*; + + match &mut expr.data { + Var { name } => { + if self.ctx.contains(name.to_str()) { + return; + } + if let Some(res) = self.vars.get(name.to_str()) { + *expr = res.clone().clone(); + } + }, + Lambda { param, body, .. } => { + let backup = self.ctx.clone(); + self.ctx.insert(param.to_string()); + self.subst_expr(body); + self.ctx = backup; + } + App { fun, args } => { + self.subst_expr(fun); + for arg in args { + self.subst_expr(arg); + } + } + Fun { name: _, args } | Ctr { name: _, args } => { + for arg in args { + self.subst_expr(arg); + } + } + Let { name, val, next } => { + let backup = self.ctx.clone(); + self.ctx.insert(name.to_string()); + self.subst_expr(val); + self.subst_expr(next); + self.ctx = backup; + } + Binary { left, right, .. } => { + self.subst_expr(left); + self.subst_expr(right); + } + _ => () + } + } +} \ No newline at end of file diff --git a/crates/kind-target-kdl/src/lib.rs b/crates/kind-target-kdl/src/lib.rs index 48e5e335..2d4c9af5 100644 --- a/crates/kind-target-kdl/src/lib.rs +++ b/crates/kind-target-kdl/src/lib.rs @@ -17,12 +17,9 @@ pub fn compile_book( sender: Sender>, namespace: &str, ) -> Option { - // TODO: Inlining // TODO: Remove kdl_states (maybe check if they're ever called?) - // TODO: Don't erase kdl_state functions // TODO: Convert to some sort of Kindelia.Contract let flattened = flatten(book); - let file = compile::compile_book(&flattened, sender, namespace)?; let file = linearize::linearize_file(file);