From 14654652a3ed42ba176e9ddebb89880d4390362b Mon Sep 17 00:00:00 2001 From: aldum Date: Fri, 3 Nov 2023 14:08:15 +0100 Subject: [PATCH 1/3] report position of last valid token on parse error --- metalua/grammar/generator.lua | 23 +++++++++++++++++++---- metalua/grammar/lexer.lua | 5 +++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/metalua/grammar/generator.lua b/metalua/grammar/generator.lua index 4633c6e..68dc8e8 100644 --- a/metalua/grammar/generator.lua +++ b/metalua/grammar/generator.lua @@ -149,17 +149,32 @@ end -- Generate a tracable parsing error (not implemented yet) ------------------------------------------------------------------------------- function M.parse_error(lx, fmt, ...) - local li = lx:lineinfo_left() + local inf = lx:lineinfo_error() local file, line, column, offset, positions - if li then + local p_line, p_column + if inf then + local li = inf.last file, line, column, offset = li.source, li.line, li.column, li.offset - positions = { first = li, last = li } + local first = inf.first + positions = { first = first, last = li } + + local facing = inf.first.facing + if facing.line >= first.line and + facing.column >= first.column then + -- special case when there's no previous token + p_line, p_column = first.line, first.column + else + p_line, p_column = facing.line, facing.column + end else - line, column, offset = -1, -1, -1 + line, column, offset, p_line, p_column = -1, -1, -1, -1, -1 end local msg = string.format("line %i, char %i: "..fmt, line, column, ...) if file and file~='?' then msg = "file "..file..", "..msg end + local prev_token = string.format("facing: line %i, char %i", p_line, p_column) + msg = string.format("%s\n%s", msg, prev_token) + local src = lx.src if offset>0 and src then diff --git a/metalua/grammar/lexer.lua b/metalua/grammar/lexer.lua index 0a58058..26bd1a2 100644 --- a/metalua/grammar/lexer.lua +++ b/metalua/grammar/lexer.lua @@ -511,6 +511,7 @@ function lexer :next (n) -- TODO: is this used anywhere? I think not. a.lineinfo.last may be nil. --self.lastline = a.lineinfo.last.line end + self.lineinfo_consumed = a.lineinfo self.lineinfo_last_consumed = a.lineinfo.last return a end @@ -574,6 +575,10 @@ function lexer :lineinfo_left() return self.lineinfo_last_consumed end +function lexer :lineinfo_error() + return self.lineinfo_consumed +end + ---------------------------------------------------------------------- -- Create a new lexstream. ---------------------------------------------------------------------- From 9f458f5d1723c3da3bb0a48f979ee26df7c1bcad Mon Sep 17 00:00:00 2001 From: aldum Date: Thu, 7 Dec 2023 12:40:04 +0100 Subject: [PATCH 2/3] carve out exception for comments and explain rationale The point of adding the previous location is enabling proper syntax highlighting. For that use, not only the location of the error is required, but also the point up to which the code was valid. --- metalua/grammar/generator.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/metalua/grammar/generator.lua b/metalua/grammar/generator.lua index 68dc8e8..7dad75b 100644 --- a/metalua/grammar/generator.lua +++ b/metalua/grammar/generator.lua @@ -159,9 +159,16 @@ function M.parse_error(lx, fmt, ...) positions = { first = first, last = li } local facing = inf.first.facing + -- in case of a parse error, the consumed token usually facing the one + -- that caused the problem is the last valid one, allowing us to point + -- to the exact location where the invalid token starts + -- there are exceptions to this, identified so far: + -- * there's just one token yet + -- * there are comment(s) after the last valid token if facing.line >= first.line and - facing.column >= first.column then - -- special case when there's no previous token + facing.column >= first.column + or facing.comments + then p_line, p_column = first.line, first.column else p_line, p_column = facing.line, facing.column From bf3b9f40f1231115189dd8b236545f5ff057d517 Mon Sep 17 00:00:00 2001 From: aldum Date: Thu, 7 Dec 2023 15:05:20 +0100 Subject: [PATCH 3/3] add unclosed multiline comments exception --- metalua/grammar/generator.lua | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/metalua/grammar/generator.lua b/metalua/grammar/generator.lua index 7dad75b..fc04750 100644 --- a/metalua/grammar/generator.lua +++ b/metalua/grammar/generator.lua @@ -159,15 +159,22 @@ function M.parse_error(lx, fmt, ...) positions = { first = first, last = li } local facing = inf.first.facing + local no_invalid_comment = function(comments) + for _, c in ipairs(comments) do + if c[1]:match("%[%[") then + return false + end + end + return true + end -- in case of a parse error, the consumed token usually facing the one -- that caused the problem is the last valid one, allowing us to point -- to the exact location where the invalid token starts -- there are exceptions to this, identified so far: -- * there's just one token yet -- * there are comment(s) after the last valid token - if facing.line >= first.line and - facing.column >= first.column - or facing.comments + if (facing.line >= first.line and facing.column >= first.column) or + (facing.comments and no_invalid_comment(facing.comments)) then p_line, p_column = first.line, first.column else