1if not modules then modules = { } end modules ['mtx-check'] = {
2 version = 1.001,
3 comment = "companion to mtxrun.lua",
4 author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5 copyright = "PRAGMA ADE / ConTeXt Development Team",
6 license = "see context related readme files"
7}
8
9local P, R, S, V, C, CP, CC, lpegmatch = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cp, lpeg.Cc, lpeg.match
10local gsub, sub, format = string.gsub, string.sub, string.format
11local insert, remove = table.insert, table.remove
12
13local helpinfo = [[
14<?xml version="1.0"?>
15<application>
16 <metadata>
17 <entry name="name">mtx-check</entry>
18 <entry name="detail">Basic ConTeXt Syntax Checking</entry>
19 <entry name="version">0.10</entry>
20 </metadata>
21 <flags>
22 <category name="basic">
23 <subcategory>
24 <flag name="check"><short>check tex file for errors</short></flag>
25 </subcategory>
26 </category>
27 </flags>
28</application>
29]]
30
31local application = logs.application {
32 name = "mtx-check",
33 banner = "Basic ConTeXt Syntax Checking 0.10",
34 helpinfo = helpinfo,
35}
36
37local report = application.report
38
39scripts = scripts or { }
40scripts.checker = scripts.checker or { }
41
42local validator = { }
43
44validator.n = 1
45validator.errors = { }
46validator.trace = false
47validator.direct = false
48
49validator.printer = print
50validator.tracer = print
51
52local message = function(position, kind, extra)
53 local ve = validator.errors
54 ve[#ve+1] = { kind, position, validator.n, extra }
55 if validator.direct then
56 position = position or "eof"
57 if extra then
58 validator.printer(format("%s error at position %s (line %s) (%s)",kind,position,validator.n,extra))
59 else
60 validator.printer(format("%s error at position %s (line %s)",kind,position,validator.n))
61 end
62 end
63end
64
65local progress = function(position, data, kind)
66 if validator.trace then
67 validator.tracer(format("%s at position %s: %s", kind, position, data or ""))
68 end
69end
70
71local i_m, d_m = P("$"), P("$$")
72local l_s, r_s = P("["), P("]")
73local l_g, r_g = P("{"), P("}")
74
75local okay = lpeg.P("{[}") + lpeg.P("{]}")
76
77local esc = P("\\")
78local space = S(" \t\f\v")
79local newline = lpeg.patterns.newline
80
81local line = newline / function() validator.n = validator.n + 1 end
82
83local startluacode = P("\\startluacode")
84local stopluacode = P("\\stopluacode")
85
86local somecode = startluacode * (1-stopluacode)^1 * stopluacode
87
88local stack = { }
89
90local function push(p,s)
91
92 insert(stack,{ p, s, validator.n })
93end
94
95local function pop(p,s)
96
97 local top = remove(stack)
98 if not top then
99 message(p,"missing start")
100 elseif top[2] ~= s then
101 message(p,"missing stop",format("see line %s",top[3]))
102 else
103
104 end
105end
106
107local cstoken = R("az","AZ","\127\255")
108
109local start = CP() * P("\\start") * C(cstoken^0) / push
110local stop = CP() * P("\\stop") * C(cstoken^0) / pop
111
112local contextgrammar = P { "tokens",
113 ["tokens"] = (V("ignore") + V("start") + V("stop") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0,
114 ["start"] = start,
115 ["stop"] = stop,
116 ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0),
117 ["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,
118 ["setup"] = l_s * (okay + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s,
119 ["display"] = d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m,
120 ["inline"] = i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m,
121 ["errors"] = V("gerror") + V("serror") + V("derror") + V("ierror"),
122 ["gerror"] = CP() * (l_g + r_g) * CC("grouping error") / message,
123 ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message,
124 ["derror"] = CP() * d_m * CC("display math error") / message,
125 ["ierror"] = CP() * i_m * CC("inline math error") / message,
126 ["ignore"] = somecode,
127}
128
129
130
131local function push(p,s)
132 insert(stack,{ p, s, validator.n })
133end
134
135local function pop(p,s)
136 local top = remove(stack)
137 if not top then
138 message(p,"missing <some>def")
139 end
140end
141
142local function finish(p)
143 local bot = stack[1]
144 if bot then
145 message(false,format("missing enddef for %s",bot[2]),format("see line %s",bot[3]))
146 end
147 stack = { }
148end
149
150local l_b, r_b = P("["), P("]")
151local l_g, r_g = P("{"), P("}")
152local l_p, r_p = P("("), P(")")
153
154local start = CP() * C( P("vardef") + P("primarydef") + P("secondarydef") + P("tertiarydef") + P("def") ) / push
155local stop = CP() * C( P("enddef") ) / pop
156
157local dstring = P('"') * (1-P('"'))^0 * P('"')
158local semicolon = P(";")
159
160local separator = line + space + semicolon
161
162
163
164local metafungrammar = P { "tokens",
165 ["tokens"] = (V("start") + V("stop") + V("string") + V("whatever") + V("braces") + V("brackets") + V("parentheses") + V("errors") + 1)^0
166 * (CP() / finish),
167 ["start"] = separator * start * #separator,
168 ["stop"] = separator * stop * #separator,
169 ["string"] = dstring,
170 ["whatever"] = line + C(P("%") * (1-line)^0),
171 ["braces"] = l_g * (V("whatever") + V("string") + V("braces") + V("brackets") + V("parentheses") + (1 - l_g - r_g))^0 * r_g,
172 ["brackets"] = l_b * (V("whatever") + V("string") + V("braces") + V("brackets") + V("parentheses") + (1 - l_b - r_b))^0 * r_b,
173 ["parentheses"] = l_p * (V("whatever") + V("string") + V("braces") + V("brackets") + V("parentheses") + (1 - l_p - r_p))^0 * r_p,
174 ["errors"] = V("gerror") + V("berror") + V("perror"),
175 ["gerror"] = CP() * (l_g + r_g) * CC("braces error") / message,
176 ["berror"] = CP() * (l_b + r_b) * CC("brackets error") / message,
177 ["perror"] = CP() * (l_p + r_p) * CC("parentheses error") / message,
178}
179
180local grammars = {
181 mp = metafungrammar,
182 mpii = metafungrammar,
183 mpiv = metafungrammar,
184 tex = contextgrammar,
185 mkii = contextgrammar,
186 mkiv = contextgrammar,
187 mkvi = contextgrammar,
188 mkil = contextgrammar,
189 mkli = contextgrammar,
190}
191
192function validator.check(str,filetype)
193 validator.n = 1
194 validator.errors = { }
195 local grammar = grammars[filetype] or grammars.tex
196 lpegmatch(grammar,str)
197end
198
199
200
201
202
203
204
205
206
207local remapper = {
208 ["\n"] = " <lf> ",
209 ["\r"] = " <cr> ",
210 ["\t"] = " <tab> ",
211}
212
213function scripts.checker.check(filename)
214 local str = io.loaddata(filename)
215 if str then
216 validator.check(str,file.suffix(filename))
217 local errors = validator.errors
218 if #errors > 0 then
219 for k=1,#errors do
220 local v = errors[k]
221 local kind, position, line, extra = v[1], v[2], v[3], v[4]
222 if not position then
223 position = #str
224 end
225 local data = sub(str,position-30,position+30)
226 data = gsub(data,".", remapper)
227 data = gsub(data,"^ *","")
228 if extra then
229 print(format("% 5i %-10s %s (%s)", line, kind, data, extra))
230 else
231 print(format("% 5i %-10s %s", line, kind, data))
232 end
233 end
234 else
235 print("no error")
236 end
237 else
238 print("no file")
239 end
240end
241
242if environment.argument("check") then
243 scripts.checker.check(environment.files[1])
244elseif environment.argument("help") then
245 application.help()
246elseif environment.argument("exporthelp") then
247 application.export(environment.argument("exporthelp"),environment.files[1])
248elseif environment.files[1] then
249 scripts.checker.check(environment.files[1])
250else
251 application.help()
252end
253
254 |