diff --git a/fine/src/parser.rs b/fine/src/parser.rs index e8bd273d..1e0101c5 100644 --- a/fine/src/parser.rs +++ b/fine/src/parser.rs @@ -430,7 +430,7 @@ impl<'a> CParser<'a> { } fn expect_start(&mut self, kind: TokenKind) { - assert!(self.eat(kind), "should have started with {kind:?}"); + assert!(self.eat(kind)); } fn advance_with_error(&mut self, error: T) -> MarkClosed @@ -708,7 +708,7 @@ fn block(p: &mut CParser) { while !p.at(TokenKind::RightBrace) && !p.eof() { statement(p); } - p.expect(TokenKind::RightBrace, "expect '}' to end a block"); + p.expect(TokenKind::RightBrace, "expect '}' to start a block"); p.end(m, TreeKind::Block); } @@ -756,7 +756,6 @@ 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(TokenKind::RightBrace) { p.expect(TokenKind::Semicolon, "expect ';' to end a return statement"); @@ -939,19 +938,13 @@ fn conditional(p: &mut CParser) -> MarkClosed { p.expect_start(TokenKind::If); expression(p); - if p.at(TokenKind::LeftBrace) { - block(p) - } else { - p.error("expected a block after `if`") - } + block(p); if p.eat(TokenKind::Else) { if p.at(TokenKind::If) { // Don't require another block, just jump right into the conditional. conditional(p); - } else if p.at(TokenKind::LeftBrace) { - block(p); } else { - p.error("expected a block after `else`") + block(p); } } diff --git a/fine/src/semantics.rs b/fine/src/semantics.rs index 561fb9fe..e55a44a6 100644 --- a/fine/src/semantics.rs +++ b/fine/src/semantics.rs @@ -1741,7 +1741,7 @@ impl<'a> Semantics<'a> { eprintln!("\n{}", self.syntax_tree.dump(true)); { - let errors = self.snapshot_errors(); + let errors = self.errors.borrow(); if errors.len() == 0 { eprintln!("There were no errors reported during checking.\n"); } else { diff --git a/fine/tests/expression/alternates.fine b/fine/tests/expression/alternates.fine deleted file mode 100644 index 8ee2ebb4..00000000 --- a/fine/tests/expression/alternates.fine +++ /dev/null @@ -1,129 +0,0 @@ -// Examples of alternate types/union types/heterogeneous types. -class Foo { - x: f64; -} - -class Bar { - y: f64; -} - -fun extract_value(v: Foo or Bar) -> f64 { - if match v as Foo { - v.x // Magic type binding! - } else as Bar { - v.y // Same magic re-binding of the variable to the new type - } // No error; exhaustivity analysis should work. -} - -// This is Bob Nystrom's example from -// https://journal.stuffwithstuff.com/2023/08/04/representing-heterogeneous-data/ -// -class MeleeWeapon { - damage: f64; -} - -class RangedWeapon { - minRange: f64; - maxRange: f64; -} - -class Monster { - health: f64; -} - -fun print(x:string) {} - -fun in_range(weapon: MeleeWeapon or RangedWeapon, distance: f64) { - if match weapon as w : RangedWeapon { - distance >= w.minRange and distance <= w.maxRange - } else { - distance == 1 - } -} - -fun attack(weapon: MeleeWeapon or RangedWeapon, monster: Monster, distance: f64) { - // This is worse but it works. - if match weapon as MeleeWeapon and distance > 1 or - match weapon as w:RangedWeapon and (distance < w.minRange or distance > w.maxRange) { - print("You are out of range") - return - } - - // Bob says he doesn't want to do flow analysis but we're doing all our - // tricks with variables and scoping here, with one *big* bit of magic which - // is... - // - // NOTE: special syntax here: `identifier` as `TypeExpression` ALMOST means - // `identifier as identifier : TypeExpression` as the shorthand for checking - // local variables. The *almost* part is that the effective type of the - // variable changes but not the binding. (Is this what we want?) - let damage = if match weapon as w: MeleeWeapon { - roll_dice(w.damage) - } else as w: RangedWeapon { - // This is the trick here: else as re-uses the expression from the match - // and so we can do exhaustivity analysis. - w.maxRange - w.minRange - }; - - if monster.health <= damage { - print("You kill the monster!"); - monster.health = 0 - } else { - print("You wound the monster."); - monster.health = monster.health - damage - } -} - -fun more_examples(weapon: MeleeWeapon or RangedWeapon) -> f64 or () { - if match weapon as w: RangedWeapon and w.maxRange > 10 { - // w is still in scope here; the `and` is bound into a predicate expression - // and breaks exhaustivity - w.minRange - } -} - -// Some fun with iterators - -class Iterator { - current: f64; - - fun next(self) -> f64 or () { - if self.current < 10 { - let result = self.current; - self.current = self.current + 1; - return result; - } - } -} - -fun test() -> f64 { - let sum = 0; - - // A single step of an iterator... - let it = new Iterator { current: 0 }; - if match it.next() as v: f64 { - sum = sum + v; - } - - // Looping by hand over an iterator is pretty clean. - let it = new Iterator { current: 0 }; - while match it.next() as v: f64 { - sum = sum + v; - } - - // Unroll by hand... - let it = new Iterator { current: 0 }; - loop { - if match it.next() as v: f64 { - sum = sum + v; - } else { - return sum; - } - } - - // Not in this test but `for` over an object should turn into something - // like the above. -} - -// @ignore WIP -// @no-errors \ No newline at end of file diff --git a/fine/tests/expression/errors/broken_conditional.fine b/fine/tests/expression/errors/broken_conditional.fine deleted file mode 100644 index 84efd0cb..00000000 --- a/fine/tests/expression/errors/broken_conditional.fine +++ /dev/null @@ -1,8 +0,0 @@ -fun test() { - if true true { } -} - -// NOTE: These errors should be better -// @expect-errors: -// | 2:10: Error at 'true': expected a block after `if` -// | 2:15: Error at '{': expect ';' to end an expression statement