1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

Pipe operator

This commit is contained in:
azur 2023-05-01 01:00:06 +07:00
parent 8de2629269
commit 9a649db22a
8 changed files with 111 additions and 246 deletions

4
Cargo.lock generated
View file

@ -212,10 +212,6 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "ir"
version = "0.1.0"
[[package]] [[package]]
name = "is-terminal" name = "is-terminal"
version = "0.4.7" version = "0.4.7"

View file

@ -4,5 +4,4 @@ members = [
"bin", "bin",
"syntax", "syntax",
"typing", "typing",
"ir",
] ]

View file

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

View file

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

View file

@ -1,3 +1,4 @@
let mk = fn n = fn x = x + n; let succ = fn x = x + 1;
let f = mk(35); let add = fn a b = a + b;
let res = f(34);
let res = 10 |> succ |> fn a = add(a, 1) |> add;

View file

@ -16,6 +16,7 @@ pub enum Token<'src> {
Add, Sub, Mul, Div, Rem, Add, Sub, Mul, Div, Rem,
Eq, Ne, Lt, Gt, Le, Ge, Eq, Ne, Lt, Gt, Le, Ge,
And, Or, Not, And, Or, Not,
Pipe,
Assign, Comma, Colon, Semicolon, Assign, Comma, Colon, Semicolon,
Open(Delim), Close(Delim), Open(Delim), Close(Delim),
@ -47,6 +48,7 @@ impl<'src> Display for Token<'src> {
Token::And => write!(f, "&&"), Token::And => write!(f, "&&"),
Token::Or => write!(f, "||"), Token::Or => write!(f, "||"),
Token::Not => write!(f, "!"), Token::Not => write!(f, "!"),
Token::Pipe => write!(f, "|>"),
Token::Assign => write!(f, "="), Token::Assign => write!(f, "="),
Token::Comma => write!(f, ","), Token::Comma => write!(f, ","),
@ -103,6 +105,7 @@ pub enum BinaryOp {
Add, Sub, Mul, Div, Rem, Add, Sub, Mul, Div, Rem,
And, Or, And, Or,
Eq, Ne, Lt, Le, Gt, Ge, Eq, Ne, Lt, Le, Gt, Ge,
Pipe,
} }
impl Display for BinaryOp { impl Display for BinaryOp {
@ -121,6 +124,7 @@ impl Display for BinaryOp {
BinaryOp::Le => write!(f, "<="), BinaryOp::Le => write!(f, "<="),
BinaryOp::Gt => write!(f, ">"), BinaryOp::Gt => write!(f, ">"),
BinaryOp::Ge => write!(f, ">="), BinaryOp::Ge => write!(f, ">="),
BinaryOp::Pipe => write!(f, "|>"),
} }
} }
} }

View file

@ -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::Unit),
just("\\").to(Token::Lambda), just("\\").to(Token::Lambda),
just("->").to(Token::Arrow), just("->").to(Token::Arrow),
just("|>").to(Token::Pipe),
just('+').to(Token::Add), just('+').to(Token::Add),
just('-').to(Token::Sub), 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") .labelled("expression")
}) })
} }

View file

@ -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)] #[derive(Clone, Debug)]
struct Infer<'src> { struct Infer<'src> {
env: HashMap<&'src str, Type>, env: HashMap<&'src str, Type>,
subst: Vec<Type>, subst: Vec<Type>,
constraints: Vec<(Type, Type, SimpleSpan)>, constraints: Vec<Constraint>,
} }
impl<'src> Infer<'src> { impl<'src> Infer<'src> {
@ -86,8 +104,8 @@ impl<'src> Infer<'src> {
} }
/// Add new constraint /// Add new constraint
fn add_constraint(&mut self, t1: Type, t2: Type, span: SimpleSpan) { fn add_constraint(&mut self, c: Constraint) {
self.constraints.push((t1, t2, span)); self.constraints.push(c);
} }
/// Check if a type variable occurs in a type /// Check if a type variable occurs in a type
@ -112,9 +130,15 @@ impl<'src> Infer<'src> {
} }
/// Unify two types /// 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::*; use Type::*;
match (t1, t2) { match (c.t1.clone(), c.t2.clone()) {
// Literal types // Literal types
(Unit, Unit) (Unit, Unit)
| (Bool, Bool) | (Bool, Bool)
@ -128,15 +152,15 @@ impl<'src> Infer<'src> {
// unify the substitution with t2 // unify the substitution with t2
if let Some(t) = self.subst(i) { if let Some(t) = self.subst(i) {
if t != Var(i) { if t != Var(i) {
return self.unify(t, t2, s); return self.unify(constraint!(t, t2));
} }
} }
// If the variable occurs in t2 // If the variable occurs in t2
if self.occurs(i, t2.clone()) { if self.occurs(i, t2.clone()) {
return Err(InferError::new("Infinite type", s) return Err(InferError::new("Infinite type", c.span)
.add_error(format!( .add_error(format!(
"This type contains itself: {}", rename_type(Var(i)) "This type contains itself: {}", rename_type(Var(i))
), s)); ), c.span));
} }
// Set the substitution // Set the substitution
self.subst[i] = t2; self.subst[i] = t2;
@ -145,15 +169,15 @@ impl<'src> Infer<'src> {
(t1, Var(i)) => { (t1, Var(i)) => {
if let Some(t) = self.subst(i) { if let Some(t) = self.subst(i) {
if t != Var(i) { if t != Var(i) {
return self.unify(t1, t, s); return self.unify(constraint!(t1, t));
} }
} }
if self.occurs(i, t1.clone()) { if self.occurs(i, t1.clone()) {
return Err(InferError::new("Infinite type", s) return Err(InferError::new("Infinite type", c.span)
.add_error(format!( .add_error(format!(
"This type contains itself: {}", "This type contains itself: {}",
rename_type(Var(i)) rename_type(Var(i))
), s)); ), c.span));
} }
self.subst[i] = t1; self.subst[i] = t1;
Ok(()) Ok(())
@ -163,53 +187,53 @@ 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", s) return Err(InferError::new("Argument length mismatch", c.span)
.add_error(format!( .add_error(format!(
"Expected {} arguments, found {}", "This function should take {} arguments, found {}",
a1.len(), a2.len() a1.len(), a2.len()
), s)); ), c.span));
} }
// 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()) {
self.unify(a1, a2, s)?; self.unify(constraint!(a1, a2))?;
} }
// Unify the return types // Unify the return types
self.unify(*r1, *r2, s) self.unify(constraint!(*r1, *r2))
}, },
// Tuple // Tuple
(Tuple(t1), Tuple(t2)) => { (Tuple(t1), Tuple(t2)) => {
// Check the number of elements // Check the number of elements
if t1.len() != t2.len() { 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!( .add_error(format!(
"Expected {} elements, found {}", "Expected {} elements, found {}",
t1.len(), t2.len() t1.len(), t2.len()
), s)); ), c.span));
} }
// Unify the elements // Unify the elements
for (t1, t2) in t1.into_iter().zip(t2.into_iter()) { for (t1, t2) in t1.into_iter().zip(t2.into_iter()) {
self.unify(t1, t2, s)?; self.unify(constraint!(t1, t2))?;
} }
Ok(()) Ok(())
}, },
// Array // 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 // 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!( .add_error(format!(
"Expected {}, found {}", "Expected {}, found {}",
rename_type(t1), rename_type(t2) rename_type(t1), rename_type(t2)
), s)), ), c.span)),
} }
} }
/// Solve the constraints by unifying them /// Solve the constraints by unifying them
fn solve(&mut self) -> Result<(), InferError> { fn solve(&mut self) -> Result<(), InferError> {
for (t1, t2, span) in self.constraints.clone().into_iter() { for c in self.constraints.clone().into_iter() {
self.unify(t1, t2, span)?; self.unify(c)?;
} }
Ok(()) Ok(())
} }
@ -330,25 +354,31 @@ impl<'src> Infer<'src> {
&mut self, e: (Expr<'src>, SimpleSpan), expected: Type &mut self, e: (Expr<'src>, SimpleSpan), expected: Type
) -> (TExpr<'src>, Vec<InferError>) { ) -> (TExpr<'src>, Vec<InferError>) {
let span = e.1; let span = e.1;
macro_rules! constraint {
($ty:expr) => {
self.add_constraint(Constraint::new(expected, $ty, span))
};
}
match e.0 { match e.0 {
// Literal values // Literal values
// Push the constraint (expected type to be the literal type) and // Push the constraint (expected type to be the literal type) and
// return the typed expression // return the typed expression
Expr::Lit(l) => match l { Expr::Lit(l) => match l {
Lit::Unit => { Lit::Unit => {
self.add_constraint(expected, Type::Unit, span); constraint!(Type::Unit);
ok!(TExpr::Lit(Lit::Unit)) ok!(TExpr::Lit(Lit::Unit))
} }
Lit::Bool(b) => { Lit::Bool(b) => {
self.add_constraint(expected, Type::Bool, span); constraint!(Type::Bool);
ok!(TExpr::Lit(Lit::Bool(b))) ok!(TExpr::Lit(Lit::Bool(b)))
} }
Lit::Int(i) => { Lit::Int(i) => {
self.add_constraint(expected, Type::Int, span); constraint!(Type::Int);
ok!(TExpr::Lit(Lit::Int(i))) ok!(TExpr::Lit(Lit::Int(i)))
} }
Lit::Str(s) => { Lit::Str(s) => {
self.add_constraint(expected, Type::Str, span); constraint!(Type::Str);
ok!(TExpr::Lit(Lit::Str(s))) 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 // The same as literals but the type is looked up in the environment
Expr::Ident(ref x) => { Expr::Ident(ref x) => {
if let Some(t) = self.env.get(x) { if let Some(t) = self.env.get(x) {
self.add_constraint(expected, t.clone(), span); constraint!(t.clone());
ok!(TExpr::Ident(x)) ok!(TExpr::Ident(x))
} else { } else {
let kind = match &expected { let kind = match &expected {
@ -378,7 +408,7 @@ impl<'src> Infer<'src> {
// Numeric operators (Int -> Int) // Numeric operators (Int -> Int)
UnaryOp::Neg => { UnaryOp::Neg => {
let (te, err) = self.infer(unbox!(e), Type::Int); let (te, err) = self.infer(unbox!(e), Type::Int);
self.add_constraint(expected, Type::Int, span); constraint!(Type::Int);
(TExpr::Unary { (TExpr::Unary {
op, op,
expr: (Box::new(te), span), expr: (Box::new(te), span),
@ -388,7 +418,7 @@ impl<'src> Infer<'src> {
// Boolean operators (Bool -> Bool) // Boolean operators (Bool -> Bool)
UnaryOp::Not => { UnaryOp::Not => {
let (te, err) = self.infer(unbox!(e), Type::Bool); let (te, err) = self.infer(unbox!(e), Type::Bool);
self.add_constraint(expected, Type::Bool, span); constraint!(Type::Bool);
(TExpr::Unary { (TExpr::Unary {
op, op,
expr: (Box::new(te), span), expr: (Box::new(te), span),
@ -407,7 +437,7 @@ impl<'src> Infer<'src> {
let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Int); let (lt, mut errs0) = self.infer(unbox!(lhs), Type::Int);
let (rt, errs1) = self.infer(unbox!(rhs), Type::Int); let (rt, errs1) = self.infer(unbox!(rhs), Type::Int);
errs0.extend(errs1); errs0.extend(errs1);
self.add_constraint(expected, Type::Int, span); constraint!(Type::Int);
(TExpr::Binary { (TExpr::Binary {
op, op,
lhs: (Box::new(lt), lhs.1), 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 (lt, mut errs0) = self.infer(unbox!(lhs), Type::Bool);
let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool); let (rt, errs1) = self.infer(unbox!(rhs), Type::Bool);
errs0.extend(errs1); errs0.extend(errs1);
self.add_constraint(expected, Type::Bool, span); constraint!(Type::Bool);
(TExpr::Binary { (TExpr::Binary {
op, op,
lhs: (Box::new(lt), lhs.1), 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 (lt, mut errs0) = self.infer(unbox!(lhs), t.clone());
let (rt, errs1) = self.infer(unbox!(rhs), t); let (rt, errs1) = self.infer(unbox!(rhs), t);
errs0.extend(errs1); errs0.extend(errs1);
self.add_constraint(expected, Type::Bool, span); constraint!(Type::Bool);
(TExpr::Binary { (TExpr::Binary {
op, op,
lhs: (Box::new(lt), lhs.1), lhs: (Box::new(lt), lhs.1),
@ -453,6 +483,27 @@ impl<'src> Infer<'src> {
ret_ty: Type::Bool, ret_ty: Type::Bool,
}, errs0) }, 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 // Lambda
@ -486,12 +537,12 @@ impl<'src> Infer<'src> {
} }
// Push the constraints // Push the constraints
self.add_constraint(expected, Type::Func( constraint!(Type::Func(
xs.clone().into_iter() xs.clone().into_iter()
.map(|x| x.1) .map(|x| x.1)
.collect(), .collect(),
Box::new(rt.clone()), Box::new(rt.clone()),
), span); ));
(TExpr::Lambda { (TExpr::Lambda {
params: xs, params: xs,
@ -580,7 +631,7 @@ impl<'src> Infer<'src> {
self.env.insert(name.clone(), ty.clone()); self.env.insert(name.clone(), ty.clone());
let (val_ty, errs) = self.infer(unbox!(value), 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 { (TExpr::Define {
name, name,
@ -615,11 +666,12 @@ impl<'src> Infer<'src> {
let rt = if void || last.is_none() { let rt = if void || last.is_none() {
// If the block is void or there is no expression, // If the block is void or there is no expression,
// the return type is unit // the return type is unit
self.add_constraint(expected, Type::Unit, span); constraint!(Type::Unit);
Type::Unit Type::Unit
} else { } else {
// Otherwise, the return type is the same as the expected type // 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 expected
}; };