diff --git a/src/main.rs b/src/main.rs index f847cdd..ebb0eae 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,6 @@ extern crate lazy_static; extern crate maplit; #[macro_use] extern crate serde_derive; -extern crate serde; extern crate serde_json; use std::path::Path; diff --git a/src/schala_lang/parsing.rs b/src/schala_lang/parsing.rs index e61cdc6..9f0dcd0 100644 --- a/src/schala_lang/parsing.rs +++ b/src/schala_lang/parsing.rs @@ -300,13 +300,19 @@ prefix_op := '+' | '-' | '!' | '~' primary := literal | paren_expr | identifier_expr paren_expr := LParen expression RParen -identifier_expr := call_expr | index_expr | IDENTIFIER +identifier_expr := call_expr | index_expr | if_expr | IDENTIFIER literal := 'true' | 'false' | number_literal | STR_LITERAL +if_expr := 'if' expression block else_clause +else_clause := ε | 'else' block + +block := '{' (statement)* '}' + call_expr := IDENTIFIER '(' expr_list ')' //TODO maybe make this optional? or no, have a bare identifier meant to be used as method taken care of in eval index_expr := '[' (expression (',' (expression)* | ε) ']' expr_list := expression (',' expression)* | ε + // a float_literal can still be assigned to an int in type-checking number_literal := int_literal | float_literal int_literal = ('0x' | '0b') digits @@ -424,7 +430,8 @@ pub enum Expression { Index { indexee: Box, indexers: Vec, - } + }, + IfExpression(Box, Vec, Option>), } #[derive(Debug, PartialEq)] @@ -598,6 +605,7 @@ impl Parser { parse_method!(primary(&mut self) -> ParseResult { match self.peek() { LParen => self.paren_expr(), + Keyword(Kw::If) => self.if_expr(), Identifier(_) => self.identifier_expr(), _ => self.literal(), } @@ -610,7 +618,7 @@ impl Parser { Ok(expr) }); - fn identifier_expr(&mut self) -> ParseResult { + parse_method!(identifier_expr(&mut self) -> ParseResult { let identifier = self.identifier()?; match self.peek() { LParen => { @@ -629,7 +637,7 @@ impl Parser { } _ => Ok(Expression::Variable(identifier)) } - } + }); parse_method!(call_expr(&mut self) -> ParseResult> { let mut exprs = Vec::new(); @@ -665,6 +673,40 @@ impl Parser { Ok(exprs) }); + parse_method!(if_expr(&mut self) -> ParseResult { + expect!(self, Keyword(Kw::If), "Expected 'if'"); + let condition = self.expression()?; + let then_clause = self.block()?; + let else_clause = self.else_clause()?; + Ok(Expression::IfExpression(Box::new(condition), then_clause, else_clause)) + }); + + parse_method!(else_clause(&mut self) -> ParseResult>> { + Ok(if let Keyword(Kw::Else) = self.peek() { + self.next(); + Some(self.block()?) + } else { + None + }) + }); + + parse_method!(block(&mut self) -> ParseResult> { + expect!(self, LCurlyBrace, "Expected '{'"); + let mut statements = Vec::new(); + loop { + match self.peek() { + EOF | RCurlyBrace => break, + Newline | Semicolon => { + self.next(); + continue; + }, + _ => statements.push(self.statement()?), + } + } + expect!(self, RCurlyBrace, "Expected '}'"); + Ok(statements) + }); + parse_method!(identifier(&mut self) -> ParseResult> { match self.next() { Identifier(s) => Ok(s), @@ -889,4 +931,24 @@ mod parse_tests { parse_test!("var a = 10", AST(vec![Declaration(Binding { name: rc!(a), constant: false, expr: IntLiteral(10) } )])); parse_test!("const a = 2 + 2", AST(vec![Declaration(Binding { name: rc!(a), constant: true, expr: binexp!("+", IntLiteral(2), IntLiteral(2)) }) ])); } + + #[test] + fn parsing_block_expressions() { + parse_test!("if a() { b(); c() }", AST(vec![Expression( + IfExpression(Box::new(Call { name: rc!(a), params: vec![]}), + vec![Expression(Call { name: rc!(b), params: vec![]}), Expression(Call { name: rc!(c), params: vec![] })], + None))])); + parse_test!(r#" + if true { + const a = 10 + b + } else { + c + }"#, + AST(vec![Expression(IfExpression(Box::new(BoolLiteral(true)), + vec![Declaration(Binding { name: rc!(a), constant: true, expr: IntLiteral(10) }), + Expression(Variable(rc!(b)))], + Some(vec![Expression(Variable(rc!(c)))])))]) + ); + } }