diff options
author | Ori Bernstein <ori@eigenstate.org> | 2019-01-11 12:07:44 -0800 |
---|---|---|
committer | Ori Bernstein <ori@eigenstate.org> | 2019-01-11 12:07:44 -0800 |
commit | 4212e61603d2a270ab839bd00c9a47a5ee241913 (patch) | |
tree | d51b992581690141aac24ffccaca028ee56c5eac | |
parent | e1eab19ea317e875334d7836613abf843281c941 (diff) | |
download | libxmyrb-4212e61603d2a270ab839bd00c9a47a5ee241913.tar.gz |
Add rough first cut at doc generation.
-rw-r--r-- | gendoc.py | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/gendoc.py b/gendoc.py new file mode 100644 index 0000000..e1c7057 --- /dev/null +++ b/gendoc.py @@ -0,0 +1,401 @@ +from lxml import etree +import getopt +import os +import sys +import errno +import re +import io +import subprocess +import collections + +lentag = 0 +isextension = False +indents = collections.defaultdict(int) +tymap = { + 'char': 'byte', + 'BYTE': 'byte', + 'INT8': 'int8', + 'INT16': 'int16', + 'INT32': 'int32', + 'INT64': 'int64', + 'CARD8': 'byte', + 'CARD16': 'uint16', + 'CARD32': 'uint32', + 'CARD64': 'uint64', + 'BOOL': 'bool', + 'xid': 'xid', + 'void': 'void', +} + +renames = { + 'render': { + 'CreateCursor': 'RenderCreateCursor' + } +} + +def writeln(f, s, *args): + s = s.format(*args) + if len(s) == 0: + f.write('\n') + for ln in s.splitlines(): + if len(ln): + f.write('\t'*indents[f] + ln + '\n') + else: + f.write('\n') + +def indent(f): + indents[f] += 1 + +def outdent(f): + indents[f] -= 1 + +def myrtype(ty): + if ty in tymap: + return tymap[ty] + else: + return ty.lower() + +def isprimitive(ty): + return ty in tymap + +def load(mod, path): + with open(path) as f: + t = etree.parse(f) + + elts = collections.defaultdict(dict) + for e in t.getroot(): + if e.tag is etree.Comment: + continue + name = e.get('name') + if mod in renames and name in renames[mod]: + print('rename {} => {}'.format(name, renames[mod][name])) + e.set('name', renames[mod][name]) + if e.tag == 'enum': elts['enums'][e.get('name')] = e + elif e.tag == 'error': elts['errors'][e.get('name')] = e + elif e.tag == 'errorcopy': elts['errors'][e.get('name')] = e + elif e.tag == 'event': elts['events'][e.get('name')] = e + elif e.tag == 'eventcopy': elts['events'][e.get('name')] = e + elif e.tag == 'struct': elts['structs'][e.get('name')] = e + elif e.tag == 'request': elts['requests'][e.get('name')] = e + elif e.tag == 'union': elts['unions'][e.get('name')] = e + elif e.tag == 'import': elts['imports'][e.text] = e.text + elif e.tag == 'xidtype': tymap[e.get('name')] = tymap['xid'] + elif e.tag == 'xidunion': tymap[e.get('name')] = tymap['xid'] + elif e.tag == 'typedef': tymap[e.get('newname')] = myrtype(e.get('oldname')) + elif e.tag == 'generate': raise Exception('unimplemented') + else: + raise Exception('unknown tag {}'.format(e.tag)) + return elts + +def enumval(e): + if e.tag == 'bit': + return 1 << int(e.text) + elif e.tag == 'value': + return int(e.text) + else: + print etree.tostring(e) + raise Exception('unknown tag {}'.format(e.tag)) + +def genenum(f, name, elts): + ename = name.lower() + writeln(f, 'type {} = int32', ename) + for elt in elts: + if elt.tag is etree.Comment: + continue + if elt.tag == 'item': + val = enumval(elt[0]) + writeln(f, 'const {}{}\t: {} = {}', + name, elt.get('name'), ename, val) + writeln(f, '') + +def lenexpr(elt): + if elt.tag == 'fieldref': + return '(val.{} : std.size)'.format(elt.text) + elif elt.tag == 'op': + return '({} {} {})'.format( + lenexpr(elt[0]), elt.get('op'), lenexpr(elt[1])) + elif elt.tag == 'value': + return elt.text + else: + print etree.tostring(elt) + raise Exception('unknown tag {}'.format(elt.tag)) + +def fieldname(elt): + name = elt.get('name') + if name == 'type': + name = 'kind' + return name + +def genstructif(f, elt, suff=''): + if elt.tag == 'field': + writeln(f, '{}\t: {}{}', + fieldname(elt), myrtype(elt.get('type')), suff) + elif elt.tag == 'list': + etype = elt.get('type') + if etype == 'void': + etype = 'BYTE' + listlen = ':' + if elt.find('value') is not None: + listlen = elt.find('value').text + writeln(f, '{}\t: {}[{}]{}', + fieldname(elt), myrtype(etype), listlen, suff) + elif elt.tag == 'pad': + pass + elif elt.tag == 'switch': + for case in elt.findall('bitcase'): + if case.find('field') is None: + print etree.tostring(case) + raise Exception('unknown tag {}'.format(elt.tag)) + writeln(f, '{}\t: {}', + fieldname(case.find('field')), myrtype(case.find('field').get('type'))) + elif elt.tag == 'exprfield': + # synthesized, we can ignore for now + pass + else: + print etree.tostring(elt) + raise Exception('unknown tag {}'.format(elt.tag)) + +def packfirst(elts, isreq, isevent): + if isreq or (isevent and not elts.get('xge')): + for (idx, elt) in enumerate(elts): + if elt.tag is etree.Comment or elt.tag in {'reply', 'doc'}: + continue + if elt.tag == 'pad' and elt.get('bytes') == '1': + return (True, elt, elts[idx+1:]) + if elt.tag == 'field': + if myrtype(elt.get('type')) in {'byte', 'int8', 'uint8', 'bool'}: + return (True, elt, elts[idx+1:]) + return (False, None, elts) + +def genstruct(f, name, selt, isreq=False, isresp=False, isevent=False): + suff = '' + if isreq: + suff = 'req' + elif isresp: + suff = 'resp' + (pack, first, elts) = packfirst(selt, isreq, isevent) + sname = name.lower() + writeln(f, 'type {}{} = struct', sname, suff) + # generate interface + indent(f) + if isreq: + writeln(f, 'major\t: byte') + if pack: + genstructif(f, first) + else: + writeln(f, 'minor\t: byte') + writeln(f, 'length\t: uint16') + elif isresp: + writeln(f, 'response_type\t: byte') + writeln(f, 'sequence\t: uint16') + writeln(f, 'length\t: uint32') + elif isevent: + writeln(f, 'issent\t: bool') + if selt.get('xge') is not None: + writeln(f, 'ext\t: byte') + writeln(f, 'sequence\t:uint16') + writeln(f, 'length\t: uint32') + writeln(f, 'event_type\t: uint16') + else: + if pack: + genstructif(f, first) + if selt.get('no-sequence-number') is None: + writeln(f, 'sequence\t:uint16') + + for elt in elts: + if elt.tag is etree.Comment or elt.tag in {'reply', 'doc'}: + continue + genstructif(f, elt) + + + outdent(f) + writeln(f, ';;') + writeln(f, '') + + sw = selt.find('switch') + if sw is not None: + writeln(f, 'const No{}vals = (0 : {}vals#)', sname, sname) + writeln(f, 'type {}vals = struct', sname) + indent(f) + for elt in sw.findall('bitcase'): + if elt.tag is etree.Comment or elt.tag in {'reply', 'doc'}: + continue + genstructif(f, elt.find('field')) + outdent(f) + writeln(f, ';;') + writeln(f, '') + writeln(f, '') + + +def eltsz(elt): + return 20 + + +# ugh, C style +def genunion(f, name, elts, suff=''): + sz = 0 + uname = name.lower() + writeln(f, 'type {} = struct', uname) + indent(f) + for u in elts: + if u.tag is etree.Comment: + continue + genstructif(f, u, '[...]') + sz = max(sz, eltsz(u)) + outdent(f) + writeln(f, ';;') + writeln(f, '') + + writeln(f, 'const pack{}{} : (c : display#, val : {}{}# -> void)', uname, suff, uname, suff) + writeln(f, 'const unpack{}{} : (buf : byte[:], off : std.size, val : {}{}# -> std.size)', uname, suff, uname, suff) + writeln(f, '') + + +def gencallproto(f, name, elts): + f.write(indents[summary]*'\t') + f.write('const {} : (dpy : display#'.format(name.lower())) + resp = elts.find('reply') + for elt in elts: + if elt.tag is etree.Comment or elt.tag in {'pad', 'reply', 'doc', 'reply', 'exprfield'}: + continue + + f.write(', ') + if elt.tag == 'field': + f.write('{} : {}'.format( + fieldname(elt), myrtype(elt.get('type')))) + elif elt.tag == 'list': + etype = elt.get('type') + if etype == 'void': + etype = 'BYTE' + f.write('{}: {}[:]'.format( + fieldname(elt), myrtype(etype))) + elif elt.tag == 'switch': + f.write('vals : {}vals#'.format(name.lower())) + elif elt.tag == 'exprfield': + # synthesized, we can ignore for now + pass + else: + print etree.tostring(elt) + raise Exception('unknown tag {}'.format(elt.tag)) + if resp is not None: + writeln(f, '-> {}seq)\n', name.lower()) + writeln(f, 'const wait{} : (dpy : display#, tag : {}seq -> {}resp#)', + name.lower(), name.lower(), name.lower()) + else: + f.write('-> void)\n') + +def genrequest(f, name, elts): + writeln(f, 'const {}\t: byte = {}', name, elts.get('opcode')) + genstruct(f, name, elts, True, False) + resp = elts.find('reply') + if resp is not None: + writeln(f, 'type {}seq = uint16', name.lower()) + genstruct(f, name, resp, False, True) + gencallproto(f, name, elts) + +def genevent(f, name, elts): + writeln(f, 'const {} : byte = {}', name, elts.get('number')) + genstruct(f, name, elts, isevent=True) + +def geneventunion(f, mods, elts): + for mod in mods: + writeln(f, 'use "{}-gen"', mod) + writeln(f, 'pkg xmyrb =') + indent(f) + writeln(f, 'type event = union') + indent(f) + for (k, v) in elts.items(): + writeln(f, '`{} {}', k, k.lower()) + outdent(f) + writeln(f, ';;') + outdent(f) + writeln(f, ';;') + +def finddoc(doc, n): + for d in doc: + if d.get('name') == n: + if d.text: + return d.text.strip() + +def gendoc(f, fn, name, elt, doc): + indent(f) + fn(f, name, elt) + outdent(f) + if not doc: + return + b = doc.find('brief') + if b: + print(b.text) + for sub in elt: + if sub.tag == 'field': + n = sub.get('name') + desc = finddoc(doc, n) + if desc: + writeln(f, '`{}`: {}', n, finddoc(doc, n)) + writeln(f, '') + +def generate(elts): + writeln(summary, 'pkg xmyrb =') + indent(summary) + + writeln(summary, '/* enums */') + for (k, v) in elts['enums'].items(): + genenum(summary, k, v) + gendoc(fulldoc, genenum, k, v, v.find('doc')) + writeln(summary, '/* structs */') + for (k, v) in elts['structs'].items(): + genstruct(summary, k, v) + gendoc(fulldoc, genstruct, k, v, v.find('doc')) + writeln(summary, '/* requests */') + for (k, v) in elts['requests'].items(): + genrequest(summary, k, v) + gendoc(fulldoc, genrequest, k, v, v.find('doc')) + writeln(summary, '/* unions */') + for (k, v) in elts['unions'].items(): + genunion(summary, k, v) + gendoc(fulldoc, genunion, k, v, v.find('doc')) + writeln(summary, '/* events */') + for (k, v) in elts['events'].items(): + genevent(summary, k, v) + gendoc(fulldoc, genevent, k, v, v.find('doc')) + + outdent(summary) + writeln(summary, ';;') + +def addevents(events, elts): + for (k, v) in elts['events'].items(): + if k in events: + raise Exception('event {} found in two modules'.format(k)) + events[k] = v + +def main(): + global summary, fulldoc + cmd = ['pkg-config', '--variable', 'xcbincludedir', 'xcb-proto'] + xcbdir = subprocess.check_output(cmd).strip() + events = {} + for mod in sys.argv[1:]: + summary = io.BytesIO() + fulldoc = io.BytesIO() + indent(summary) + path = os.path.join(xcbdir, mod + '.xml') + print('generate {}...'.format(mod)) + + t = load(mod, path) + addevents(events, t) + generate(t) + + out = '{}-doc.txt'.format(mod) + with open(out, 'w') as f: + f.write(summary.getvalue()) + f.write(fulldoc.getvalue()) + outdent(summary) + outdent(fulldoc) + summary.close() + + summary = io.BytesIO() + geneventunion(summary, sys.argv[1:], events) + with open('events-merged.txt', 'w') as f: + f.write(summary.getvalue()) + +if __name__ == '__main__': + main() |