From 9a649db22a470d2404d89bcf41e6fe5a9d91ec22 Mon Sep 17 00:00:00 2001 From: azur Date: Mon, 1 May 2023 01:00:06 +0700 Subject: [PATCH] Pipe operator --- Cargo.lock | 4 - Cargo.toml | 1 - ir/Cargo.toml | 8 -- ir/src/lib.rs | 190 ------------------------------------------- simple.hlm | 7 +- syntax/src/expr.rs | 4 + syntax/src/parser.rs | 13 ++- typing/src/infer.rs | 130 ++++++++++++++++++++--------- 8 files changed, 111 insertions(+), 246 deletions(-) delete mode 100644 ir/Cargo.toml delete mode 100644 ir/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index ade12bc..177147d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,10 +212,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "ir" -version = "0.1.0" - [[package]] name = "is-terminal" version = "0.4.7" diff --git a/Cargo.toml b/Cargo.toml index 2384fd5..4a07dc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,5 +4,4 @@ members = [ "bin", "syntax", "typing", - "ir", ] \ No newline at end of file diff --git a/ir/Cargo.toml b/ir/Cargo.toml deleted file mode 100644 index d3ea0de..0000000 --- a/ir/Cargo.toml +++ /dev/null @@ -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] diff --git a/ir/src/lib.rs b/ir/src/lib.rs deleted file mode 100644 index 3af472c..0000000 --- a/ir/src/lib.rs +++ /dev/null @@ -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), -} - -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(IRExpr<'src>), - Block { - id: usize, - body: Vec, - }, - Func { - name: &'src str, - args: Vec<&'src str>, - body: Vec, - }, -} - -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)); - } -} \ No newline at end of file diff --git a/simple.hlm b/simple.hlm index 8686804..79dadc7 100644 --- a/simple.hlm +++ b/simple.hlm @@ -1,3 +1,4 @@ -let mk = fn n = fn x = x + n; -let f = mk(35); -let res = f(34); \ No newline at end of file +let succ = fn x = x + 1; +let add = fn a b = a + b; + +let res = 10 |> succ |> fn a = add(a, 1) |> add; \ No newline at end of file diff --git a/syntax/src/expr.rs b/syntax/src/expr.rs index e311f2c..22fae5b 100644 --- a/syntax/src/expr.rs +++ b/syntax/src/expr.rs @@ -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, "|>"), } } } diff --git a/syntax/src/parser.rs b/syntax/src/parser.rs index 2b2384c..0c7bc01 100644 --- a/syntax/src/parser.rs +++ b/syntax/src/parser.rs @@ -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") }) } diff --git a/typing/src/infer.rs b/typing/src/infer.rs index fb08724..341cfcc 100644 --- a/typing/src/infer.rs +++ b/typing/src/infer.rs @@ -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, - constraints: Vec<(Type, Type, SimpleSpan)>, + constraints: Vec, } 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) { 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 };