feat: added getter and setter derivings

This commit is contained in:
felipegchi 2022-11-29 13:19:56 -03:00
parent b7b15e7590
commit 6844cc2735
6 changed files with 318 additions and 6 deletions

View File

@ -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<concrete::Entry> {
let mk_var = |name: Ident| -> Box<Expr> {
Box::new(Expr {
data: ExprKind::Var { name },
range,
})
};
let mk_cons = |name: QualifiedIdent, args: Vec<Binding>| -> Box<Expr> {
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<Box<Pat>> = 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
}

View File

@ -4,3 +4,5 @@ pub mod errors;
pub mod matching;
pub mod open;
pub mod subst;
pub mod getters;
pub mod setters;

View File

@ -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()),
};

View File

@ -1,3 +0,0 @@
//! Derives getters and setters for record
//! types.

View File

@ -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<concrete::Entry> {
let mk_var = |name: Ident| -> Box<Expr> {
Box::new(Expr {
data: ExprKind::Var { name },
range,
})
};
let mk_cons = |name: QualifiedIdent, args: Vec<Binding>| -> Box<Expr> {
Box::new(Expr {
data: ExprKind::Constr { name, args },
range,
})
};
let typ = |range: Range| -> Box<Expr> {
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<Box<Pat>> = 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
}

View File

@ -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<Derive> {
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<Box<dyn Diagnostic>>, 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)))