From 5b1875c492fe739ca3a64c97752ee81e60ac822d Mon Sep 17 00:00:00 2001 From: felipegchi Date: Mon, 16 Jan 2023 13:52:03 -0300 Subject: [PATCH] feat: mutter derivation --- crates/kind-derive/src/lib.rs | 3 +- crates/kind-derive/src/mutters.rs | 148 ++++++++++++++++++++++++++ crates/kind-pass/src/expand/mod.rs | 20 +++- crates/kind-tree/src/concrete/expr.rs | 64 +++++++++-- crates/kind-tree/src/concrete/pat.rs | 9 ++ 5 files changed, 232 insertions(+), 12 deletions(-) create mode 100644 crates/kind-derive/src/mutters.rs diff --git a/crates/kind-derive/src/lib.rs b/crates/kind-derive/src/lib.rs index d8a25f0d..46bf817d 100644 --- a/crates/kind-derive/src/lib.rs +++ b/crates/kind-derive/src/lib.rs @@ -7,4 +7,5 @@ pub mod matching; pub mod open; pub mod subst; pub mod getters; -pub mod setters; \ No newline at end of file +pub mod setters; +pub mod mutters; \ No newline at end of file diff --git a/crates/kind-derive/src/mutters.rs b/crates/kind-derive/src/mutters.rs new file mode 100644 index 00000000..2a23ccbf --- /dev/null +++ b/crates/kind-derive/src/mutters.rs @@ -0,0 +1,148 @@ +//! Module to derive mutters for record types + +use kind_span::Range; + +use kind_tree::concrete::expr::Expr; +use kind_tree::concrete::pat::{Pat, PatIdent}; +use kind_tree::concrete::*; +use kind_tree::concrete::{self}; +use kind_tree::symbol::{Ident}; +use kind_tree::telescope::Telescope; + +pub fn derive_mutters(range: Range, rec: &RecordDecl) -> Vec { + let mut types = Telescope::default(); + + for arg in rec.parameters.iter() { + types.push(arg.to_implicit()) + } + + // The type + + let all_args = rec.parameters.clone(); + + let res_motive_ty = Expr::cons( + rec.name.clone(), + all_args + .iter() + .cloned() + .map(|x| Binding::Positional(Expr::var(x.name))) + .collect(), + range + ); + + // Sccrutinzies + + types.push(Argument { + hidden: false, + erased: false, + name: Ident::generate("scrutinee"), + typ: Some(res_motive_ty.clone()), + range, + }); + + // Motive with indices + + let mut pats: Vec> = Vec::new(); + + let fields_spine: Vec<_> = rec + .fields + .iter() + .map(|(name, _, typ)| (name.clone(), typ.clone())) + .collect(); + + let params_spine: Vec<_> = rec + .parameters + .iter() + .map(|arg| { + ( + arg.name.clone(), + arg.typ.clone().unwrap_or_else(|| Expr::typ(arg.range.clone())), + ) + }) + .collect(); + + let spine = [params_spine.as_slice(), fields_spine.as_slice()].concat(); + + pats.push(Box::new(Pat { + data: concrete::pat::PatKind::App( + rec.name.add_segment(rec.constructor.to_str()), + spine + .iter() + .cloned() + .map(|(name, _)| Pat::var(name)) + .collect(), + ), + range, + })); + + let mut entries = vec![]; + + let mut cons_name = rec.name.add_segment(rec.constructor.to_str()); + + cons_name.range = rec.constructor.range; + + for (i, (arg, cons_typ)) in fields_spine.iter().enumerate() { + let mut types = types.clone(); + + let place = rec.parameters.len() + i; + + types.push(Argument { + hidden: false, + erased: false, + name: Ident::generate("mut"), + typ: Some(Expr::all(Ident::generate("_"), cons_typ.clone(), cons_typ.clone(), false, range)), + range, + }); + + let new_var = Ident::generate("_fn"); + + let mut pats = pats.clone(); + + pats.push(Box::new(Pat { + data: concrete::pat::PatKind::Var(PatIdent(new_var.clone())), + range, + })); + + let mut args: Vec<_> = spine + .iter() + .cloned() + .map(|x| Binding::Positional(Expr::var(x.0))) + .collect(); + + args[place] = Binding::Positional(Expr::app(Expr::var(new_var), vec![args[place].to_app_binding()], range)); + + let body = Box::new(Expr { + data: ExprKind::Constr { + name: cons_name.clone(), + args, + }, + range, + }); + + let mut name = rec.name.add_segment(arg.to_str()).add_segment("mut"); + + name.range = rec.constructor.range; + + let rules = vec![Box::new(Rule { + name: name.clone(), + pats: pats.clone(), + body, + range: rec.constructor.range, + })]; + + let entry = Entry { + name: name.clone(), + docs: Vec::new(), + args: types.clone(), + typ: res_motive_ty.clone(), + rules, + range: rec.constructor.range, + attrs: Vec::new(), + generated_by: Some(rec.name.to_string().clone()), + }; + + entries.push(entry) + } + + entries +} diff --git a/crates/kind-pass/src/expand/mod.rs b/crates/kind-pass/src/expand/mod.rs index 488c8439..adf18b54 100644 --- a/crates/kind-pass/src/expand/mod.rs +++ b/crates/kind-pass/src/expand/mod.rs @@ -8,6 +8,7 @@ use std::sync::mpsc::Sender; use fxhash::FxHashMap; use kind_derive::getters::derive_getters; use kind_derive::matching::derive_match; +use kind_derive::mutters::derive_mutters; use kind_derive::open::derive_match_rec; use kind_derive::setters::derive_setters; use kind_report::data::Diagnostic; @@ -36,6 +37,7 @@ pub enum Derive { Match, Getters, Setters, + Mutters } impl Display for Derive { @@ -44,13 +46,17 @@ impl Display for Derive { Derive::Match => write!(f, "match"), Derive::Getters => write!(f, "getters"), Derive::Setters => write!(f, "setters"), + Derive::Mutters => write!(f, "mutters"), } } } pub fn insert_or_report(channel: Channel, hashmap: &mut Derivations, key: Derive, range: Range) { if let Some(last_range) = hashmap.get(&key) { - let err = Box::new(PassDiagnostic::DuplicatedAttributeArgument(*last_range, range)); + let err = Box::new(PassDiagnostic::DuplicatedAttributeArgument( + *last_range, + range, + )); channel.send(err).unwrap(); } else { hashmap.insert(key, range); @@ -62,6 +68,7 @@ fn string_to_derive(name: &str) -> Option { "match" => Some(Derive::Match), "getters" => Some(Derive::Getters), "setters" => Some(Derive::Setters), + "mutters" => Some(Derive::Mutters), _ => None, } } @@ -127,7 +134,10 @@ pub fn expand_sum_type( } other => { error_channel - .send(Box::new(PassDiagnostic::CannotDerive(other.to_string(), val))) + .send(Box::new(PassDiagnostic::CannotDerive( + other.to_string(), + val, + ))) .unwrap(); failed = true; } @@ -162,6 +172,12 @@ pub fn expand_record_type( entries.insert(res.name.to_string(), (res, info)); } } + Derive::Mutters => { + for res in derive_mutters(rec.name.range, rec) { + let info = res.extract_book_info(); + entries.insert(res.name.to_string(), (res, info)); + } + } } } } diff --git a/crates/kind-tree/src/concrete/expr.rs b/crates/kind-tree/src/concrete/expr.rs index a2acbfdc..a5e435c2 100644 --- a/crates/kind-tree/src/concrete/expr.rs +++ b/crates/kind-tree/src/concrete/expr.rs @@ -210,8 +210,8 @@ pub enum ExprKind { type_name: QualifiedIdent, var_name: Ident, motive: Option>, - next: Box - } + next: Box, + }, } /// Describes a single expression inside Kind2. @@ -292,7 +292,25 @@ impl Expr { range, }) } +} +impl Binding { + pub fn to_app_binding(&self) -> AppBinding { + match self { + Binding::Positional(expr) => { + AppBinding { + data: expr.clone(), + erased: false, + } + }, + Binding::Named(_, _, expr) => { + AppBinding { + data: expr.clone(), + erased: false, + } + }, + } + } } impl Locatable for Binding { @@ -467,16 +485,34 @@ impl Display for Match { fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { write!(f, "match {} {}", self.typ, self.scrutinee)?; - match &self.motive { - None => Ok(()), - Some(res) => write!(f, " : {}", res), - }?; + if let Some(res) = &self.value { + write!(f, " = {}", res)?; + } + + if !self.with_vars.is_empty() { + write!(f, " with")?; + for var in &self.with_vars { + if let Some(ty) = &var.1 { + write!(f, " ({} : {})", var.0, ty)?; + } else { + write!(f, " {}", var.0)?; + } + } + } + write!(f, " {{ ")?; for case in &self.cases { write!(f, "{}; ", case)? } - write!(f, "}}") + + write!(f, "}}")?; + + if let Some(res) = &self.motive { + write!(f, " : {}", res)?; + } + + Ok(()) } } @@ -556,8 +592,18 @@ impl Display for Expr { args.iter().map(|x| format!(" {}", x)).collect::() ), Let { name, val, next } => write!(f, "(let {} = {}; {})", name, val, next), - Open { type_name, var_name, motive: Some(motive), next } => write!(f, "(open {} {} : {motive}; {})", type_name, var_name, next), - Open { type_name, var_name, motive: None, next } => write!(f, "(open {} {}; {})", type_name, var_name, next), + Open { + type_name, + var_name, + motive: Some(motive), + next, + } => write!(f, "(open {} {} : {motive}; {})", type_name, var_name, next), + Open { + type_name, + var_name, + motive: None, + next, + } => write!(f, "(open {} {}; {})", type_name, var_name, next), If { cond, then_, else_ } => { write!(f, "(if {} {{{}}} else {{{}}})", cond, then_, else_) } diff --git a/crates/kind-tree/src/concrete/pat.rs b/crates/kind-tree/src/concrete/pat.rs index 66fb7a3b..827b7647 100644 --- a/crates/kind-tree/src/concrete/pat.rs +++ b/crates/kind-tree/src/concrete/pat.rs @@ -73,3 +73,12 @@ impl Display for Pat { } } } + +impl Pat { + pub fn var(name: Ident) -> Box { + Box::new(Pat { + range: name.range, + data: PatKind::Var(PatIdent(name)), + }) + } +} \ No newline at end of file