diff --git a/crates/codegen/src/cpp.rs b/crates/codegen/src/cpp.rs index 5d0901b..4b08b15 100644 --- a/crates/codegen/src/cpp.rs +++ b/crates/codegen/src/cpp.rs @@ -58,7 +58,10 @@ impl Codegen { out.push_str(&self.gen_ir(&expr)); } 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)) + }, IRKind::Value { value } => { match value { diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 1a466d0..cf8db3a 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -51,19 +51,24 @@ pub fn ast_to_ir(ast: Vec<(Expr, Range)>) -> (Vec, Vec (irs, errors) } +#[macro_export] +macro_rules! if_err_return { + ($value:expr) => { + if let Some(err) = $value { return (None, Some(err)); }; + }; +} + pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { match expr { Expr::Let { name, type_hint, value } => { let value = expr_to_ir(&value.0); - if let Some(err) = value.1 { - // Return error - return (None, Some(err)); - } else { - let value = value.0.unwrap(); // Unwrapping because it should always be Some - let ir_kind = IRKind::Define { name: name.clone(), type_hint: type_hint.clone(), value: Box::new(value) }; - return (Some(ir_kind), None); - } + if_err_return!(value.1); + + let value = value.0.unwrap(); + let ir_kind = IRKind::Define { name: name.clone(), type_hint: type_hint.clone(), value: Box::new(value) }; + return (Some(ir_kind), None); }, + Expr::Call { name, args } => { let name = match &name.0 { Expr::Identifier(s) => { @@ -80,16 +85,13 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { for arg in &args.0 { // Lower each argument, if there is an error then return early let arg = expr_to_ir(&arg.0); - if let Some(err) = arg.1 { - return (None, Some(err)); - } else { - // Else push the lowered argument - largs.push(arg.0.unwrap()); - } + if_err_return!(arg.1); + largs.push(arg.0.unwrap()); } 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(), @@ -98,48 +100,64 @@ pub fn expr_to_ir(expr: &Expr) -> (Option, Option) { 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()); } + if_err_return!(arg.1); + + 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::>(); + let body = expr_to_ir(&body.0); - if let Some(err) = body.1 { - return (None, Some(err)); - } else { - let body = body.0.unwrap(); - let ir_kind = IRKind::Fun { name: name.clone(), return_type_hint: gen_type_hint(type_hint), args, body: Box::new(body) }; - return (Some(ir_kind), None); - } + if_err_return!(body.1); + + let body = body.0.unwrap(); + let ir_kind = IRKind::Fun { name: name.clone(), return_type_hint: gen_type_hint(type_hint), args, body: Box::new(body) }; + return (Some(ir_kind), None); }, + Expr::Return { expr } => { let expr = expr_to_ir(&expr.0); - if let Some(err) = expr.1 { - return (None, Some(err)); - } else { - let expr = expr.0.unwrap(); - let ir_kind = IRKind::Return { value: Box::new(expr) }; - return (Some(ir_kind), None); - } + if_err_return!(expr.1); + + let expr = expr.0.unwrap(); + let ir_kind = IRKind::Return { value: Box::new(expr) }; + return (Some(ir_kind), None); }, + Expr::Do { body } => { let mut lbody = Vec::new(); for expr in body { let expr = expr_to_ir(&expr.0); - if let Some(err) = expr.1 { - return (None, Some(err)); - } else { - lbody.push(expr.0.unwrap()); - } + if_err_return!(expr.1); + lbody.push(expr.0.unwrap()); } let ir_kind = IRKind::Do { body: lbody }; return (Some(ir_kind), None); }, + Expr::If { cond, body, else_body } => { + let cond = expr_to_ir(&cond.0); + if_err_return!(cond.1); + + let body = expr_to_ir(&body.0); + if_err_return!(body.1); + + let else_body = expr_to_ir(&else_body.0); + if_err_return!(else_body.1); + + let ir_kind = IRKind::If { + cond: Box::new(cond.0.unwrap()), + body: Box::new(body.0.unwrap()), + else_body: Box::new(else_body.0.unwrap()) + }; + return (Some(ir_kind), None); + }, + // TODO: Handle primitive types error (e.g. overflow) // For now it just leaves the value as is and let the target compiler handle it Expr::Int(value) => (Some(IRKind::Value { value: Value::Int(*value) }), None), diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 5c20076..0f71b7d 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -28,8 +28,8 @@ pub enum Expr { If { cond: Box>, - then: Box>, - else_: Box> + body: Box>, + else_body: Box> }, Do { body: Vec> @@ -243,10 +243,35 @@ fn expr_parser() -> impl Parser>, Error = Simple ) }); + let if_block = just(Token::KwIf) + .ignore_then(expr.clone()) + .then_ignore(just(Token::KwThen)) + .then( + do_block.clone() + .or(expr.clone()) + ) + .then_ignore(just(Token::KwElse)) + .then( + do_block.clone() + .or(expr.clone()) + ) + .then_ignore(just(Token::KwEnd)) + .map(|((cond, then), else_)| { + ( + Expr::If { + cond: Box::new(cond.clone()), + body: Box::new(then), + else_body: Box::new(else_.clone()), + }, + cond.1.start..else_.1.end, + ) + }); + let_ .or(fun) .or(return_) .or(do_block) + .or(if_block) .or(compare) }).labelled("expression"); diff --git a/example/err.hades b/example/err.hz similarity index 100% rename from example/err.hades rename to example/err.hz diff --git a/example/ex.hades b/example/ex.hz similarity index 100% rename from example/ex.hades rename to example/ex.hz diff --git a/example/if.hz b/example/if.hz new file mode 100644 index 0000000..d9a5054 --- /dev/null +++ b/example/if.hz @@ -0,0 +1,7 @@ +fun main: int = do + if true then + @write("True") + else + @write("False") + end; +end; \ No newline at end of file