From d3c31447c97cf49c3f7d6cce522e86f3db383c37 Mon Sep 17 00:00:00 2001 From: James Westman Date: Sat, 3 May 2025 10:10:06 -0500 Subject: [PATCH] parser: Tweak parsing during error conditions When an explicit parsing error is encountered and a CompileError raised, apply the changes to the context state. This way, the rule that catches the exception (e.g. Statement or Until) knows where the error occurred. Also, changed "Expected" errors to be reported at the end of the previous non-whitespace token. --- blueprintcompiler/errors.py | 2 ++ blueprintcompiler/parse_tree.py | 15 +++++++++++---- blueprintcompiler/utils.py | 4 ++-- tests/sample_errors/incomplete_signal.err | 3 +-- tests/sample_errors/menu_toplevel_attribute.err | 3 +-- tests/sample_errors/no_import_version.err | 2 +- 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/blueprintcompiler/errors.py b/blueprintcompiler/errors.py index df1c2e1..9836c85 100644 --- a/blueprintcompiler/errors.py +++ b/blueprintcompiler/errors.py @@ -111,6 +111,8 @@ class CompileError(PrintableError): n_carets += line.count("\t", col_num, col_num + n_carets) line = line.replace("\t", " ") + n_carets = max(n_carets, 1) + stream.write( f"""{self.color}{Colors.BOLD}{self.category}: {self.message}{Colors.CLEAR} at {filename} line {line_num} column {col_num}: diff --git a/blueprintcompiler/parse_tree.py b/blueprintcompiler/parse_tree.py index a215f19..9bdbef1 100644 --- a/blueprintcompiler/parse_tree.py +++ b/blueprintcompiler/parse_tree.py @@ -235,7 +235,15 @@ class ParseNode: start_idx = ctx.index inner_ctx = ctx.create_child() - if self._parse(inner_ctx): + try: + result = self._parse(inner_ctx) + except Exception as e: + # If an exception occurs, there's an explicit error, not just a rule that didn't match. Apply the context + # state so that whichever rule handles the exception (e.g. a Statement) knows where the error occurred. + ctx.apply_child(inner_ctx) + raise e + + if result: ctx.apply_child(inner_ctx) if ctx.index == start_idx: return ParseResult.EMPTY @@ -269,11 +277,11 @@ class Err(ParseNode): if self.child.parse(ctx).failed(): start_idx = ctx.start while ctx.tokens[start_idx].type in SKIP_TOKENS: - start_idx += 1 + start_idx -= 1 start_token = ctx.tokens[start_idx] raise CompileError( - self.message, Range(start_token.start, start_token.start, ctx.text) + self.message, Range(start_token.end, start_token.end, ctx.text) ) return True @@ -411,7 +419,6 @@ class Until(ParseNode): ctx.skip_unexpected_token() except CompileError as e: ctx.errors.append(e) - ctx.next_token() return True diff --git a/blueprintcompiler/utils.py b/blueprintcompiler/utils.py index ea8102e..de6d493 100644 --- a/blueprintcompiler/utils.py +++ b/blueprintcompiler/utils.py @@ -76,8 +76,8 @@ def did_you_mean(word: str, options: T.List[str]) -> T.Optional[str]: def idx_to_pos(idx: int, text: str) -> T.Tuple[int, int]: if idx == 0 or len(text) == 0: return (0, 0) - line_num = text.count("\n", 0, idx) + 1 - col_num = idx - text.rfind("\n", 0, idx) - 1 + line_num = text.count("\n", 0, idx - 1) + 1 + col_num = idx - text.rfind("\n", 0, idx - 1) - 1 return (line_num - 1, col_num) diff --git a/tests/sample_errors/incomplete_signal.err b/tests/sample_errors/incomplete_signal.err index 901ef3b..c61ef28 100644 --- a/tests/sample_errors/incomplete_signal.err +++ b/tests/sample_errors/incomplete_signal.err @@ -1,2 +1 @@ -5,1,0,Expected a signal detail name -4,9,3,Unexpected tokens \ No newline at end of file +4,11,0,Expected a signal detail name \ No newline at end of file diff --git a/tests/sample_errors/menu_toplevel_attribute.err b/tests/sample_errors/menu_toplevel_attribute.err index 8f3ef26..ee588d0 100644 --- a/tests/sample_errors/menu_toplevel_attribute.err +++ b/tests/sample_errors/menu_toplevel_attribute.err @@ -1,2 +1 @@ -4,5,21,Attributes are not permitted at the top level of a menu -4,16,10,Unexpected tokens \ No newline at end of file +4,5,21,Attributes are not permitted at the top level of a menu \ No newline at end of file diff --git a/tests/sample_errors/no_import_version.err b/tests/sample_errors/no_import_version.err index db830e0..4ee792f 100644 --- a/tests/sample_errors/no_import_version.err +++ b/tests/sample_errors/no_import_version.err @@ -1 +1 @@ -1,11,0,Expected a version number for GTK +1,10,0,Expected a version number for GTK