parser: Merge consecutive "Unexpected tokens" errors

Fixes #24
This commit is contained in:
James Westman 2021-12-16 23:18:31 -06:00
parent 5b76a3b8dd
commit 8e1a9f72dd
No known key found for this signature in database
GPG key ID: CE2DBA0ADB654EA6
5 changed files with 34 additions and 5 deletions

View file

@ -85,6 +85,11 @@ at {filename} line {line_num} column {col_num}:
print() print()
class UnexpectedTokenError(CompileError):
def __init__(self, start, end):
super().__init__("Unexpected tokens", start, end)
@dataclass @dataclass
class CodeAction: class CodeAction:
title: str title: str

View file

@ -25,7 +25,7 @@ from collections import defaultdict
from enum import Enum from enum import Enum
from .ast import AstNode from .ast import AstNode
from .errors import assert_true, CompilerBugError, CompileError from .errors import assert_true, CompilerBugError, CompileError, UnexpectedTokenError
from .tokenizer import Token, TokenType from .tokenizer import Token, TokenType
@ -184,6 +184,22 @@ class ParseContext:
token = self.tokens[self.index] token = self.tokens[self.index]
return token return token
def skip_unexpected_token(self):
""" Skips a token and logs an "unexpected token" error. """
self.skip()
start = self.tokens[self.index].start
self.next_token()
self.skip()
end = self.tokens[self.index - 1].end
if (len(self.errors)
and isinstance((err := self.errors[-1]), UnexpectedTokenError)
and err.end == start):
err.end = end
else:
self.errors.append(UnexpectedTokenError(start, end))
def is_eof(self) -> Token: def is_eof(self) -> Token:
return self.index >= len(self.tokens) or self.peek_token().type == TokenType.EOF return self.index >= len(self.tokens) or self.peek_token().type == TokenType.EOF
@ -330,8 +346,7 @@ class Until(ParseNode):
while not self.delimiter.parse(ctx).succeeded(): while not self.delimiter.parse(ctx).succeeded():
try: try:
if not self.child.parse(ctx).matched(): if not self.child.parse(ctx).matched():
token = ctx.next_token() ctx.skip_unexpected_token()
ctx.errors.append(CompileError("Unexpected token", token.start, token.end))
except CompileError as e: except CompileError as e:
ctx.errors.append(e) ctx.errors.append(e)
ctx.next_token() ctx.next_token()

View file

@ -0,0 +1,7 @@
using Gtk 4.0;
Button {
visible: false;
not actually blueprint code;
Label {}
}

View file

@ -0,0 +1 @@
5,3,31,Unexpected tokens

View file

@ -71,9 +71,9 @@ class TestSamples(unittest.TestCase):
raise MultipleErrors(ast.errors) raise MultipleErrors(ast.errors)
except PrintableError as e: except PrintableError as e:
def error_str(error): def error_str(error):
line, col = utils.idx_to_pos(error.start, blueprint) line, col = utils.idx_to_pos(error.start + 1, blueprint)
len = error.end - error.start len = error.end - error.start
return ",".join([str(line + 1), str(col + 1), str(len), error.message]) return ",".join([str(line + 1), str(col), str(len), error.message])
if isinstance(e, CompileError): if isinstance(e, CompileError):
actual = error_str(e) actual = error_str(e)
@ -143,6 +143,7 @@ class TestSamples(unittest.TestCase):
self.assert_sample_error("a11y_prop_type") self.assert_sample_error("a11y_prop_type")
self.assert_sample_error("class_assign") self.assert_sample_error("class_assign")
self.assert_sample_error("class_dne") self.assert_sample_error("class_dne")
self.assert_sample_error("consecutive_unexpected_tokens")
self.assert_sample_error("duplicate_obj_id") self.assert_sample_error("duplicate_obj_id")
self.assert_sample_error("enum_member_dne") self.assert_sample_error("enum_member_dne")
self.assert_sample_error("filters_in_non_file_filter") self.assert_sample_error("filters_in_non_file_filter")