diff --git a/crates/codegen/src/cpp.rs b/crates/codegen/src/cpp.rs index 172d9e0..e42795e 100644 --- a/crates/codegen/src/cpp.rs +++ b/crates/codegen/src/cpp.rs @@ -3,9 +3,9 @@ use std::fmt::Display; use hir::{IR, IRKind, Value}; const MODULE_INCLUDES: [&str; 3] = [ - "", - "", - "", + "", // stdin `@read()` and stdout `@write()` + "", // bool type + "", // string type ]; pub struct Codegen { @@ -28,47 +28,91 @@ impl Codegen { self.emit(format!("#include {}\n", module)); } for ir in irs { - self.emit(&self.gen_ir(&ir.kind)); + self.emit(&self.gen_ir(&ir.kind, true)); } } - fn gen_ir(&self, ir: &IRKind) -> String { + fn gen_ir(&self, ir: &IRKind, should_gen_semicolon: bool) -> String { + #[macro_export] + macro_rules! semicolon { () => { if should_gen_semicolon { ";" } else { "" } }; } match ir { IRKind::Define { name, type_hint, value } => { - format!("{} {} = {};\n", type_hint, name, self.gen_ir(value)) + format!( + "{} {} = {}{}\n", + type_hint, + name, + self.gen_ir(value, false), + semicolon!() + ) }, + IRKind::Call { name, args } => { - format!("{}({});\n", name, args.iter().map(|arg| self.gen_ir(arg)).collect::>().join(", ")) + format!( + "{}({}){}", + name, + args + .iter() + .map(|arg| self.gen_ir(arg, false)) + .collect::>() + .join(", ") + .trim_end_matches(";\n"), + semicolon!(), + ) }, + IRKind::Intrinsic { name, args } => { match name.as_str() { - "write" => { format!("std::cout << {};\n", self.gen_ir(&args[0])) }, - "read" => { format!("std::cin >> {};\n", self.gen_ir(&args[0])) }, + "write" => { format!("std::cout << {};\n", self.gen_ir(&args[0], false)) }, + "read" => { format!("std::cin >> {};\n", self.gen_ir(&args[0], false)) }, _ => unreachable!(format!("Unknown intrinsic: {}", name)) // Shoul be handled by lowering } - } + }, + IRKind::Fun { name, return_type_hint, args, body } => { - let args = args.iter().map(|arg| format!("{} {}", arg.1, arg.0)).collect::>().join(", "); - format!("{} {}({}) {{\n{};\n}}\n", return_type_hint, name, args, self.gen_ir(body)) + let args = args + .iter() + .map(|arg| format!("{} {}", arg.1, arg.0)) + .collect::>(). + join(", "); + format!( + "{} {}({}) {{\n{}\n}}\n", + return_type_hint, + name, + args, + self.gen_ir(body, false) + ) }, + IRKind::Return { value } => { - format!("return {};\n", self.gen_ir(value)) + format!( + "return {};\n", + self.gen_ir(value, false) + ) }, + IRKind::Do { body } => { let mut out = String::new(); for expr in body { - out.push_str(&self.gen_ir(&expr)); + out.push_str(&self.gen_ir(&expr, true)); } out }, + IRKind::If { cond, body, else_body } => { - format!("if ({}) {{\n{}}} else {{\n{}}}\n", self.gen_ir(cond), self.gen_ir(body), self.gen_ir(else_body)) + format!( + "if ({}) {{\n{}}} else {{\n{}}}\n", + self.gen_ir(cond, true), + self.gen_ir(body, true), + self.gen_ir(else_body, true), + ) }, + IRKind::Unary { op, right } => { - format!("{}{}", op, self.gen_ir(right)) + format!("{}{}", op, self.gen_ir(right, false)) }, + IRKind::Binary { left, op, right } => { - format!("{} {} {}", self.gen_ir(left), op, self.gen_ir(right)) + format!("{} {} {}", self.gen_ir(left, false), op, self.gen_ir(right, false)) }, IRKind::Value { value } => { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index a12fbfc..4993bf9 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1,7 +1,10 @@ use std::ops::Range; use parser::Expr; -const INTRINSICS: [&str; 2] = ["write", "read"]; +const INTRINSICS: [&str; 2] = [ + "write", + "read", +]; #[derive(Debug)] pub enum Value { Int(i64), Boolean(bool), String(String), Ident(String) } @@ -97,6 +100,41 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { return (Some(ir_kind), None); }, + Expr::Pipe { lhs, rhs } => { + let lhs_ir = expr_to_ir(&lhs.0); + if_err_return!(lhs_ir.1); + + match &rhs.0 { + call @ Expr::Call { name, args } => { + let name = match &name.0 { + Expr::Identifier(s) => s.clone(), + // Should never happen because the parser should have caught this + _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string(), note: None })) + }; + + let call = expr_to_ir(&call); + if_err_return!(call.1); + + let mut largs = Vec::new(); + for arg in &args.0 { + let arg = expr_to_ir(&arg.0); + if_err_return!(arg.1); + largs.push(arg.0.unwrap()); + } + + let mut args = vec![lhs_ir.0.unwrap()]; + args.append(&mut largs); + + return (Some(IRKind::Call { name, args }), None); + }, + _ => return (None, Some(LoweringError { + span: rhs.1.clone(), + message: "Expected call".to_string(), + note: None + })), + }; + }, + Expr::Let { name, type_hint, value } => { let value = expr_to_ir(&value.0); if_err_return!(value.1); diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index 4862997..d434335 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -15,6 +15,7 @@ pub enum Token { // Operators Plus, Minus, Multiply, Divide, Not, Equal, NotEqual, Less, Greater, + Pipe, // Symbols & Delimiters Assign, @@ -50,6 +51,7 @@ impl std::fmt::Display for Token { Token::NotEqual => write!(f, "!="), Token::Less => write!(f, "<"), Token::Greater => write!(f, ">"), + Token::Pipe => write!(f, "|>"), Token::Assign => write!(f, "="), Token::Dot => write!(f, "."), @@ -79,10 +81,15 @@ pub fn lexer() -> impl Parser, Error = Simple> { just('-').to(Token::Minus), just('*').to(Token::Multiply), just('/').to(Token::Divide), + just('!').to(Token::Not), just("==").to(Token::Equal), + + just("|>").to(Token::Pipe), + just('<').to(Token::Less), just('>').to(Token::Greater), + just('=').to(Token::Assign), just('.').to(Token::Dot), just(',').to(Token::Comma), diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index c1b89b5..94ed125 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -40,7 +40,7 @@ fn main() { match args.options { Options::Compile { input: file_name, - ast: _print_ast, + ast: print_ast, log: should_log, output, } => { @@ -73,12 +73,16 @@ fn main() { logif!(0, format!("Parsing took {}ms", start.elapsed().as_millis())); } + if print_ast { log(0, format!("{:#?}", ast)); } + match ast { Some(ast) => { // Convert the AST to HIR let (ir, lowering_error) = ast_to_ir(ast); for err in lowering_error { diagnostics.add_lowering_error(err); } + if print_ast { log(0, format!("{:#?}", ir)); } + // Report lowering errors if any if diagnostics.has_error() { diagnostics.display(src); diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 233eafd..f148630 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -11,6 +11,7 @@ pub enum Expr { Unary { op: String, rhs: Box> }, Binary { lhs: Box>, op: String, rhs: Box> }, Call { name: Box>, args: Spanned>> }, + Pipe { lhs: Box>, rhs: Box> }, Intrinsic { name: Box>, args: Spanned>> }, Let { @@ -171,6 +172,21 @@ fn expr_parser() -> impl Parser>, Error = Simple ) }); + let pipe = compare.clone() + .then( + just(Token::Pipe) + .then(compare) + .repeated()) + .foldl(|lhs, (_, rhs)| { + ( + Expr::Pipe { + lhs: Box::new(lhs), + rhs: Box::new(rhs.clone()), + }, + rhs.1, + ) + }); + let let_ = just(Token::KwLet) .ignore_then(identifier) .then_ignore(just(Token::Colon)) @@ -272,7 +288,7 @@ fn expr_parser() -> impl Parser>, Error = Simple .or(return_) .or(do_block) .or(if_block) - .or(compare) + .or(pipe) }).labelled("expression"); expr diff --git a/example/hello_world.hz b/example/hello_world.hz index 83fef3f..fd2daf2 100644 --- a/example/hello_world.hz +++ b/example/hello_world.hz @@ -1,4 +1,4 @@ fun main: int = do - @writesss("Hello, World!\n"); + @write("Hello, World!\n"); return 69; end; \ No newline at end of file diff --git a/example/pipe.hz b/example/pipe.hz new file mode 100644 index 0000000..3b485b2 --- /dev/null +++ b/example/pipe.hz @@ -0,0 +1,8 @@ +fun foo (xs: int): int = return xs + 1; +fun bar (xs: int) (x: int): int = return xs - x; + +fun main: int = do + let res: int = foo(69) + |> bar(1); + @write(res); +end; \ No newline at end of file