[fine] More resilience

We don't lose function declarations and whatnot, although we get lost
with broken return types.
This commit is contained in:
John Doty 2024-01-31 07:46:20 -08:00
parent 93c9dcd13b
commit 7f30d0ccc3
3 changed files with 98 additions and 39 deletions

View file

@ -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,9 +759,17 @@ 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) {
assert!(p.at(TokenKind::If));
@ -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");
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);
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");
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() {
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<MarkClosed> {
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() {
if p.at_any(EXPRESSION_FIRST) {
list_constructor_element(p);
} else {
break;
}
}
p.expect(
TokenKind::RightBracket,

View file

@ -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

View file

@ -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 '}'