mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
Pipe operator
This commit is contained in:
parent
8de2629269
commit
9a649db22a
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -212,10 +212,6 @@ dependencies = [
|
|||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ir"
|
||||
version = "0.1.0"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.7"
|
||||
|
|
|
@ -4,5 +4,4 @@ members = [
|
|||
"bin",
|
||||
"syntax",
|
||||
"typing",
|
||||
"ir",
|
||||
]
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "ir"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
190
ir/src/lib.rs
190
ir/src/lib.rs
|
@ -1,190 +0,0 @@
|
|||
use std::fmt::{Display, Formatter, Result as FmtResult};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum IRExpr<'src> {
|
||||
Int(i64),
|
||||
Var(&'src str),
|
||||
Call(&'src str, Vec<Self>),
|
||||
}
|
||||
|
||||
impl Display for IRExpr<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
match self {
|
||||
IRExpr::Int(x) => write!(f, "{x}"),
|
||||
IRExpr::Var(x) => write!(f, "{x}"),
|
||||
IRExpr::Call(name, args) => {
|
||||
write!(f, "{name}(")?;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 { write!(f, ", ")?; }
|
||||
write!(f, "{arg}")?;
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum IR<'src> {
|
||||
Define {
|
||||
name: &'src str,
|
||||
value: Box<IRExpr<'src>>,
|
||||
},
|
||||
IRExpr(IRExpr<'src>),
|
||||
Block {
|
||||
id: usize,
|
||||
body: Vec<Self>,
|
||||
},
|
||||
Func {
|
||||
name: &'src str,
|
||||
args: Vec<&'src str>,
|
||||
body: Vec<Self>,
|
||||
},
|
||||
}
|
||||
|
||||
fn display_ir(ir: &IR, indent: usize) -> String {
|
||||
let mut s = String::new();
|
||||
for _ in 0..indent { s.push(' '); }
|
||||
match ir {
|
||||
IR::Define { name, value } => s.push_str(&format!("{name} = {value}")),
|
||||
IR::IRExpr(expr) => s.push_str(&format!("{expr}")),
|
||||
IR::Block { id, body } => {
|
||||
s.push_str(&format!("{id}:\n"));
|
||||
for ir in body {
|
||||
s.push_str(&display_ir(ir, indent + 4));
|
||||
s.push_str("\n");
|
||||
}
|
||||
},
|
||||
IR::Func { name, args, body } => {
|
||||
s.push_str(&format!("{name} "));
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if i > 0 { s.push_str(" "); }
|
||||
s.push_str(&format!("{arg}"));
|
||||
}
|
||||
s.push_str(":\n");
|
||||
for ir in body {
|
||||
s.push_str(&display_ir(ir, indent + 4));
|
||||
s.push_str("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
impl Display for IR<'_> {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
|
||||
write!(f, "{}", display_ir(self, 0))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{
|
||||
IR::*,
|
||||
IRExpr::*
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_ir() {
|
||||
let fns = [
|
||||
Func {
|
||||
name: "my_add",
|
||||
args: vec!["a", "b"],
|
||||
body: vec![
|
||||
Block {
|
||||
id: 0,
|
||||
body: vec![
|
||||
Define {
|
||||
name: "v0",
|
||||
value: Call(
|
||||
"add",
|
||||
vec![
|
||||
Var("a"),
|
||||
Var("b"),
|
||||
]
|
||||
).into(),
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
},
|
||||
Func {
|
||||
name: "factorial",
|
||||
args: vec!["n"],
|
||||
body: vec![
|
||||
Block {
|
||||
id: 0,
|
||||
body: vec![
|
||||
Define {
|
||||
name: "v0",
|
||||
value: Call(
|
||||
"eq",
|
||||
vec![
|
||||
Var("n"),
|
||||
Int(1),
|
||||
]
|
||||
).into(),
|
||||
},
|
||||
IRExpr(Call(
|
||||
"jf",
|
||||
vec![
|
||||
Var("v0"),
|
||||
Int(1),
|
||||
]
|
||||
)),
|
||||
IRExpr(Call(
|
||||
"ret",
|
||||
vec![
|
||||
Var("n"),
|
||||
]
|
||||
)),
|
||||
]
|
||||
},
|
||||
Block {
|
||||
id: 1,
|
||||
body: vec![
|
||||
Define {
|
||||
name: "v0",
|
||||
value: Call(
|
||||
"isub",
|
||||
vec![
|
||||
Var("n"),
|
||||
Int(1),
|
||||
]
|
||||
).into(),
|
||||
},
|
||||
Define {
|
||||
name: "v1",
|
||||
value: Call(
|
||||
"call",
|
||||
vec![
|
||||
Var("factorial"),
|
||||
Var("v0"),
|
||||
]
|
||||
).into(),
|
||||
},
|
||||
Define {
|
||||
name: "v2",
|
||||
value: Call(
|
||||
"imul",
|
||||
vec![
|
||||
Var("n"),
|
||||
Var("v1"),
|
||||
]
|
||||
).into(),
|
||||
},
|
||||
IRExpr(Call(
|
||||
"ret",
|
||||
vec![
|
||||
Var("v2"),
|
||||
]
|
||||
)),
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
fns.iter().for_each(|ir| println!("{}", ir));
|
||||
}
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
let mk = fn n = fn x = x + n;
|
||||
let f = mk(35);
|
||||
let res = f(34);
|
||||
let succ = fn x = x + 1;
|
||||
let add = fn a b = a + b;
|
||||
|
||||
let res = 10 |> succ |> fn a = add(a, 1) |> add;
|
|
@ -16,6 +16,7 @@ pub enum Token<'src> {
|
|||
Add, Sub, Mul, Div, Rem,
|
||||
Eq, Ne, Lt, Gt, Le, Ge,
|
||||
And, Or, Not,
|
||||
Pipe,
|
||||
|
||||
Assign, Comma, Colon, Semicolon,
|
||||
Open(Delim), Close(Delim),
|
||||
|
@ -47,6 +48,7 @@ impl<'src> Display for Token<'src> {
|
|||
Token::And => write!(f, "&&"),
|
||||
Token::Or => write!(f, "||"),
|
||||
Token::Not => write!(f, "!"),
|
||||
Token::Pipe => write!(f, "|>"),
|
||||
|
||||
Token::Assign => write!(f, "="),
|
||||
Token::Comma => write!(f, ","),
|
||||
|
@ -103,6 +105,7 @@ pub enum BinaryOp {
|
|||
Add, Sub, Mul, Div, Rem,
|
||||
And, Or,
|
||||
Eq, Ne, Lt, Le, Gt, Ge,
|
||||
Pipe,
|
||||
}
|
||||
|
||||
impl Display for BinaryOp {
|
||||
|
@ -121,6 +124,7 @@ impl Display for BinaryOp {
|
|||
BinaryOp::Le => write!(f, "<="),
|
||||
BinaryOp::Gt => write!(f, ">"),
|
||||
BinaryOp::Ge => write!(f, ">="),
|
||||
BinaryOp::Pipe => write!(f, "|>"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Vec<(Token<'src>, Span)>, e
|
|||
just("()").to(Token::Unit),
|
||||
just("\\").to(Token::Lambda),
|
||||
just("->").to(Token::Arrow),
|
||||
just("|>").to(Token::Pipe),
|
||||
|
||||
just('+').to(Token::Add),
|
||||
just('-').to(Token::Sub),
|
||||
|
@ -324,7 +325,17 @@ pub fn expr_parser<'tokens, 'src: 'tokens>() -> impl Parser<
|
|||
}
|
||||
);
|
||||
|
||||
logical
|
||||
let pipe = logical.clone()
|
||||
.foldl(
|
||||
just(Token::Pipe).to(BinaryOp::Pipe)
|
||||
.then(logical).repeated(),
|
||||
|a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::Binary(op, boxspan(a), boxspan(b)), span.into())
|
||||
}
|
||||
);
|
||||
|
||||
pipe
|
||||
.labelled("expression")
|
||||
})
|
||||
}
|
||||
|
|
|
@ -57,11 +57,29 @@ impl InferError {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
struct Constraint {
|
||||
t1: Type,
|
||||
t2: Type,
|
||||
// Where the constraint was generated, for error reporting
|
||||
span: SimpleSpan,
|
||||
}
|
||||
|
||||
impl Constraint {
|
||||
fn new(t1: Type, t2: Type, span: SimpleSpan) -> Self {
|
||||
Self {
|
||||
t1,
|
||||
t2,
|
||||
span,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Infer<'src> {
|
||||
env: HashMap<&'src str, Type>,
|
||||
subst: Vec<Type>,
|
||||
constraints: Vec<(Type, Type, SimpleSpan)>,
|
||||
constraints: Vec<Constraint>,
|
||||
}
|
||||
|
||||
impl<'src> Infer<'src> {
|
||||
|
@ -86,8 +104,8 @@ impl<'src> Infer<'src> {
|
|||
}
|
||||
|
||||
/// Add new constraint
|
||||
fn add_constraint(&mut self, t1: Type, t2: Type, span: SimpleSpan) {
|
||||
self.constraints.push((t1, t2, span));
|
||||
fn add_constraint(&mut self, c: Constraint) {
|
||||
self.constraints.push(c);
|
||||
}
|
||||
|
||||
/// Check if a type variable occurs in a type
|
||||
|
@ -112,9 +130,15 @@ impl<'src> Infer<'src> {
|
|||
}
|
||||
|
||||
/// Unify two types
|
||||
fn unify(&mut self, t1: Type, t2: Type, s: SimpleSpan) -> Result<(), InferError> {
|
||||
fn unify(&mut self, c: Constraint) -> Result<(), InferError> {
|
||||
macro_rules! constraint {
|
||||
($t1:expr, $t2:expr) => {
|
||||
Constraint::new($t1, $t2, c.span)
|
||||
};
|
||||
}
|
||||
|
||||
use Type::*;
|
||||
match (t1, t2) {
|
||||
match (c.t1.clone(), c.t2.clone()) {
|
||||
// Literal types
|
||||
(Unit, Unit)
|
||||
| (Bool, Bool)
|
||||
|
@ -128,15 +152,15 @@ impl<'src> Infer<'src> {
|
|||
// unify the substitution with t2
|
||||
if let Some(t) = self.subst(i) {
|
||||
if t != Var(i) {
|
||||
return self.unify(t, t2, s);
|
||||
return self.unify(constraint!(t, t2));
|
||||
}
|
||||
}
|
||||
// If the variable occurs in t2
|
||||
if self.occurs(i, t2.clone()) {
|
||||
return Err(InferError::new("Infinite type", s)
|
||||
return Err(InferError::new("Infinite type", c.span)
|
||||
.add_error(format!(
|
||||
"This type contains itself: {}", rename_type(Var(i))
|
||||
), s));
|
||||
), c.span));
|
||||
}
|
||||
// Set the substitution
|
||||
self.subst[i] = t2;
|
||||
|
@ -145,15 +169,15 @@ impl<'src> Infer<'src> {
|
|||
(t1, Var(i)) => {
|
||||
if let Some(t) = self.subst(i) {
|
||||
if t != Var(i) {
|
||||
return self.unify(t1, t, s);
|
||||
return self.unify(constraint!(t1, t));
|
||||
}
|
||||
}
|
||||
if self.occurs(i, t1.clone()) {
|
||||
return Err(InferError::new("Infinite type", s)
|
||||
return Err(InferError::new("Infinite type", c.span)
|
||||
.add_error(format!(
|
||||
"This type contains itself: {}",
|
||||
rename_type(Var(i))
|
||||
), s));
|
||||
), c.span));
|
||||
}
|
||||
self.subst[i] = t1;
|
||||
Ok(())
|
||||
|
@ -163,53 +187,53 @@ impl<'src> Infer<'src> {
|
|||
(Func(a1, r1), Func(a2, r2)) => {
|
||||
// Check the number of arguments
|
||||
if a1.len() != a2.len() {
|
||||
return Err(InferError::new("Argument length mismatch", s)
|
||||
return Err(InferError::new("Argument length mismatch", c.span)
|
||||
.add_error(format!(
|
||||
"Expected {} arguments, found {}",
|
||||
"This function should take {} arguments, found {}",
|
||||
a1.len(), a2.len()
|
||||
), s));
|
||||
), c.span));
|
||||
}
|
||||
// Unify the arguments
|
||||
for (a1, a2) in a1.into_iter().zip(a2.into_iter()) {
|
||||
self.unify(a1, a2, s)?;
|
||||
self.unify(constraint!(a1, a2))?;
|
||||
}
|
||||
// Unify the return types
|
||||
self.unify(*r1, *r2, s)
|
||||
self.unify(constraint!(*r1, *r2))
|
||||
},
|
||||
|
||||
// Tuple
|
||||
(Tuple(t1), Tuple(t2)) => {
|
||||
// Check the number of elements
|
||||
if t1.len() != t2.len() {
|
||||
return Err(InferError::new("Tuple length mismatch", s)
|
||||
return Err(InferError::new("Tuple length mismatch", c.span)
|
||||
.add_error(format!(
|
||||
"Expected {} elements, found {}",
|
||||
t1.len(), t2.len()
|
||||
), s));
|
||||
), c.span));
|
||||
}
|
||||
// Unify the elements
|
||||
for (t1, t2) in t1.into_iter().zip(t2.into_iter()) {
|
||||
self.unify(t1, t2, s)?;
|
||||
self.unify(constraint!(t1, t2))?;
|
||||
}
|
||||
Ok(())
|
||||
},
|
||||
|
||||
// Array
|
||||
(Array(t1), Array(t2)) => self.unify(*t1, *t2, s),
|
||||
(Array(t1), Array(t2)) => self.unify(constraint!(*t1, *t2)),
|
||||
|
||||
// The rest will be type mismatch
|
||||
(t1, t2) => Err(InferError::new("Type mismatch", s)
|
||||
(t1, t2) => Err(InferError::new("Type mismatch", c.span)
|
||||
.add_error(format!(
|
||||
"Expected {}, found {}",
|
||||
rename_type(t1), rename_type(t2)
|
||||
), s)),
|
||||
), c.span)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Solve the constraints by unifying them
|
||||
fn solve(&mut self) -> Result<(), InferError> {
|
||||
for (t1, t2, span) in self.constraints.clone().into_iter() {
|
||||
self.unify(t1, t2, span)?;
|
||||
for c in self.constraints.clone().into_iter() {
|
||||
self.unify(c)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -330,25 +354,31 @@ impl<'src> Infer<'src> {
|
|||
&mut self, e: (Expr<'src>, SimpleSpan), expected: Type
|
||||
) -> (TExpr<'src>, Vec<InferError>) {
|
||||
let span = e.1;
|
||||
macro_rules! constraint {
|
||||
($ty:expr) => {
|
||||
self.add_constraint(Constraint::new(expected, $ty, span))
|
||||
};
|
||||
}
|
||||
|
||||
match e.0 {
|
||||
// Literal values
|
||||
// Push the constraint (expected type to be the literal type) and
|
||||
// return the typed expression
|
||||
Expr::Lit(l) => match l {
|
||||
Lit::Unit => {
|
||||
self.add_constraint(expected, Type::Unit, span);
|
||||
constraint!(Type::Unit);
|
||||
ok!(TExpr::Lit(Lit::Unit))
|
||||
}
|
||||
Lit::Bool(b) => {
|
||||
self.add_constraint(expected, Type::Bool, span);
|
||||
constraint!(Type::Bool);
|
||||
ok!(TExpr::Lit(Lit::Bool(b)))
|
||||
}
|
||||
Lit::Int(i) => {
|
||||
self.add_constraint(expected, Type::Int, span);
|
||||
constraint!(Type::Int);
|
||||
ok!(TExpr::Lit(Lit::Int(i)))
|
||||
}
|
||||
Lit::Str(s) => {
|
||||
self.add_constraint(expected, Type::Str, span);
|
||||
constraint!(Type::Str);
|
||||
ok!(TExpr::Lit(Lit::Str(s)))
|
||||
}
|
||||
}
|
||||
|
@ -357,7 +387,7 @@ impl<'src> Infer<'src> {
|
|||
// The same as literals but the type is looked up in the environment
|
||||
Expr::Ident(ref x) => {
|
||||
if let Some(t) = self.env.get(x) {
|
||||
self.add_constraint(expected, t.clone(), span);
|
||||
constraint!(t.clone());
|
||||
ok!(TExpr::Ident(x))
|
||||
} else {
|
||||
let kind = match &expected {
|
||||
|
@ -378,7 +408,7 @@ impl<'src> Infer<'src> {
|
|||
// Numeric operators (Int -> Int)
|
||||
UnaryOp::Neg => {
|
||||
let (te, err) = self.infer(unbox!(e), Type::Int);
|
||||
self.add_constraint(expected, Type::Int, span);
|
||||
constraint!(Type::Int);
|
||||
(TExpr::Unary {
|
||||
op,
|
||||
expr: (Box::new(te), span),
|
||||
|
@ -388,7 +418,7 @@ impl<'src> Infer<'src> {
|
|||
// Boolean operators (Bool -> Bool)
|
||||
UnaryOp::Not => {
|
||||
let (te, err) = self.infer(unbox!(e), Type::Bool);
|
||||
self.add_constraint(expected, Type::Bool, span);
|
||||
constraint!(Type::Bool);
|
||||
(TExpr::Unary {
|
||||
op,
|
||||
expr: (Box::new(te), span),
|
||||
|
@ -407,7 +437,7 @@ impl<'src> Infer<'src> {
|
|||
let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Int);
|
||||
let (rt, errs1) = self.infer(unbox!(rhs), Type::Int);
|
||||
errs0.extend(errs1);
|
||||
self.add_constraint(expected, Type::Int, span);
|
||||
constraint!(Type::Int);
|
||||
(TExpr::Binary {
|
||||
op,
|
||||
lhs: (Box::new(lt), lhs.1),
|
||||
|
@ -422,7 +452,7 @@ impl<'src> Infer<'src> {
|
|||
let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Bool);
|
||||
let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool);
|
||||
errs0.extend(errs1);
|
||||
self.add_constraint(expected, Type::Bool, span);
|
||||
constraint!(Type::Bool);
|
||||
(TExpr::Binary {
|
||||
op,
|
||||
lhs: (Box::new(lt), lhs.1),
|
||||
|
@ -445,7 +475,7 @@ impl<'src> Infer<'src> {
|
|||
let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone());
|
||||
let (rt, errs1) = self.infer(unbox!(rhs), t);
|
||||
errs0.extend(errs1);
|
||||
self.add_constraint(expected, Type::Bool, span);
|
||||
constraint!(Type::Bool);
|
||||
(TExpr::Binary {
|
||||
op,
|
||||
lhs: (Box::new(lt), lhs.1),
|
||||
|
@ -453,6 +483,27 @@ impl<'src> Infer<'src> {
|
|||
ret_ty: Type::Bool,
|
||||
}, errs0)
|
||||
},
|
||||
|
||||
BinaryOp::Pipe => {
|
||||
// Since this is parsed with a fold left, the right hand
|
||||
// side should always be a function
|
||||
let t = self.fresh();
|
||||
let (lt, mut errs0) = self.infer(unbox!(lhs), t.clone());
|
||||
// The right hand side should be a function that takes
|
||||
// 1 argument with the type of t
|
||||
let (rt, errs1) = self.infer(
|
||||
unbox!(rhs),
|
||||
Type::Func(vec![t.clone()], Box::new(t.clone())),
|
||||
);
|
||||
errs0.extend(errs1);
|
||||
constraint!(t.clone());
|
||||
(TExpr::Binary {
|
||||
op,
|
||||
lhs: (Box::new(lt), lhs.1),
|
||||
rhs: (Box::new(rt), rhs.1),
|
||||
ret_ty: t,
|
||||
}, errs0)
|
||||
},
|
||||
}
|
||||
|
||||
// Lambda
|
||||
|
@ -486,12 +537,12 @@ impl<'src> Infer<'src> {
|
|||
}
|
||||
|
||||
// Push the constraints
|
||||
self.add_constraint(expected, Type::Func(
|
||||
constraint!(Type::Func(
|
||||
xs.clone().into_iter()
|
||||
.map(|x| x.1)
|
||||
.collect(),
|
||||
Box::new(rt.clone()),
|
||||
), span);
|
||||
));
|
||||
|
||||
(TExpr::Lambda {
|
||||
params: xs,
|
||||
|
@ -580,7 +631,7 @@ impl<'src> Infer<'src> {
|
|||
self.env.insert(name.clone(), ty.clone());
|
||||
let (val_ty, errs) = self.infer(unbox!(value), ty.clone());
|
||||
|
||||
self.constraints.push((expected, Type::Unit, e.1));
|
||||
constraint!(Type::Unit);
|
||||
|
||||
(TExpr::Define {
|
||||
name,
|
||||
|
@ -615,11 +666,12 @@ impl<'src> Infer<'src> {
|
|||
let rt = if void || last.is_none() {
|
||||
// If the block is void or there is no expression,
|
||||
// the return type is unit
|
||||
self.add_constraint(expected, Type::Unit, span);
|
||||
constraint!(Type::Unit);
|
||||
Type::Unit
|
||||
} else {
|
||||
// Otherwise, the return type is the same as the expected type
|
||||
self.add_constraint(expected.clone(), last.unwrap(), span);
|
||||
// constraint!(last.unwrap());
|
||||
self.add_constraint(Constraint::new(expected.clone(), last.unwrap(), span));
|
||||
expected
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in a new issue