diff --git a/crates/codegen/src/cpp.rs b/crates/codegen/src/cpp.rs index 4bb34a2..66c1c7e 100644 --- a/crates/codegen/src/cpp.rs +++ b/crates/codegen/src/cpp.rs @@ -36,12 +36,15 @@ impl Codegen { format!("{} {} = {};\n", type_hint, name, self.gen_ir(value)) }, IRKind::Call { name, args } => { + format!("{}({});\n", name, args.iter().map(|arg| self.gen_ir(arg)).collect::>().join(", ")) + }, + 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])) }, - _ => format!("{}({});\n", name, args.iter().map(|arg| self.gen_ir(arg)).collect::>().join(", ")), + _ => 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", return_type_hint, name, args, self.gen_ir(body)) diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 5f6ac83..1a466d0 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -1,6 +1,8 @@ use std::ops::Range; use parser::Expr; +const INTRINSICS: [&str; 2] = ["write", "read"]; + #[derive(Debug)] pub enum Value { Int(i64), Boolean(bool), String(String), Ident(String) } @@ -9,6 +11,7 @@ pub enum IRKind { Define { name: String, type_hint: String, value: Box }, Fun { name: String, return_type_hint: String, args: Vec<(String, String)>, body: Box }, Call { name: String, args: Vec }, + Intrinsic { name: String, args: Vec }, Do { body: Vec }, If { cond: Box, body: Box, else_body: Box }, Value { value: Value }, @@ -63,7 +66,12 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { }, Expr::Call { name, args } => { let name = match &name.0 { - Expr::Identifier(s) => s.clone(), + Expr::Identifier(s) => { + if INTRINSICS.contains(&s.as_str()) { s.clone() } + else { + return (None, Some(LoweringError { span: name.1.clone(), message: format!("Unknown intrinsic: {}", s) })); + } + } // Should never happen because the parser should have caught this _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string() })) }; @@ -82,6 +90,20 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { let ir_kind = IRKind::Call { name, args: largs }; return (Some(ir_kind), None); }, + Expr::Intrinsic { name, args } => { + let name = match &name.0 { + Expr::Identifier(s) => s.clone(), + _ => return (None, Some(LoweringError { span: name.1.clone(), message: "Expected identifier".to_string() })) + }; + let mut largs = Vec::new(); + for arg in &args.0 { + let arg = expr_to_ir(&arg.0); + if let Some(err) = arg.1 { return (None, Some(err)); } + else { largs.push(arg.0.unwrap()); } + } + let ir_kind = IRKind::Intrinsic { name, args: largs }; + return (Some(ir_kind), None); + }, Expr::Fun { name, type_hint, args, body } => { // Iterate each argument and give it a type hint let args = args.0.iter().map(|arg| (arg.0.0.clone(), gen_type_hint(&arg.1.0))).collect::>(); diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index 55e8252..4862997 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -15,12 +15,13 @@ pub enum Token { // Operators Plus, Minus, Multiply, Divide, Not, Equal, NotEqual, Less, Greater, - + // Symbols & Delimiters Assign, Dot, Comma, Colon, SemiColon, OpenParen, CloseParen, + At, } impl std::fmt::Display for Token { @@ -49,7 +50,7 @@ impl std::fmt::Display for Token { Token::NotEqual => write!(f, "!="), Token::Less => write!(f, "<"), Token::Greater => write!(f, ">"), - + Token::Assign => write!(f, "="), Token::Dot => write!(f, "."), Token::Comma => write!(f, ","), @@ -57,6 +58,7 @@ impl std::fmt::Display for Token { Token::SemiColon => write!(f, ";"), Token::OpenParen => write!(f, "("), Token::CloseParen => write!(f, ")"), + Token::At => write!(f, "@"), } } } @@ -88,6 +90,7 @@ pub fn lexer() -> impl Parser, Error = Simple> { just(';').to(Token::SemiColon), just('(').to(Token::OpenParen), just(')').to(Token::CloseParen), + just('@').to(Token::At), )); let keyword = text::ident().map(|s: String| match s.as_str() { diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 79cc2fa..5c20076 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>> }, + Intrinsic { name: Box>, args: Spanned>> }, Let { name: String, @@ -60,7 +61,7 @@ fn expr_parser() -> impl Parser>, Error = Simple // .delimited_by(just(Token::OpenParen), just(Token::CloseParen))) .labelled("atom"); - let call = atom + let call = atom.clone() .then( args.clone() .delimited_by( @@ -79,11 +80,31 @@ fn expr_parser() -> impl Parser>, Error = Simple ) }); + let intrinsic = just(Token::At) + .ignore_then(atom) + .then( + args.clone() + .delimited_by( + just(Token::OpenParen), + just(Token::CloseParen), + ) + .repeated() + ) + .foldl(|name, args| { + ( + Expr::Intrinsic { + name: Box::new(name.clone()), + args: (args, name.1.clone()), + }, + name.1, + ) + }); + let unary = choice(( just(Token::Plus), just(Token::Minus))) .repeated() - .then(call) + .then(call.or(intrinsic)) .foldr(|op, rhs| { ( Expr::Unary { diff --git a/example/ex.hades b/example/ex.hades index 0c1c9a9..2f63cc1 100644 --- a/example/ex.hades +++ b/example/ex.hades @@ -1,4 +1,4 @@ fun main: int = do - write("Hello, World!\n"); + @write("Hello, World!\n"); return 0; end; \ No newline at end of file