diff --git a/fine/src/parser.rs b/fine/src/parser.rs index 374260b7..723dbe92 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -480,10 +480,6 @@ impl<'a> CParser<'a> { }); } - fn synchronize(&mut self) { - if self.panic {} - } - fn build_tree(self) -> (SyntaxTree<'a>, Lines) { let mut events = self.events; let mut stack = Vec::new(); @@ -551,7 +547,15 @@ fn file(p: &mut CParser) { // the right-brace that a block would end with.) p.advance_with_error("unbalanced '}'"); } - _ => statement(p), + _ => { + if !statement(p) { + if p.at_any(STATEMENT_RECOVERY) { + break; + } else { + p.advance_with_error("expected statement"); + } + } + } } } p.end(m, TreeKind::File); @@ -715,19 +719,35 @@ fn type_parameter(p: &mut CParser) { p.end(m, TreeKind::TypeParameter); } +const STATEMENT_RECOVERY: &[TokenKind] = &[ + TokenKind::RightBrace, + TokenKind::Fun, + TokenKind::LeftBrace, + TokenKind::Let, + TokenKind::Return, + TokenKind::For, + TokenKind::Class, +]; + fn block(p: &mut CParser) { let m = p.start(); p.expect_start(TokenKind::LeftBrace); while !p.at(TokenKind::RightBrace) && !p.eof() { - statement(p); + if !statement(p) { + if p.at_any(STATEMENT_RECOVERY) { + break; + } else { + p.advance_with_error("expected statement"); + } + } } p.expect(TokenKind::RightBrace, "expect '}' to end a block"); p.end(m, TreeKind::Block); } -fn statement(p: &mut CParser) { +fn statement(p: &mut CParser) -> bool { match p.peek() { TokenKind::Fun => function(p), TokenKind::LeftBrace => block(p), @@ -739,8 +759,16 @@ fn statement(p: &mut CParser) { // require a semicolon at the end if it's all by itself. TokenKind::If => statement_if(p), - _ => statement_expression(p), + _ => { + if p.at(TokenKind::Semicolon) || p.at_any(EXPRESSION_FIRST) { + statement_expression(p) + } else { + return false; + } + } } + + true } fn statement_if(p: &mut CParser) { @@ -758,7 +786,9 @@ fn statement_let(p: &mut CParser) { p.expect_start(TokenKind::Let); p.expect(TokenKind::Identifier, "expected a name for the variable"); p.expect(TokenKind::Equal, "expected a '=' after the variable name"); - expression(p); + if p.at_any(EXPRESSION_FIRST) { + expression(p); + } if !p.at(TokenKind::RightBrace) { p.expect(TokenKind::Semicolon, "expect ';' to end a let statement"); } @@ -770,8 +800,10 @@ fn statement_return(p: &mut CParser) { let m = p.start(); p.expect_start(TokenKind::Return); - // TODO: Make expression optional if we're returning () - expression(p); + if p.at_any(EXPRESSION_FIRST) { + // TODO: Make expression optional if we're returning () + expression(p); + } if !p.at(TokenKind::RightBrace) { p.expect(TokenKind::Semicolon, "expect ';' to end a return statement"); } @@ -788,8 +820,12 @@ fn statement_for(p: &mut CParser) { "expected an identifier for the loop variable", ); p.expect(TokenKind::In, "expect an 'in' after the loop variable"); - expression(p); - block(p); + if p.at_any(EXPRESSION_FIRST) { + expression(p); + } + if p.at(TokenKind::LeftBrace) { + block(p); + } p.end(m, TreeKind::ForStatement); } @@ -797,7 +833,7 @@ fn statement_for(p: &mut CParser) { fn statement_expression(p: &mut CParser) { let m = p.start(); - if !p.at(TokenKind::RightBrace) && !p.at(TokenKind::Semicolon) { + if p.at_any(EXPRESSION_FIRST) { expression(p); } if !p.at(TokenKind::RightBrace) { @@ -810,6 +846,21 @@ fn statement_expression(p: &mut CParser) { p.end(m, TreeKind::ExpressionStatement); } +const EXPRESSION_FIRST: &[TokenKind] = &[ + TokenKind::Number, + TokenKind::String, + TokenKind::True, + TokenKind::False, + TokenKind::LeftParen, + TokenKind::Bang, + TokenKind::Minus, + TokenKind::If, + TokenKind::Identifier, + TokenKind::Selff, + TokenKind::LeftBracket, + TokenKind::New, +]; + fn expression(p: &mut CParser) { expression_with_power(p, 0) } @@ -840,7 +891,9 @@ fn infix_power(token: TokenKind) -> Option<(u8, u8)> { } fn expression_with_power(p: &mut CParser, minimum_power: u8) { - let mut expr = prefix_expression(p); + let Some(mut expr) = prefix_expression(p) else { + return; + }; while p.at(TokenKind::LeftParen) { let m = p.start_before(expr); argument_list(p); @@ -877,7 +930,11 @@ fn argument_list(p: &mut CParser) { p.expect_start(TokenKind::LeftParen); while !p.at(TokenKind::RightParen) && !p.eof() { - argument(p); + if p.at_any(EXPRESSION_FIRST) { + argument(p); + } else { + break; + } } p.expect( TokenKind::RightParen, @@ -898,8 +955,8 @@ fn argument(p: &mut CParser) { p.end(m, TreeKind::Argument); } -fn prefix_expression(p: &mut CParser) -> MarkClosed { - match p.peek() { +fn prefix_expression(p: &mut CParser) -> Option { + let result = match p.peek() { TokenKind::Number => literal(p), TokenKind::String => literal(p), TokenKind::True => literal(p), @@ -919,8 +976,12 @@ fn prefix_expression(p: &mut CParser) -> MarkClosed { TokenKind::New => object_constructor(p), - _ => p.advance_with_error("expected an expression"), - } + _ => { + assert!(!p.at_any(EXPRESSION_FIRST)); + return None; + } + }; + Some(result) } fn literal(p: &mut CParser) -> MarkClosed { @@ -995,7 +1056,11 @@ fn list_constructor(p: &mut CParser) -> MarkClosed { p.expect_start(TokenKind::LeftBracket); while !p.at(TokenKind::RightBracket) && !p.eof() { - list_constructor_element(p); + if p.at_any(EXPRESSION_FIRST) { + list_constructor_element(p); + } else { + break; + } } p.expect( TokenKind::RightBracket, diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine index b8760ead..bcee2edb 100644 --- a/fine/tests/expression/alternates.fine +++ b/fine/tests/expression/alternates.fine @@ -8,10 +8,9 @@ class Bar { } fun extract_value(v: Foo or Bar) -> f64 { - if v is Foo { - v.x // Magic type binding! - } else if v is Bar { - v.y // Same magic re-binding of the variable to the new type + match v { + v:Foo -> v.x, + v:Bar -> v.y, } // No error; exhaustivity analysis should work. } @@ -34,10 +33,9 @@ class Monster { fun print(x:string) {} fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) { - if weapon is w : RangedWeapon { - distance >= w.minRange and distance <= w.maxRange - } else { - distance == 1 + weapon match { + w:RangedWeapon -> distance >= w.minRange and distance <= w.maxRange, + _ -> distance == 1 } } @@ -60,12 +58,9 @@ fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) // local variables. The *almost* part is that the effective type of the // variable changes but not the binding. (Is this what we want?) // - // TODO: What do we do about captured variables? - // - let damage = if weapon is w: MeleeWeapon { - roll_dice(w.damage) - } else if weapon is w: RangedWeapon { - w.maxRange - w.minRange + let damage = match weapon { + w:MeleeWeapon -> roll_dice(w.damage), + w:RangedWeapon -> w.maxRange - w.minRange, }; if monster.health <= damage { @@ -87,7 +82,7 @@ fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () { // Some fun with iterators class Finished {} -let FINISHED = new Finished {} +let FINISHED = new Finished {}; class Iterator { current: f64; @@ -132,5 +127,5 @@ fun test() -> f64 { // like the above. } -// @ignore WIP +/// @ignore WIP // @no-errors \ No newline at end of file diff --git a/fine/tests/expression/errors/wild_member_access.fine b/fine/tests/expression/errors/wild_member_access.fine index f1c47c2d..45778ad5 100644 --- a/fine/tests/expression/errors/wild_member_access.fine +++ b/fine/tests/expression/errors/wild_member_access.fine @@ -17,6 +17,5 @@ fun test() { // matklad talks about in his resilient parser article. // // @expect-errors: -// | 7:12: Error at '{': expected an expression +// | 7:12: Error at '{': expect ';' to end a let statement // | 7:26: cannot find value foo here -// | 8:0: Error at '}': unbalanced '}'