diff --git a/lualib/nelua/thirdparty/lpegrex.lua b/lualib/nelua/thirdparty/lpegrex.lua index ef0c483f..75b29bb0 100644 --- a/lualib/nelua/thirdparty/lpegrex.lua +++ b/lualib/nelua/thirdparty/lpegrex.lua @@ -588,18 +588,17 @@ Returns line number, column number, line content, line start position and line e ]] function lpegrex.calcline(subject, position) if position < 0 then error 'invalid position' end - local sublen = #subject - if position > sublen then position = sublen end - local caps = calclinepatt:match(subject:sub(1,position)) - local ncaps = #caps - local lineno = ncaps + 1 - local lastpos = caps[ncaps] or 0 - local linestart = lastpos + 1 - local colno = position - lastpos - local lineend = subject:find("\n", position+1, true) - lineend = lineend and lineend-1 or #subject - local line = subject:sub(linestart, lineend) - return lineno, colno, line, linestart, lineend + position = math.min(position, #subject) + local lastNewlinePatt = lpeg.P {lpeg.Cp() * ((1 - lpeg.P "\n") ^ 0 * "\n") ^ 0 * lpeg.Cp()} + local linestart = (lastNewlinePatt:match(subject:sub(1, position)) or 1) - 1 + local linenum = select(2, subject:sub(1, position):gsub("\n", "\n")) + 1 + if position > 0 and subject:sub(position, position) == "\n" then + linenum = linenum - 1 + end + local lineend = subject:find("\n", position + 1, true) or #subject + 1 + local line = subject:sub(linestart + 1, lineend - 1) + local colnum = position - linestart + return linenum, colnum, line, linestart + 1, lineend end -- Auxiliary function for `prettyast` diff --git a/spec/aster_spec.lua b/spec/aster_spec.lua index 84ac8490..37d48f6e 100644 --- a/spec/aster_spec.lua +++ b/spec/aster_spec.lua @@ -2,6 +2,7 @@ local lester = require 'nelua.thirdparty.lester' local aster = require 'nelua.aster' local expect = require 'spec.tools.expect' local Attr = require 'nelua.attr' +local lpegrex = require 'nelua.thirdparty.lpegrex' local describe, it = lester.describe, lester.it local n = aster @@ -62,4 +63,12 @@ it("clone", function() expect.equal(aster.pretty(aster.clone{n.Id{'x'},n.Number{1}}), aster.pretty{n.Id{'x'},n.Number{1}}) end) +it("error on line numbers", function() + expect.fail(function() + aster.parse([[print(]]) + end, "unclosed parenthesis, did you forget a `)`?") + local lineno, colno = lpegrex.calcline("print(", 6) + assert(lineno == 1 and colno == 6, "expected error at line 1, column 6") +end) + end)