mirror of
https://github.com/HigherOrderCO/Kind1.git
synced 2024-09-11 08:45:32 +03:00
refactor: a lot of refactoring and implementation of flattening
This commit is contained in:
parent
8d7321b11a
commit
42d8baf03d
@ -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<Term> {
|
||||
mk_single_ctr(format!("{}.", ident))
|
||||
}
|
||||
|
||||
fn span_to_num(span: Span) -> Box<Term> {
|
||||
fn range_to_num(range: Range) -> Box<Term> {
|
||||
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<Term> {
|
||||
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<Term>) -> Box<Term> {
|
||||
})
|
||||
}
|
||||
|
||||
fn desugar_str(input: &str, span: Span) -> Box<desugared::Expr> {
|
||||
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<desugared::Expr> {
|
||||
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<Term> {
|
||||
@ -163,68 +151,89 @@ fn codegen_all_expr(
|
||||
) -> Box<Term> {
|
||||
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::<Vec<Box<Term>>>()
|
||||
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::<Vec<Box<Term>>>()
|
||||
],
|
||||
),
|
||||
Fun(name, spine) => {
|
||||
let new_spine: Vec<Box<Term>> = spine
|
||||
Fun { name, args } => {
|
||||
let new_spine: Vec<Box<Term>> = 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<lang
|
||||
mk_lifted_ctr(
|
||||
eval_ctr(true, TermTag::All),
|
||||
vec![
|
||||
span_to_num(Span::Locatable(arg.span)),
|
||||
range_to_num(arg.range),
|
||||
mk_u60(arg.name.encode()),
|
||||
codegen_expr(true, &arg.typ),
|
||||
lam(&arg.name, codegen_type(&args[1..], typ)),
|
||||
@ -443,7 +474,7 @@ fn codegen_entry_rules(
|
||||
format!("QT{}", index),
|
||||
vec_preppend![
|
||||
mk_ctr_name(&entry.name),
|
||||
span_to_num(entry.span);
|
||||
range_to_num(entry.range);
|
||||
args
|
||||
],
|
||||
)],
|
||||
|
@ -78,7 +78,7 @@ pub fn type_check(
|
||||
/// we run the "eval_main" that runs the generated version that both HVM and
|
||||
/// and the checker can understand.
|
||||
pub fn eval_api(book: &Book) -> Box<Term> {
|
||||
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();
|
||||
|
||||
|
@ -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<Range, String> {
|
||||
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<u64, String> {
|
||||
@ -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<TypeError, String> {
|
||||
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)),
|
||||
|
@ -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<T>(
|
||||
render_config: RenderConfig,
|
||||
root: PathBuf,
|
||||
file: String,
|
||||
compiled: bool,
|
||||
fun: &mut dyn FnMut(&mut Session) -> Option<T>,
|
||||
) -> Option<T> {
|
||||
let (rx, tx) = std::sync::mpsc::channel();
|
||||
@ -148,7 +140,15 @@ pub fn compile_in_session<T>(
|
||||
let diagnostics = tx.try_iter().collect::<Vec<Box<dyn Diagnostic>>>();
|
||||
|
||||
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!(),
|
||||
}
|
||||
}
|
||||
|
@ -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'
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
#derive[open]
|
||||
record User {
|
||||
constructor new
|
||||
name : U60
|
||||
|
@ -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.
|
||||
|
@ -0,0 +1,6 @@
|
||||
type Eq <t: Type> (a: t) ~ (b: t) {
|
||||
rfl: Eq t a a
|
||||
}
|
||||
|
||||
Teste : Eq 123u120 124u120
|
||||
Teste = Eq.rfl
|
@ -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!
|
||||
|
||||
|
@ -0,0 +1,12 @@
|
||||
type Eq <t: Type> (a: t) ~ (b: t) {
|
||||
rfl: Eq t a a
|
||||
}
|
||||
|
||||
record U120 {
|
||||
constructor new
|
||||
low : U60
|
||||
high : U60
|
||||
}
|
||||
|
||||
Teste : Eq 123u120 124u120
|
||||
Teste = Eq.rfl
|
@ -1 +1 @@
|
||||
4
|
||||
(Teste [])
|
@ -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
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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<desugared::Book> {
|
||||
pub fn type_check_book(session: &mut Session, path: &PathBuf) -> Option<untyped::Book> {
|
||||
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<desugared::Book> {
|
||||
) -> Option<untyped::Book> {
|
||||
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<desug
|
||||
Some(desugared_book)
|
||||
}
|
||||
|
||||
pub fn compile_book_to_hvm(book: desugared::Book) -> 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<kind_target_kdl::File> {
|
||||
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::<Vec<_>>();
|
||||
|
||||
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<backend::Term> {
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
|
@ -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<Ident> = unbound.iter().map(|x| x.to_ident()).collect();
|
||||
let res: Vec<Ident> = unbound
|
||||
.iter()
|
||||
.filter(|x| !x.generated)
|
||||
.map(|x| x.to_ident())
|
||||
.collect();
|
||||
if !res.is_empty() {
|
||||
unbound_variable(session, &book, &res);
|
||||
failed = true;
|
||||
|
@ -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());
|
||||
|
@ -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, "+"),
|
||||
|
@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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<Constructor, SyntaxDiagnostic> {
|
||||
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<Attribute>,
|
||||
) -> Result<RecordDecl, SyntaxDiagnostic> {
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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 } => {
|
||||
|
@ -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<desugared::Attribute> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -249,15 +249,16 @@ impl<'a> DesugarState<'a> {
|
||||
} else {
|
||||
let mut idx: Vec<Ident> = 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,
|
||||
|
@ -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<desugared::Expr> {
|
||||
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) {
|
||||
|
@ -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::<Vec<Box<desugared::Expr>>>();
|
||||
|
||||
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::<Vec<desugared::Argument>>();
|
||||
|
||||
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::<Vec<desugared::Argument>>()
|
||||
.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,
|
||||
};
|
||||
|
@ -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<Box<dyn Diagnostic>>,
|
||||
entrypoint: FxHashSet<String>,
|
||||
) -> Option<Book> {
|
||||
) -> Option<untyped::Book> {
|
||||
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<PassError>) {
|
||||
self.errs.send(err).unwrap();
|
||||
self.failed = true;
|
||||
}
|
||||
pub fn err_irrelevant(
|
||||
&mut self,
|
||||
declared_val: Option<Range>,
|
||||
used: Range,
|
||||
declared_ty: Option<Range>,
|
||||
) {
|
||||
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<usize>) -> Option<Relevance> {
|
||||
@ -195,7 +197,7 @@ impl<'a> ErasureState<'a> {
|
||||
on: (Option<Range>, Relevance),
|
||||
name: &QualifiedIdent,
|
||||
spine: &Vec<Box<Expr>>,
|
||||
) -> Vec<Box<Expr>> {
|
||||
) -> Vec<Box<untyped::Expr>> {
|
||||
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<Range>, Relevance), pat: &Expr) -> Box<Expr> {
|
||||
pub fn erase_pat(&mut self, on: (Option<Range>, Relevance), pat: &Expr) -> Box<untyped::Expr> {
|
||||
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<Range>, Relevance), expr: &Expr) -> Box<Expr> {
|
||||
pub fn erase_expr(
|
||||
&mut self,
|
||||
on: &(Option<Range>, Relevance),
|
||||
expr: &Expr,
|
||||
) -> Box<untyped::Expr> {
|
||||
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<Range>, 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<Entry> {
|
||||
pub fn erase_entry(&mut self, entry: &Entry) -> Box<untyped::Entry> {
|
||||
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::<Vec<Rule>>();
|
||||
Box::new(Entry {
|
||||
.collect::<Vec<untyped::Rule>>();
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -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>, Range, Option<Range>),
|
||||
CannotFindAlias(String, Range),
|
||||
NotATypeConstructor(Range, Range),
|
||||
ShouldBeAParameter(Span, Range),
|
||||
ShouldBeAParameter(Option<Range>, Range),
|
||||
NoFieldCoverage(Range, Vec<String>),
|
||||
CannotPatternMatchOnErased(Range),
|
||||
UnboundVariable(Vec<Ident>, Vec<String>),
|
||||
@ -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 {
|
||||
|
@ -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_ } => {
|
||||
|
@ -55,6 +55,7 @@ pub struct DiagnosticFrame {
|
||||
pub enum Log {
|
||||
Checking(String),
|
||||
Checked(Duration),
|
||||
Compiled(Duration),
|
||||
Failed(Duration),
|
||||
}
|
||||
pub trait Diagnostic {
|
||||
|
@ -93,13 +93,14 @@ fn get_colorizer<T>(color: &Color) -> &dyn Fn(T) -> Paint<T> {
|
||||
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<T: Write + Sized>(
|
||||
}
|
||||
}
|
||||
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<dyn Fn(&str) -> 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) => {
|
||||
|
@ -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<Range> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Term> {
|
||||
use desugared::ExprKind::*;
|
||||
pub fn compile_term(expr: &untyped::Expr) -> Box<Term> {
|
||||
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<Term> {
|
||||
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<Term> {
|
||||
})
|
||||
};
|
||||
|
||||
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))
|
||||
}
|
||||
|
@ -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"
|
368
crates/kind-target-kdl/src/compile.rs
Normal file
368
crates/kind-target-kdl/src/compile.rs
Normal file
@ -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<String, kdl::Statement>,
|
||||
runs: Vec<kdl::Statement>,
|
||||
}
|
||||
|
||||
pub struct CompileCtx<'a> {
|
||||
file: File,
|
||||
kdl_names: FxHashMap<String, kdl::Name>,
|
||||
kdl_states: Vec<String>,
|
||||
book: &'a untyped::Book,
|
||||
|
||||
sender: Sender<Box<dyn Diagnostic>>,
|
||||
failed: bool,
|
||||
}
|
||||
|
||||
impl<'a> CompileCtx<'a> {
|
||||
pub fn new(book: &'a untyped::Book, sender: Sender<Box<dyn Diagnostic>>) -> 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<dyn Diagnostic>) {
|
||||
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<Box<dyn Diagnostic>>,
|
||||
namespace: &str,
|
||||
) -> Option<File> {
|
||||
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::<Vec<_>>();
|
||||
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,
|
||||
}
|
||||
}
|
82
crates/kind-target-kdl/src/errors.rs
Normal file
82
crates/kind-target-kdl/src/errors.rs
Normal file
@ -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<kind_span::SyntaxCtxIndex> {
|
||||
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,
|
||||
}],
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
235
crates/kind-target-kdl/src/flatten.rs
Normal file
235
crates/kind-target-kdl/src/flatten.rs
Normal file
@ -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<usize>,
|
||||
) -> (Rule, Vec<Entry>) {
|
||||
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<Rule> = Vec::new();
|
||||
let mut old_rule_pats: Vec<Box<Expr>> = Vec::new();
|
||||
let mut old_rule_body_args: Vec<Box<Expr>> = 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<Entry> {
|
||||
let mut name_count = 0;
|
||||
|
||||
let mut skip: FxHashSet<usize> = FxHashSet::default();
|
||||
let mut new_entries: Vec<Entry> = Vec::new();
|
||||
let mut old_entry_rules: Vec<Rule> = 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
|
||||
}
|
@ -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<String, kdl::Statement>,
|
||||
runs: Vec<kdl::Statement>,
|
||||
}
|
||||
use flatten::flatten;
|
||||
use kind_report::data::Diagnostic;
|
||||
use kind_tree::{untyped};
|
||||
|
||||
pub struct CompileCtx<'a> {
|
||||
file: File,
|
||||
kdl_names: FxHashMap<String, kdl::Name>,
|
||||
kdl_states: Vec<String>,
|
||||
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<Box<dyn Diagnostic>>, namespace: &str) -> Option<compile::File> {
|
||||
let flattened = flatten(book);
|
||||
compile::compile_book(&flattened, sender, namespace)
|
||||
}
|
||||
|
42
crates/kind-target-kdl/src/subst.rs
Normal file
42
crates/kind-target-kdl/src/subst.rs
Normal file
@ -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"),
|
||||
}
|
||||
}
|
@ -120,6 +120,7 @@ pub struct Entry {
|
||||
pub rules: Vec<Box<Rule>>,
|
||||
pub range: Range,
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub generated_by: Option<String>,
|
||||
}
|
||||
|
||||
/// A single cosntructor inside the algebraic data
|
||||
@ -128,6 +129,7 @@ pub struct Entry {
|
||||
pub struct Constructor {
|
||||
pub name: Ident,
|
||||
pub docs: Vec<String>,
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub args: Telescope<Argument>,
|
||||
pub typ: Option<Box<Expr>>,
|
||||
}
|
||||
@ -153,6 +155,7 @@ pub struct RecordDecl {
|
||||
pub parameters: Telescope<Argument>,
|
||||
pub fields: Vec<(Ident, Vec<String>, Box<Expr>)>,
|
||||
pub attrs: Vec<Attribute>,
|
||||
pub cons_attrs: Vec<Attribute>,
|
||||
}
|
||||
|
||||
/// 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(())
|
||||
}
|
||||
|
@ -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<Expr>, Box<Expr>, bool),
|
||||
All {
|
||||
param: Ident,
|
||||
typ: Box<Expr>,
|
||||
body: Box<Expr>,
|
||||
erased: bool,
|
||||
},
|
||||
/// A anonymous function that receives one argument
|
||||
Lambda(Ident, Box<Expr>, bool),
|
||||
Lambda {
|
||||
param: Ident,
|
||||
body: Box<Expr>,
|
||||
erased: bool,
|
||||
},
|
||||
/// Application of a expression to a spine of expressions
|
||||
App(Box<Expr>, Vec<AppBinding>),
|
||||
App {
|
||||
fun: Box<Expr>,
|
||||
args: Vec<AppBinding>,
|
||||
},
|
||||
/// 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<Expr>, Box<Expr>),
|
||||
Let {
|
||||
name: Ident,
|
||||
val: Box<Expr>,
|
||||
next: Box<Expr>,
|
||||
},
|
||||
/// Type ascription (x : y)
|
||||
Ann(Box<Expr>, Box<Expr>),
|
||||
Ann { expr: Box<Expr>, typ: Box<Expr> },
|
||||
/// Substitution
|
||||
Sub(Ident, usize, usize, Box<Expr>),
|
||||
Sub {
|
||||
name: Ident,
|
||||
indx: usize,
|
||||
redx: usize,
|
||||
expr: Box<Expr>,
|
||||
},
|
||||
/// 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<Expr>, Box<Expr>),
|
||||
Binary {
|
||||
op: Operator,
|
||||
left: Box<Expr>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
/// 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<Expr> {
|
||||
pub fn var(name: Ident) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
data,
|
||||
span: Span::Generated,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn var(ident: Ident) -> Box<Expr> {
|
||||
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<Expr>,
|
||||
body: Box<Expr>,
|
||||
erased: bool,
|
||||
) -> Box<Expr> {
|
||||
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<Expr>) -> Box<Expr> {
|
||||
pub fn sub(range: Range, name: Ident, indx: usize, redx: usize, expr: Box<Expr>) -> Box<Expr> {
|
||||
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<Expr>, erased: bool) -> Box<Expr> {
|
||||
pub fn lambda(range: Range, param: Ident, body: Box<Expr>, erased: bool) -> Box<Expr> {
|
||||
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<Expr> {
|
||||
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<Expr>, spine: Vec<AppBinding>) -> Box<Expr> {
|
||||
pub fn app(range: Range, fun: Box<Expr>, args: Vec<AppBinding>) -> Box<Expr> {
|
||||
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<Expr>>) -> Box<Expr> {
|
||||
pub fn fun(range: Range, name: QualifiedIdent, args: Vec<Box<Expr>>) -> Box<Expr> {
|
||||
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<Expr>>) -> Box<Expr> {
|
||||
pub fn ctr(range: Range, name: QualifiedIdent, args: Vec<Box<Expr>>) -> Box<Expr> {
|
||||
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<Expr>, body: Box<Expr>) -> Box<Expr> {
|
||||
pub fn let_(range: Range, name: Ident, val: Box<Expr>, next: Box<Expr>) -> Box<Expr> {
|
||||
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<Expr>, typ: Box<Expr>) -> Box<Expr> {
|
||||
pub fn ann(range: Range, expr: Box<Expr>, typ: Box<Expr>) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
span: Span::Locatable(range),
|
||||
data: ExprKind::Ann(val, typ),
|
||||
range,
|
||||
data: ExprKind::Ann { expr, typ },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn typ(range: Range) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
span: Span::Locatable(range),
|
||||
range,
|
||||
data: ExprKind::Typ,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn u60(range: Range) -> Box<Expr> {
|
||||
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<Expr> {
|
||||
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<Expr> {
|
||||
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<Expr> {
|
||||
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<Expr>, right: Box<Expr>) -> Box<Expr> {
|
||||
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<Expr> {
|
||||
Box::new(Expr {
|
||||
span: Span::Locatable(range),
|
||||
data: ExprKind::Hole(num),
|
||||
range,
|
||||
data: ExprKind::Hole { num },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn str(range: Range, str: String) -> Box<Expr> {
|
||||
pub fn str(range: Range, val: String) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
span: Span::Locatable(range),
|
||||
data: ExprKind::Str(str),
|
||||
range,
|
||||
data: ExprKind::Str { val },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn hlp(range: Range, hlp: Ident) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
span: Span::Locatable(range),
|
||||
range,
|
||||
data: ExprKind::Hlp(hlp),
|
||||
})
|
||||
}
|
||||
@ -227,7 +274,7 @@ impl Expr {
|
||||
pub fn err(range: Range) -> Box<Expr> {
|
||||
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<Expr>,
|
||||
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<Box<Expr>>,
|
||||
pub body: Box<Expr>,
|
||||
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<Argument>,
|
||||
pub typ: Box<Expr>,
|
||||
pub rules: Vec<Rule>,
|
||||
pub attrs: Vec<Attribute>,
|
||||
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::<String>()
|
||||
fun,
|
||||
args.iter().map(|x| format!(" {}", x)).collect::<String>()
|
||||
),
|
||||
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::<String>()
|
||||
name,
|
||||
args.iter().map(|x| format!(" {}", x)).collect::<String>()
|
||||
)
|
||||
}
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Ident>,
|
||||
pub kdl_state: Option<Ident>,
|
||||
}
|
||||
|
||||
/// Enum of binary operators.
|
||||
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
|
||||
|
@ -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,
|
||||
}
|
||||
|
331
crates/kind-tree/src/untyped/mod.rs
Normal file
331
crates/kind-tree/src/untyped/mod.rs
Normal file
@ -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<Box<Expr>>;
|
||||
|
||||
#[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<Expr>,
|
||||
erased: bool,
|
||||
},
|
||||
/// Application of a expression to a spine of expressions
|
||||
App {
|
||||
fun: Box<Expr>,
|
||||
args: Vec<Box<Expr>>,
|
||||
},
|
||||
/// 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<Expr>,
|
||||
next: Box<Expr>,
|
||||
},
|
||||
/// Primitive numeric values
|
||||
Num {
|
||||
num: crate::Number,
|
||||
},
|
||||
/// Very special constructor :)
|
||||
Str {
|
||||
val: String,
|
||||
},
|
||||
/// Binary operation (e.g. 2 + 3)
|
||||
Binary {
|
||||
op: Operator,
|
||||
left: Box<Expr>,
|
||||
right: Box<Expr>,
|
||||
},
|
||||
|
||||
Err,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
|
||||
pub struct Expr {
|
||||
pub data: ExprKind,
|
||||
pub range: Range,
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub fn var(name: Ident) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range: name.range,
|
||||
data: ExprKind::Var { name },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn str(range: Range, val: String) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range,
|
||||
data: ExprKind::Str { val },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lambda(range: Range, param: Ident, body: Box<Expr>, erased: bool) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range,
|
||||
data: ExprKind::Lambda {
|
||||
param,
|
||||
body,
|
||||
erased,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn fun(range: Range, name: QualifiedIdent, args: Vec<Box<Expr>>) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range: range.into(),
|
||||
data: ExprKind::Fun { name, args },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn app(range: Range, fun: Box<Expr>, args: Vec<Box<Expr>>) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range: range.into(),
|
||||
data: ExprKind::App { fun, args },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn ctr(range: Range, name: QualifiedIdent, args: Vec<Box<Expr>>) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range: range.into(),
|
||||
data: ExprKind::Ctr { name, args },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn let_(range: Range, name: Ident, val: Box<Expr>, next: Box<Expr>) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range,
|
||||
data: ExprKind::Let { name, val, next },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn num60(range: Range, num: u64) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range,
|
||||
data: ExprKind::Num {
|
||||
num: crate::Number::U60(num),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn num120(range: Range, num: u128) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range,
|
||||
data: ExprKind::Num {
|
||||
num: crate::Number::U120(num),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn binary(range: Range, op: Operator, left: Box<Expr>, right: Box<Expr>) -> Box<Expr> {
|
||||
Box::new(Expr {
|
||||
range,
|
||||
data: ExprKind::Binary { op, left, right },
|
||||
})
|
||||
}
|
||||
|
||||
pub fn err(range: Range) -> Box<Expr> {
|
||||
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<Expr>,
|
||||
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<Box<Expr>>,
|
||||
pub body: Box<Expr>,
|
||||
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<Rule>,
|
||||
pub attrs: Attributes,
|
||||
pub range: Range,
|
||||
}
|
||||
|
||||
/// A book is a collection of desugared entries.
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct Book {
|
||||
pub entrs: LinkedHashMap<String, Box<Entry>>,
|
||||
pub names: FxHashMap<String, usize>,
|
||||
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::<String>()
|
||||
),
|
||||
Fun { name, args } | Ctr { name, args } => {
|
||||
if args.is_empty() {
|
||||
write!(f, "{}", name)
|
||||
} else {
|
||||
write!(
|
||||
f,
|
||||
"({}{})",
|
||||
name,
|
||||
args.iter().map(|x| format!(" {}", x)).collect::<String>()
|
||||
)
|
||||
}
|
||||
}
|
||||
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<Expr>, range: Range) -> Argument {
|
||||
Argument {
|
||||
hidden: false,
|
||||
erased: false,
|
||||
name: name.clone(),
|
||||
typ,
|
||||
range,
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user