mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
IR pipeline
This commit is contained in:
parent
9a649db22a
commit
291d18a92d
10
Cargo.lock
generated
10
Cargo.lock
generated
|
@ -79,6 +79,7 @@ dependencies = [
|
||||||
"ariadne",
|
"ariadne",
|
||||||
"chumsky",
|
"chumsky",
|
||||||
"clap",
|
"clap",
|
||||||
|
"ir",
|
||||||
"syntax",
|
"syntax",
|
||||||
"typing",
|
"typing",
|
||||||
]
|
]
|
||||||
|
@ -212,6 +213,15 @@ dependencies = [
|
||||||
"windows-sys",
|
"windows-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"chumsky",
|
||||||
|
"syntax",
|
||||||
|
"typing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "is-terminal"
|
name = "is-terminal"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
|
|
@ -4,4 +4,5 @@ members = [
|
||||||
"bin",
|
"bin",
|
||||||
"syntax",
|
"syntax",
|
||||||
"typing",
|
"typing",
|
||||||
|
"ir",
|
||||||
]
|
]
|
|
@ -9,6 +9,7 @@ chumsky = "1.0.0-alpha.3"
|
||||||
clap = { version = "4.2.4", features = ["derive"] }
|
clap = { version = "4.2.4", features = ["derive"] }
|
||||||
syntax = { path = "../syntax" }
|
syntax = { path = "../syntax" }
|
||||||
typing = { path = "../typing" }
|
typing = { path = "../typing" }
|
||||||
|
ir = { path = "../ir" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "hc"
|
name = "hc"
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use ariadne::{sources, Color, Label, Report, ReportKind};
|
use ariadne::{sources, Color, Label, Report, ReportKind};
|
||||||
use chumsky::{Parser, prelude::Input};
|
use chumsky::{Parser, prelude::Input};
|
||||||
|
|
||||||
use syntax::parser::{lexer, exprs_parser};
|
use syntax::parser::{lexer, exprs_parser};
|
||||||
use typing::infer::{infer_exprs, InferErrorKind};
|
use typing::infer::{infer_exprs, InferErrorKind};
|
||||||
|
use ir::Lowerer;
|
||||||
|
|
||||||
pub mod args;
|
pub mod args;
|
||||||
|
|
||||||
|
@ -27,6 +29,7 @@ fn main() {
|
||||||
// Typecheck if there are no lexing or parsing errors
|
// Typecheck if there are no lexing or parsing errors
|
||||||
if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) {
|
if let Some(ast) = ast.filter(|_| errs.len() + parse_errs.len() == 0) {
|
||||||
let (ast, e) = infer_exprs(ast.0);
|
let (ast, e) = infer_exprs(ast.0);
|
||||||
|
// If there is an error, print it
|
||||||
if !e.is_empty() {
|
if !e.is_empty() {
|
||||||
e.into_iter()
|
e.into_iter()
|
||||||
.for_each(|e| {
|
.for_each(|e| {
|
||||||
|
@ -49,8 +52,12 @@ fn main() {
|
||||||
.print(sources([(filename.clone(), src.clone())]))
|
.print(sources([(filename.clone(), src.clone())]))
|
||||||
.unwrap()
|
.unwrap()
|
||||||
});
|
});
|
||||||
|
// Else go to the next stage
|
||||||
} else {
|
} else {
|
||||||
ast.iter().for_each(|node| println!("{:?}", node.0));
|
// ast.iter().for_each(|node| println!("{:?}", node.0));
|
||||||
|
let mut l = Lowerer::new();
|
||||||
|
let irs = l.lower_texprs(ast);
|
||||||
|
irs.iter().for_each(|ir| println!("{:?}", ir));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
9
ir/Cargo.toml
Normal file
9
ir/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "ir"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chumsky = "1.0.0-alpha.3"
|
||||||
|
syntax = { path = "../syntax" }
|
||||||
|
typing = { path = "../typing" }
|
100
ir/src/lib.rs
Normal file
100
ir/src/lib.rs
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
use chumsky::span::SimpleSpan;
|
||||||
|
use syntax::expr::{Lit, UnaryOp, BinaryOp};
|
||||||
|
use typing::typed::TExpr;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum IExpr<'src> {
|
||||||
|
IntPush(i64),
|
||||||
|
IntAdd,
|
||||||
|
IntSub,
|
||||||
|
VarLoad(&'src str),
|
||||||
|
VarStore(&'src str),
|
||||||
|
FnPush(Vec<Self>),
|
||||||
|
Call,
|
||||||
|
Ret,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Lowerer {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lowerer {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_texpr<'a>(self: &mut Self, e: TExpr<'a>) -> Vec<IExpr<'a>> {
|
||||||
|
use IExpr::*;
|
||||||
|
match e {
|
||||||
|
TExpr::Lit(l) => match l {
|
||||||
|
Lit::Unit => todo!(),
|
||||||
|
Lit::Bool(_) => todo!(),
|
||||||
|
Lit::Int(n) => vec![IntPush(n)],
|
||||||
|
Lit::Str(_) => todo!(),
|
||||||
|
}
|
||||||
|
TExpr::Ident(s) => vec![VarLoad(s)],
|
||||||
|
TExpr::Unary { op, expr, .. } => {
|
||||||
|
let mut expr = self.lower_texpr(*expr.0);
|
||||||
|
expr.push(match op {
|
||||||
|
UnaryOp::Neg => IntSub,
|
||||||
|
UnaryOp::Not => todo!(),
|
||||||
|
});
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
TExpr::Binary { op, lhs, rhs, .. } if op == BinaryOp::Pipe => {
|
||||||
|
println!("{lhs:?}");
|
||||||
|
println!("{rhs:?}");
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
TExpr::Binary { op, lhs, rhs, .. } => {
|
||||||
|
let mut lhs = self.lower_texpr(*lhs.0);
|
||||||
|
let mut rhs = self.lower_texpr(*rhs.0);
|
||||||
|
lhs.append(&mut rhs);
|
||||||
|
lhs.push(match op {
|
||||||
|
BinaryOp::Add => IExpr::IntAdd,
|
||||||
|
BinaryOp::Sub => IExpr::IntSub,
|
||||||
|
BinaryOp::Mul => todo!(),
|
||||||
|
BinaryOp::Div => todo!(),
|
||||||
|
BinaryOp::Rem => todo!(),
|
||||||
|
BinaryOp::Eq => todo!(),
|
||||||
|
BinaryOp::Ne => todo!(),
|
||||||
|
BinaryOp::Lt => todo!(),
|
||||||
|
BinaryOp::Gt => todo!(),
|
||||||
|
BinaryOp::Le => todo!(),
|
||||||
|
BinaryOp::Ge => todo!(),
|
||||||
|
BinaryOp::And => todo!(),
|
||||||
|
BinaryOp::Or => todo!(),
|
||||||
|
BinaryOp::Pipe => unreachable!(),
|
||||||
|
});
|
||||||
|
lhs
|
||||||
|
}
|
||||||
|
|
||||||
|
TExpr::Lambda { body, .. } => {
|
||||||
|
let mut es = self.lower_texpr(*body.0);
|
||||||
|
es.push(IExpr::Ret);
|
||||||
|
vec![IExpr::FnPush(es)]
|
||||||
|
},
|
||||||
|
TExpr::Call { func, args } => {
|
||||||
|
let mut es: Vec<IExpr> = args.into_iter()
|
||||||
|
.flat_map(|(e, _)| self.lower_texpr(e))
|
||||||
|
.collect();
|
||||||
|
es.append(&mut self.lower_texpr(*func.0));
|
||||||
|
es.push(IExpr::Call);
|
||||||
|
es
|
||||||
|
},
|
||||||
|
TExpr::Define { name, value, .. } => {
|
||||||
|
let mut es = self.lower_texpr(*value.0);
|
||||||
|
es.push(IExpr::VarStore(name));
|
||||||
|
es
|
||||||
|
},
|
||||||
|
|
||||||
|
e => unimplemented!("{:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lower_texprs<'a>(self: &mut Self, e: Vec<(TExpr<'a>, SimpleSpan)>) -> Vec<IExpr<'a>> {
|
||||||
|
e.into_iter()
|
||||||
|
.flat_map(|(e, _)| self.lower_texpr(e))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
2
ref.hlm
Normal file
2
ref.hlm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
let by_ref = fn (&x: int) = *x + 1;
|
||||||
|
let by_value = fn (x: int) = x + 1;
|
|
@ -1,4 +1,5 @@
|
||||||
let succ = fn x = x + 1;
|
let a = fun (x Int, y) Int -> x + y;
|
||||||
let add = fn a b = a + b;
|
let b = fun (x, y) -> x + y;
|
||||||
|
a(34, 35);
|
||||||
|
|
||||||
let res = 10 |> succ |> fn a = add(a, 1) |> add;
|
fun (x Int, y) Int -> x + y;
|
28
simple.ir
Normal file
28
simple.ir
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
succ x:
|
||||||
|
var_load x
|
||||||
|
int_push 1
|
||||||
|
int_add
|
||||||
|
ret
|
||||||
|
|
||||||
|
add a b:
|
||||||
|
var_load a
|
||||||
|
var_load b
|
||||||
|
int_add
|
||||||
|
ret
|
||||||
|
|
||||||
|
dedu x:
|
||||||
|
var_load x
|
||||||
|
int_push 1
|
||||||
|
int_sub
|
||||||
|
ret
|
||||||
|
|
||||||
|
_lambda_0 a:
|
||||||
|
var_load a
|
||||||
|
int_push 1
|
||||||
|
call add
|
||||||
|
ret
|
||||||
|
|
||||||
|
int_push 10
|
||||||
|
call succ
|
||||||
|
call _lambda_0
|
||||||
|
call dedu
|
|
@ -100,7 +100,7 @@ impl Display for UnaryOp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
pub enum BinaryOp {
|
pub enum BinaryOp {
|
||||||
Add, Sub, Mul, Div, Rem,
|
Add, Sub, Mul, Div, Rem,
|
||||||
And, Or,
|
And, Or,
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
|
||||||
"false" => Token::Bool(false),
|
"false" => Token::Bool(false),
|
||||||
"let" => Token::Let,
|
"let" => Token::Let,
|
||||||
"in" => Token::In,
|
"in" => Token::In,
|
||||||
"fn" => Token::Func,
|
"fun" => Token::Func,
|
||||||
"return" => Token::Return,
|
"return" => Token::Return,
|
||||||
"if" => Token::If,
|
"if" => Token::If,
|
||||||
"then" => Token::Then,
|
"then" => Token::Then,
|
||||||
|
@ -143,29 +143,19 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
||||||
)
|
)
|
||||||
.map(|e: Spanned<Expr>| e.0);
|
.map(|e: Spanned<Expr>| e.0);
|
||||||
|
|
||||||
// func (x t, y t) : rt = e
|
|
||||||
// func x, y = e
|
|
||||||
let lambda = just(Token::Func)
|
let lambda = just(Token::Func)
|
||||||
.ignore_then(
|
.ignore_then(
|
||||||
symbol
|
symbol
|
||||||
.map(|s| (s, None))
|
.then(type_parser().or_not())
|
||||||
.or(symbol
|
.separated_by(just(Token::Comma))
|
||||||
.then(type_parser())
|
.collect::<Vec<_>>()
|
||||||
.delimited_by(
|
.delimited_by(
|
||||||
just(Token::Open(Delim::Paren)),
|
just(Token::Open(Delim::Paren)),
|
||||||
just(Token::Close(Delim::Paren)),
|
just(Token::Close(Delim::Paren)),
|
||||||
)
|
)
|
||||||
.map(|(s, t)| (s, Some(t)))
|
|
||||||
)
|
)
|
||||||
.repeated()
|
.then(type_parser().or_not())
|
||||||
.collect::<Vec<_>>()
|
.then_ignore(just(Token::Arrow))
|
||||||
)
|
|
||||||
.then(
|
|
||||||
just(Token::Colon)
|
|
||||||
.ignore_then(type_parser())
|
|
||||||
.or_not()
|
|
||||||
)
|
|
||||||
.then_ignore(just(Token::Assign))
|
|
||||||
.then(expr.clone())
|
.then(expr.clone())
|
||||||
.map(|((args, ret), body)| Expr::Lambda(args, ret, boxspan(body)));
|
.map(|((args, ret), body)| Expr::Lambda(args, ret, boxspan(body)));
|
||||||
|
|
||||||
|
|
12
test.hlm
12
test.hlm
|
@ -1,11 +1 @@
|
||||||
let addi = fn x y = x + y;
|
(fun (x) -> x + 1)(68);
|
||||||
|
|
||||||
let factorial = fn x =
|
|
||||||
if x == 1
|
|
||||||
then x
|
|
||||||
else x * factorial(x - 1);
|
|
||||||
|
|
||||||
let result = factorial(addi(2, 3));
|
|
||||||
|
|
||||||
let println = fn x = ();
|
|
||||||
println(result);
|
|
18
test.ssa
18
test.ssa
|
@ -1,18 +0,0 @@
|
||||||
my_add x y:
|
|
||||||
0:
|
|
||||||
v0 = iadd x y
|
|
||||||
ret v0
|
|
||||||
|
|
||||||
factorial x:
|
|
||||||
0:
|
|
||||||
v0 = eq x 1
|
|
||||||
jf v0 1
|
|
||||||
ret x
|
|
||||||
1:
|
|
||||||
v0 = isub x 1
|
|
||||||
v1 = call factorial v0
|
|
||||||
v2 = imul x v1
|
|
||||||
ret v2
|
|
||||||
|
|
||||||
v0 = call my_add 2 3
|
|
||||||
v1 = call factorial v0
|
|
2
ty.hlm
Normal file
2
ty.hlm
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
let f = fn x y z = x + y + z in f(1);
|
||||||
|
let g = fn x = x + 1; g(1, 2);
|
|
@ -187,11 +187,23 @@ impl<'src> Infer<'src> {
|
||||||
(Func(a1, r1), Func(a2, r2)) => {
|
(Func(a1, r1), Func(a2, r2)) => {
|
||||||
// Check the number of arguments
|
// Check the number of arguments
|
||||||
if a1.len() != a2.len() {
|
if a1.len() != a2.len() {
|
||||||
return Err(InferError::new("Argument length mismatch", c.span)
|
let mut e = InferError::new("Argument length mismatch", c.span)
|
||||||
.add_error(format!(
|
.add_error(format!(
|
||||||
"This function should take {} arguments, found {}",
|
"This function is expected to take {} arguments, found {}",
|
||||||
a1.len(), a2.len()
|
a2.len(), a1.len()
|
||||||
), c.span));
|
), c.span);
|
||||||
|
if a2.len() > a1.len() {
|
||||||
|
// Get the types of the needed arguments
|
||||||
|
let mut args = Vec::new();
|
||||||
|
for i in a1.len()..a2.len() {
|
||||||
|
args.push(self.substitute(a2[i].clone()).to_string());
|
||||||
|
}
|
||||||
|
e = e.add_hint(format!(
|
||||||
|
"Need arguments of type `{}` to call this function",
|
||||||
|
args.join(", ")
|
||||||
|
), c.span);
|
||||||
|
}
|
||||||
|
return Err(e);
|
||||||
}
|
}
|
||||||
// Unify the arguments
|
// Unify the arguments
|
||||||
for (a1, a2) in a1.into_iter().zip(a2.into_iter()) {
|
for (a1, a2) in a1.into_iter().zip(a2.into_iter()) {
|
||||||
|
@ -231,11 +243,15 @@ impl<'src> Infer<'src> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Solve the constraints by unifying them
|
/// Solve the constraints by unifying them
|
||||||
fn solve(&mut self) -> Result<(), InferError> {
|
|
||||||
|
fn solve(&mut self) -> Vec<InferError> {
|
||||||
|
let mut errors = Vec::new();
|
||||||
for c in self.constraints.clone().into_iter() {
|
for c in self.constraints.clone().into_iter() {
|
||||||
self.unify(c)?;
|
if let Err(e) = self.unify(c) {
|
||||||
|
errors.push(e);
|
||||||
}
|
}
|
||||||
Ok(())
|
}
|
||||||
|
errors
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Substitute the type variables with the substitutions
|
/// Substitute the type variables with the substitutions
|
||||||
|
@ -394,10 +410,10 @@ impl<'src> Infer<'src> {
|
||||||
Type::Func(_, _) => "function",
|
Type::Func(_, _) => "function",
|
||||||
_ => "value",
|
_ => "value",
|
||||||
};
|
};
|
||||||
(
|
(TExpr::Ident(x), vec![
|
||||||
TExpr::Ident(x),
|
InferError::new(format!("Undefined {}", kind), span)
|
||||||
vec![InferError::new(format!("Undefined {}", kind), span)]
|
.add_error(format!("`{}` is not defined", x), span)
|
||||||
)
|
])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -620,6 +636,17 @@ impl<'src> Infer<'src> {
|
||||||
let (bt, berrs) = inf.infer(unbox!(body), expected.clone());
|
let (bt, berrs) = inf.infer(unbox!(body), expected.clone());
|
||||||
errs.extend(berrs);
|
errs.extend(berrs);
|
||||||
|
|
||||||
|
for s in inf.subst {
|
||||||
|
if !self.subst.contains(&s) {
|
||||||
|
self.subst.push(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for c in inf.constraints {
|
||||||
|
if !self.constraints.contains(&c) {
|
||||||
|
self.constraints.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
(TExpr::Let {
|
(TExpr::Let {
|
||||||
name, ty,
|
name, ty,
|
||||||
value: (Box::new(vt), value.1),
|
value: (Box::new(vt), value.1),
|
||||||
|
@ -688,29 +715,30 @@ impl<'src> Infer<'src> {
|
||||||
/// Infer a list of expressions
|
/// Infer a list of expressions
|
||||||
pub fn infer_exprs(es: Vec<(Expr, SimpleSpan)>) -> (Vec<(TExpr, SimpleSpan)>, Vec<InferError>) {
|
pub fn infer_exprs(es: Vec<(Expr, SimpleSpan)>) -> (Vec<(TExpr, SimpleSpan)>, Vec<InferError>) {
|
||||||
let mut inf = Infer::new();
|
let mut inf = Infer::new();
|
||||||
let mut typed_exprs = vec![];
|
// Type expressions
|
||||||
|
let mut tes = vec![];
|
||||||
|
// Unsubstituted typed expressions
|
||||||
let mut errors = vec![];
|
let mut errors = vec![];
|
||||||
|
|
||||||
for e in es {
|
for e in es {
|
||||||
let span = e.1;
|
let span = e.1;
|
||||||
let fresh = inf.fresh();
|
let fresh = inf.fresh();
|
||||||
|
// Infer the types
|
||||||
let (te, err) = inf.infer(e, fresh);
|
let (te, err) = inf.infer(e, fresh);
|
||||||
typed_exprs.push((te, span));
|
|
||||||
|
// Push the expression to the list
|
||||||
|
tes.push((te.clone(), span));
|
||||||
|
|
||||||
if !err.is_empty() {
|
if !err.is_empty() {
|
||||||
errors.extend(err);
|
errors.extend(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match inf.solve() {
|
let solve_errors = inf.solve();
|
||||||
Ok(_) => {
|
if !solve_errors.is_empty() {
|
||||||
typed_exprs = typed_exprs.into_iter()
|
errors.extend(solve_errors);
|
||||||
.map(|(x, s)| (inf.substitute_texp(x), s))
|
|
||||||
.collect();
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
errors.push(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
(rename_exprs(typed_exprs), errors)
|
|
||||||
|
(rename_exprs(tes), errors)
|
||||||
}
|
}
|
Loading…
Reference in a new issue