[fine] More resilience
We don't lose function declarations and whatnot, although we get lost with broken return types.
This commit is contained in:
parent
93c9dcd13b
commit
7f30d0ccc3
3 changed files with 98 additions and 39 deletions
|
|
@ -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<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() {
|
||||
list_constructor_element(p);
|
||||
if p.at_any(EXPRESSION_FIRST) {
|
||||
list_constructor_element(p);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
p.expect(
|
||||
TokenKind::RightBracket,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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 '}'
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue