refactor: a lot of refactoring and implementation of flattening

This commit is contained in:
felipegchi 2022-11-25 14:41:07 -03:00
parent 8d7321b11a
commit 42d8baf03d
46 changed files with 1894 additions and 833 deletions

View File

@ -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
],
)],

View File

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

View File

@ -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)),

View File

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

View File

@ -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'

View File

@ -1,3 +1,4 @@
#derive[open]
record User {
constructor new
name : U60

View File

@ -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.

View File

@ -0,0 +1,6 @@
type Eq <t: Type> (a: t) ~ (b: t) {
rfl: Eq t a a
}
Teste : Eq 123u120 124u120
Teste = Eq.rfl

View File

@ -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!

View File

@ -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

View File

@ -1 +1 @@
4
(Teste [])

View File

@ -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

View File

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

View File

@ -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)

View File

@ -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

View File

@ -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"

View File

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

View File

@ -227,7 +227,7 @@ fn parse_and_store_book_by_path<'a>(
}
module_to_book(&mut failed, session, module, book);
for idents in state.unbound_top_level.values() {
failed |= parse_and_store_book_by_identifier(session, &idents.iter().nth(0).unwrap(), book);
}
@ -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;

View File

@ -13,7 +13,7 @@ use kind_report::data::Diagnostic;
pub struct Session {
pub loaded_paths: Vec<Rc<PathBuf>>,
pub loaded_sources: Vec<String>,
pub loaded_paths_map: FxHashMap<PathBuf, usize>,
/// It will be useful in the future

View File

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

View File

@ -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, "+"),

View File

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

View File

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

View File

@ -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 } => {

View File

@ -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
}
}

View File

@ -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,

View File

@ -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) {

View File

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

View File

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

View File

@ -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 {

View File

@ -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_ } => {

View File

@ -55,6 +55,7 @@ pub struct DiagnosticFrame {
pub enum Log {
Checking(String),
Checked(Duration),
Compiled(Duration),
Failed(Duration),
}
pub trait Diagnostic {

View File

@ -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) => {

View File

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

View File

@ -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))
}

View File

@ -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"

View 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,
}
}

View 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,
}],
},
}
}
}

View 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
}

View File

@ -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)
}

View 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"),
}
}

View File

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

View File

@ -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,
}
}
}

View File

@ -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)]

View File

@ -51,7 +51,7 @@ pub struct Ident {
pub struct QualifiedIdent {
root: Symbol,
aux: Option<Symbol>,
pub range: Range,
/// Flag that is useful to avoid unbound errors while
@ -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,
}

View 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,
}
}
}