From 6844cc273541dc55c7b6842a1bf2016cd411a4f6 Mon Sep 17 00:00:00 2001 From: felipegchi Date: Tue, 29 Nov 2022 13:19:56 -0300 Subject: [PATCH] feat: added getter and setter derivings --- crates/kind-derive/src/getters.rs | 117 +++++++++++++++++++ crates/kind-derive/src/lib.rs | 2 + crates/kind-derive/src/open.rs | 8 +- crates/kind-derive/src/record.rs | 3 - crates/kind-derive/src/setters.rs | 174 +++++++++++++++++++++++++++++ crates/kind-pass/src/expand/mod.rs | 20 ++++ 6 files changed, 318 insertions(+), 6 deletions(-) create mode 100644 crates/kind-derive/src/getters.rs delete mode 100644 crates/kind-derive/src/record.rs create mode 100644 crates/kind-derive/src/setters.rs diff --git a/crates/kind-derive/src/getters.rs b/crates/kind-derive/src/getters.rs new file mode 100644 index 00000000..edf7aeff --- /dev/null +++ b/crates/kind-derive/src/getters.rs @@ -0,0 +1,117 @@ +//! Module to derive a "open" function for records. + +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, QualifiedIdent}; + +pub fn derive_getters(range: Range, rec: &RecordDecl) -> Vec { + let mk_var = |name: Ident| -> Box { + Box::new(Expr { + data: ExprKind::Var { name }, + range, + }) + }; + + let mk_cons = |name: QualifiedIdent, args: Vec| -> Box { + Box::new(Expr { + data: ExprKind::Constr { name, args }, + range, + }) + }; + + 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 = mk_cons( + rec.name.clone(), + all_args + .iter() + .cloned() + .map(|x| Binding::Positional(mk_var(x.name))) + .collect(), + ); + + // Sccrutinzies + + types.push(Argument { + hidden: false, + erased: false, + name: Ident::generate("scrutinizer"), + typ: Some(res_motive_ty), + range, + }); + + // Motive with indices + + let mut pats: Vec> = Vec::new(); + + let spine: Vec<_> = rec + .fields + .iter() + .map(|(name, _, ty)| (name, ty)) + .collect(); + + pats.push(Box::new(Pat { + data: concrete::pat::PatKind::App( + rec.name.add_segment(rec.constructor.to_str()), + spine + .iter() + .cloned() + .map(|x| { + Box::new(Pat { + data: concrete::pat::PatKind::Var(PatIdent(x.0.clone().with_name(|f| format!("{}_", f)))), + range, + }) + }) + .collect(), + ), + range, + })); + + let mut entries = vec![]; + + for (arg, typ) in spine { + let body = mk_var(arg.with_name(|f| format!("{}_", f)).clone()); + + let mut name = rec + .name + .add_segment(arg.to_str()) + .add_segment("get"); + + 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: typ.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-derive/src/lib.rs b/crates/kind-derive/src/lib.rs index 06fb4eee..dc7f9729 100644 --- a/crates/kind-derive/src/lib.rs +++ b/crates/kind-derive/src/lib.rs @@ -4,3 +4,5 @@ pub mod errors; pub mod matching; pub mod open; pub mod subst; +pub mod getters; +pub mod setters; \ No newline at end of file diff --git a/crates/kind-derive/src/open.rs b/crates/kind-derive/src/open.rs index 7364161d..23c837f8 100644 --- a/crates/kind-derive/src/open.rs +++ b/crates/kind-derive/src/open.rs @@ -42,11 +42,13 @@ pub fn derive_open(range: Range, rec: &RecordDecl) -> concrete::Entry { }) }; - let name = rec + let mut name = rec .name .add_segment(rec.constructor.to_str()) .add_segment("open"); + name.range = rec.constructor.range; + let mut types = Telescope::default(); for arg in rec.parameters.iter() { @@ -146,7 +148,7 @@ pub fn derive_open(range: Range, rec: &RecordDecl) -> concrete::Entry { name: name.clone(), pats, body, - range, + range: rec.constructor.range, })]; let entry = Entry { @@ -155,7 +157,7 @@ pub fn derive_open(range: Range, rec: &RecordDecl) -> concrete::Entry { args: types, typ: ret_ty, rules, - range, + range: rec.constructor.range, attrs: Vec::new(), generated_by: Some(rec.name.to_string().clone()), }; diff --git a/crates/kind-derive/src/record.rs b/crates/kind-derive/src/record.rs deleted file mode 100644 index ca11d673..00000000 --- a/crates/kind-derive/src/record.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Derives getters and setters for record -//! types. - diff --git a/crates/kind-derive/src/setters.rs b/crates/kind-derive/src/setters.rs new file mode 100644 index 00000000..f3d2fcda --- /dev/null +++ b/crates/kind-derive/src/setters.rs @@ -0,0 +1,174 @@ +//! Module to derive a "open" function for records. + +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, QualifiedIdent}; + +pub fn derive_setters(range: Range, rec: &RecordDecl) -> Vec { + let mk_var = |name: Ident| -> Box { + Box::new(Expr { + data: ExprKind::Var { name }, + range, + }) + }; + + let mk_cons = |name: QualifiedIdent, args: Vec| -> Box { + Box::new(Expr { + data: ExprKind::Constr { name, args }, + range, + }) + }; + + let typ = |range: Range| -> Box { + Box::new(Expr { + data: ExprKind::Lit { lit: Literal::Type }, + range, + }) + }; + + let mk_pat_var = |name: Ident| { + Box::new(Pat { + range: name.range, + data: concrete::pat::PatKind::Var(PatIdent(name)), + }) + }; + + 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 = mk_cons( + rec.name.clone(), + all_args + .iter() + .cloned() + .map(|x| Binding::Positional(mk_var(x.name))) + .collect(), + ); + + // Sccrutinzies + + types.push(Argument { + hidden: false, + erased: false, + name: Ident::generate("scrutinizer"), + 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(|| 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, _)| mk_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("set"), + typ: Some(cons_typ.clone()), + range, + }); + + let new_var = Ident::generate("_new_var"); + + 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(mk_var(x.0))) + .collect(); + + args[place] = Binding::Positional(mk_var(new_var)); + + 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("set"); + + 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 0de04731..0b4692e7 100644 --- a/crates/kind-pass/src/expand/mod.rs +++ b/crates/kind-pass/src/expand/mod.rs @@ -6,8 +6,10 @@ use std::fmt::Display; use std::sync::mpsc::Sender; use fxhash::FxHashMap; +use kind_derive::getters::derive_getters; use kind_derive::matching::derive_match; use kind_derive::open::derive_open; +use kind_derive::setters::derive_setters; use kind_report::data::Diagnostic; use kind_span::Locatable; use kind_span::Range; @@ -23,6 +25,8 @@ pub mod uses; pub enum Derive { Match, Open, + Getters, + Setters } impl Display for Derive { @@ -30,6 +34,8 @@ impl Display for Derive { match self { Derive::Match => write!(f, "match"), Derive::Open => write!(f, "open"), + Derive::Getters => write!(f, "getters"), + Derive::Setters => write!(f, "setters"), } } } @@ -59,6 +65,8 @@ fn string_to_derive(name: &str) -> Option { match name { "match" => Some(Derive::Match), "open" => Some(Derive::Open), + "getters" => Some(Derive::Getters), + "setters" => Some(Derive::Setters), _ => None, } } @@ -160,6 +168,18 @@ pub fn expand_book(error_channel: Sender>, book: &mut Book) let info = res.extract_book_info(); entries.insert(res.name.to_string(), (res, info)); } + Derive::Getters => { + for res in derive_getters(rec.name.range, rec) { + let info = res.extract_book_info(); + entries.insert(res.name.to_string(), (res, info)); + } + } + Derive::Setters => { + for res in derive_setters(rec.name.range, rec) { + let info = res.extract_book_info(); + entries.insert(res.name.to_string(), (res, info)); + } + } other => { error_channel .send(Box::new(PassError::CannotDerive(other.to_string(), val)))