if not modules then modules = { } end modules ['mtx-check'] = {
version = 1.001,
comment = "companion to mtxrun.lua",
author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
copyright = "PRAGMA ADE / ConTeXt Development Team",
license = "see context related readme files"
}
local P, R, S, V, C, CP, CC, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cp, lpeg.Cc, lpeg.match
local gsub, sub, format = string.gsub, string.sub, string.format
local insert, remove = table.insert, table.remove
local helpinfo = [[
mtx-check
Basic ConTeXt Syntax Checking
0.10
check tex file for errors
]]
local application = logs.application {
name = "mtx-check",
banner = "Basic ConTeXt Syntax Checking 0.10",
helpinfo = helpinfo,
}
local report = application.report
scripts = scripts or { }
scripts.checker = scripts.checker or { }
local validator = { }
validator.n = 1
validator.errors = { }
validator.trace = false
validator.direct = false
validator.printer = print
validator.tracer = print
local message = function(position, kind, extra)
local ve = validator.errors
ve[#ve+1] = { kind, position, validator.n, extra }
if validator.direct then
position = position or "eof"
if extra then
validator.printer(format("%s error at position %s (line %s) (%s)",kind,position,validator.n,extra))
else
validator.printer(format("%s error at position %s (line %s)",kind,position,validator.n))
end
end
end
local progress = function(position, data, kind)
if validator.trace then
validator.tracer(format("%s at position %s: %s", kind, position, data or ""))
end
end
local i_m, d_m = P("$"), P("$$")
local l_s, r_s = P("["), P("]")
local l_g, r_g = P("{"), P("}")
local okay = lpeg.P("{[}") + lpeg.P("{]}")
local esc = P("\\")
local space = S(" \t\f\v")
local newline = lpeg.patterns.newline
local line = newline / function() validator.n = validator.n + 1 end
local startluacode = P("\\startluacode")
local stopluacode = P("\\stopluacode")
local somecode = startluacode * (1-stopluacode)^1 * stopluacode
local stack = { }
local function push(p,s)
-- print("start",p,s)
insert(stack,{ p, s, validator.n })
end
local function pop(p,s)
-- print("stop",p,s)
local top = remove(stack)
if not top then
message(p,"missing start")
elseif top[2] ~= s then
message(p,"missing stop",format("see line %s",top[3]))
else
-- okay
end
end
local cstoken = R("az","AZ","\127\255")
local start = CP() * P("\\start") * C(cstoken^0) / push
local stop = CP() * P("\\stop") * C(cstoken^0) / pop
local contextgrammar = P { "tokens",
["tokens"] = (V("ignore") + V("start") + V("stop") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0,
["start"] = start,
["stop"] = stop,
["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0),
["grouped"] = l_g * (V("start") + V("stop") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + line + (1 - l_g - r_g))^0 * r_g,
["setup"] = l_s * (okay + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s,
["display"] = d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m,
["inline"] = i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m,
["errors"] = V("gerror") + V("serror") + V("derror") + V("ierror"),
["gerror"] = CP() * (l_g + r_g) * CC("grouping error") / message,
["serror"] = CP() * (l_s + r_g) * CC("setup error") / message,
["derror"] = CP() * d_m * CC("display math error") / message,
["ierror"] = CP() * i_m * CC("inline math error") / message,
["ignore"] = somecode,
}
-- metafun variant
local function push(p,s)
insert(stack,{ p, s, validator.n })
end
local function pop(p,s)
local top = remove(stack)
if not top then
message(p,"missing def")
end
end
local function finish(p)
local bot = stack[1]
if bot then
message(false,format("missing enddef for %s",bot[2]),format("see line %s",bot[3]))
end
stack = { }
end
local l_b, r_b = P("["), P("]")
local l_g, r_g = P("{"), P("}")
local l_p, r_p = P("("), P(")")
local start = CP() * C( P("vardef") + P("primarydef") + P("secondarydef") + P("tertiarydef") + P("def") ) / push
local stop = CP() * C( P("enddef") ) / pop
local dstring = P('"') * (1-P('"'))^0 * P('"')
local semicolon = P(";")
local separator = line + space + semicolon
-- todo: start/stop also in ()
local metafungrammar = P { "tokens",
["tokens"] = (V("start") + V("stop") + V("string") + V("whatever") + V("braces") + V("brackets") + V("parentheses") + V("errors") + 1)^0
* (CP() / finish),
["start"] = separator * start * #separator,
["stop"] = separator * stop * #separator,
["string"] = dstring,
["whatever"] = line + C(P("%") * (1-line)^0),
["braces"] = l_g * (V("whatever") + V("string") + V("braces") + V("brackets") + V("parentheses") + (1 - l_g - r_g))^0 * r_g,
["brackets"] = l_b * (V("whatever") + V("string") + V("braces") + V("brackets") + V("parentheses") + (1 - l_b - r_b))^0 * r_b,
["parentheses"] = l_p * (V("whatever") + V("string") + V("braces") + V("brackets") + V("parentheses") + (1 - l_p - r_p))^0 * r_p,
["errors"] = V("gerror") + V("berror") + V("perror"),
["gerror"] = CP() * (l_g + r_g) * CC("braces error") / message,
["berror"] = CP() * (l_b + r_b) * CC("brackets error") / message,
["perror"] = CP() * (l_p + r_p) * CC("parentheses error") / message,
}
local grammars = {
mp = metafungrammar,
mpii = metafungrammar,
mpiv = metafungrammar,
tex = contextgrammar,
mkii = contextgrammar,
mkiv = contextgrammar,
}
function validator.check(str,filetype)
validator.n = 1
validator.errors = { }
local grammar = grammars[filetype] or grammars.tex
lpegmatch(grammar,str)
end
--~ str = [[
--~ a{oeps {oe\{\}ps} }
--~ test { oeps \} \[\] oeps \setupxxx[oeps=bla]}
--~ test $$ \hbox{$ oeps \} \[\] oeps $} $$
--~ {$x\$xx$ $
--~ ]]
--~ str = string.rep(str,10)
local remapper = {
["\n"] = " ",
["\r"] = " ",
["\t"] = " ",
}
function scripts.checker.check(filename)
local str = io.loaddata(filename)
if str then
validator.check(str,file.suffix(filename))
local errors = validator.errors
if #errors > 0 then
for k=1,#errors do
local v = errors[k]
local kind, position, line, extra = v[1], v[2], v[3], v[4]
if not position then
position = #str
end
local data = sub(str,position-30,position+30)
data = gsub(data,".", remapper)
data = gsub(data,"^ *","")
if extra then
print(format("% 5i %-10s %s (%s)", line, kind, data, extra))
else
print(format("% 5i %-10s %s", line, kind, data))
end
end
else
print("no error")
end
else
print("no file")
end
end
if environment.argument("check") then
scripts.checker.check(environment.files[1])
elseif environment.argument("help") then
application.help()
elseif environment.argument("exporthelp") then
application.export(environment.argument("exporthelp"),environment.files[1])
elseif environment.files[1] then
scripts.checker.check(environment.files[1])
else
application.help()
end