2 -- this short utility attempts to make educated guess
3 -- about how to talk the x11 protocol based on the input
4 -- xproto.xml file. i've tried proper XSLT for lua first
5 -- and ended up like this ... its far more flexible anyway.
8 ---------------------------------------------------------
10 ---------------------------------------------------------
12 local tconcat
= table.concat
13 -- insert to table, if not nil
14 local tinsert = function(t
,v
)
15 if (v
) then table.insert(t
,v
) end
18 local function quote(s
)
22 ---------------------------------------------------------
23 -- naive, but trivial xml parser
24 ---------------------------------------------------------
26 -- transcribe &escapes;, add more if necessary
31 local function striphtml(s
)
32 return s
:gsub("(%&[a-zA-Z]*%;)", function(s
)
33 return htmltab
[s
:sub(2,#s
-1):lower()]
37 -- parse label attributes
38 local function parseargs(s
)
40 string.gsub(s
, "([%w-]+)[ \t]*=[ \t]*([\"'])(.-)%2", function (w
, _
, a
)
41 arg
[striphtml(w
)] = striphtml(a
)
47 local function xml_parse(s
)
51 local ni
,c
,label
,xarg
, empty
54 ni
,j
,c
,label
,xarg
, empty
= string.find(s
, "<(%/?)(%w+)(.-)(%/?)>", i
)
55 if not ni
then break end
56 local text
= string.sub(s
, i
, ni
-1)
57 if not string.find(text
, "^%s*$") then
60 if empty
== "/" then -- empty element tag
61 tinsert(top
, {label
=label
, xarg
=parseargs(xarg
), empty
=1})
62 elseif c
== "" then -- start tag
63 top
= {label
=label
, xarg
=parseargs(xarg
)}
64 tinsert(stack
, top
) -- new level
66 local toclose
= table.remove(stack
) -- remove top
69 error("nothing to close with "..label
)
71 if toclose
.label
~= label
then
72 error("trying to close "..toclose
.label
.." with "..label
)
78 local text
= string.sub(s
, i
)
79 if not string.find(text
, "^%s*$") then
80 tinsert(stack
[stack
.n
], striphtml(text
))
83 error("unclosed "..stack
[stack
.n
].label
)
89 ---------------------------------------------------------
90 -- X11/XML madness begins here
91 ---------------------------------------------------------
115 -- convert values with fieldrefs, operators etc to lua
116 local function process_value(tlist
,...)
127 -- force integer division XXX todo
128 --if (oa.op == "/") then oa.op = "//" end
129 lstack
[level
].op
= oa
.op
132 fieldref
= function() return {
134 tinsert(lstack
[level
], "v."..d
)
137 value
= function() return {
139 tinsert(lstack
[level
], d
)
142 bit
= function() return {
144 tinsert(lstack
[level
], tostring((1<<tonumber(d
))))
149 local e
= tconcat(lstack
[1])
150 if (tonumber(e
)) then
151 -- avoid function if its just constant
152 tinsert(tlist
,tonumber(e
))
154 tinsert(tlist
,"function(v) return (".. e
..") end")
158 tinsert(tlist
,arg
[i
])
160 --tinsert(tlist,term)
162 -- we're deep in expression, reverse the polish to natural notation
163 assert(lstack
[level
].op
)
164 local expr
= tconcat(lstack
[level
], lstack
[level
].op
)
166 tinsert(lstack
[level
], '('..expr
..')')
174 -- generate struct/union/request/response etc field parsers
176 fields
= function(finishfunc
)
178 local onames
= {narg
=0}
184 tinsert(ofmt
,typfmt
[fa
.type])
185 tinsert(onames
,quote(fa
.name
))
189 tinsert(ofmt
,tonumber(fa
.bytes
).."x")
191 list
= function(fa
,empty
)
192 local tf
= typfmt
[fa
.type]
193 tinsert(ofmt
, "#"..tf
)
196 tinsert(onames
, "#arg-"..onames
.narg
)
198 tinsert(onames
, "typfun."..fa
.type)
200 tinsert(onames
, quote(fa
.name
))
203 return process_value(onames
, (tf
=="$" and "typfun."..fa
.type), quote(fa
.name
))
205 valueparam
= function(vpa
)
206 local vptype
= vpa
['value-mask-type']
207 local vpname
= vpa
['value-mask-name']
208 local vplname
= vpa
['value-list-name']
210 tinsert(ofmt
, typfmt
[vptype
])
211 tinsert(onames
, quote(vpname
))
215 tinsert(onames
, 'function() return arg[#arg] end')
216 tinsert(onames
, quote(vplname
))
219 tinsert(prepend
, 'chkvlist(arg,"'..vpname
..'")')
221 exprfield
= function(exa
)
222 tinsert(prepend
, 'exprfield(arg,"'..exa
.name
..'",'..onames
.narg
..",")
223 tinsert(ofmt
, typfmt
[exa
.type])
224 tinsert(onames
, quote(exa
.name
))
226 return process_value(prepend
, ")")
228 reply
= function(rplya
)
229 return fields(function(ofmt
,onames
)
235 finishfunc(ofmt
,onames
,prepend
,rfmt
,rnames
)
240 local function copy_type(a
,b
)
241 typfmt
[a
] = typfmt
[b
]
242 typfun
[a
] = typfun
[b
]
248 -- the most interesting stuff
249 parenthooks
= { xcb
= function() return {
250 xidtype
= function(a
) typfmt
[a
.name
] = "I" end,
251 xidunion
= function(a
)
262 typedef
= function(a
)
263 copy_type(a
.newname
,a
.oldname
)
271 tinsert(nlist
,ea
.name
)
272 return process_value(elist
)
275 for i
,nam
in ipairs(nlist
) do
276 enums
[a
.name
][nam
] = elist
[i
]
286 function(ofmt
,onames
)
288 'function(a,s,arg) return s:format("'..tconcat(ofmt
)..
289 '",arg,'..tconcat(onames
,",")..') end'
295 function(ofmt
,onames
)
296 typfun
[a
.name
] = 'function(a,s,arg) return s:format("|'..tconcat(ofmt
,"|")..
297 '",arg,'..tconcat(onames
,",")..')end'
300 event
= function(eva
)
301 eventnum
[eva
.name
] = eva
.number
303 function(ofmt
,onames
)
304 eventfun
[eva
.name
] = 'function(s,arg) return s:format("'..tconcat(ofmt
)..
305 '",arg,'..tconcat(onames
,",")..')end'
308 eventcopy
= function(eva
)
309 eventnum
[eva
.name
] = eva
.number
310 eventfun
[eva
.name
] = "eventfun."..eva
.ref
-- eventfun[eva.ref]
312 error = function(era
)
313 errornum
[era
.name
] = era
.number
314 return fields(function(ofmt
,onames
)
315 errorfun
[era
.name
] = 'function(s,arg) return s:format("'..tconcat(ofmt
)..
316 '",arg,'..tconcat(onames
,",")..')end'
319 errorcopy
= function(era
)
320 errornum
[era
.name
] = era
.number
321 errorfun
[era
.name
] = "errorfun."..era
.ref
-- eventfun[eva.ref]
323 request
= function(ra
)
324 --reqnum[ra.name] = ra.opcode
326 function(fmt
,names
,prepend
,rfmt
,rnames
)
327 reqfun
[ra
.name
] = 'function(s,arg) '..tconcat(prepend
,"\n")..
328 'xrequest('..ra
.opcode
..',s:format("'..tconcat(fmt
)..'",arg'..
329 (names
[1] and ',' or "")..tconcat(names
,",").."))"..
330 (rfmt
[1] and ('return s:format("'..tconcat(rfmt
)..
331 '", xreply(), '..tconcat(rnames
,",")..') end') or "end")
336 -- here comes xml diving
338 xml_dive
= function(tree
,hooks
)
339 for idx
,branch
in ipairs(tree
) do
340 if type(branch
) =="string" then
341 if (hooks
._data
) then hooks
._data(branch
) end
344 local lab
= branch
.label
345 local args
= branch
.xarg
346 -- no valid label, ignore!
347 if not lab
then continue
end
348 if not hooks
or not hooks
[lab
] then
349 print("WARNING: unhandled label "..lab
)
353 xml_dive(branch
, hooks
[lab
](args
, branch
.empty
))
355 -- exit hook while leaving the branch
356 if hooks
and hooks
._exit
then hooks
._exit() end
360 ---------------------------------------------------------
361 -- dumpers, define your own out() function
362 ---------------------------------------------------------
364 local function dumpconst(t
,tn
)
366 for k
,v
in pairs(t
) do
367 for a
,b
in pairs(v
) do
368 out(k
..a
.."="..b
..",\n")
374 local function dumpfun(t
,tn
)
377 for k
,v
in pairs(t
) do
378 if v
:sub(1,#tn
) != tn
then
379 out(k
.."="..v
..",\n")
381 tinsert(save
, tn
.."."..k
.."="..v
)
385 out(tconcat(save
,"\n").."\n")
388 local function dumpval(t
,tn
)
390 for k
,v
in pairs(t
) do
391 if (tonumber(v
)) then
393 elseif (type(v
) == "string") then
396 out(k
.."="..v
..",\n")
401 ---------------------------------------------------------
403 ---------------------------------------------------------
408 local a
= io
.open("xproto.xml"):read("*a")
410 xml_dive(r
,parenthooks
)
412 dumpconst(enums
,"const")
414 dumpval(typfmt
,"typfmt")
415 dumpfun(typfun
,"typfun")
417 dumpval(errornum
,"errornum")
418 dumpfun(errorfun
,"errorfun")
420 dumpval(eventnum
,"eventnum")
421 dumpfun(eventfun
,"eventfun")
423 dumpval(reqnum
,"reqnum")
424 dumpfun(reqfun
,"reqfun")