#!/usr/bin/env python3 # # A primitive script to parse lua_api.txt for formspec elements. # import copy, lua_dump, os, urllib.request def _make_known(**kwargs): known = {} for k, v in kwargs.items(): for i in v: known[i] = k return known _known = _make_known( number=('x', 'y', 'w', 'h', 'selected_idx', 'version', 'starting_item_index'), boolean=('auto_clip', 'fixed_size', 'transparent', 'draw_border', 'bool', 'fullscreen', 'noclip', 'drawborder', 'selected'), table=('param', 'opt', 'prop'), null=('',), ) def _get_name(n): if not isinstance(n, tuple) or n[1] == '...': return '...' return n[0][:-1].rsplit('_', 1)[0].rstrip('_') _aliases = { 'type': 'elem_type', 'cell': 'cells', } def _fix_param(param): if isinstance(param, str): if ',' not in param: param = param.lower().strip().strip('<>').replace(' ', '_') param = _aliases.get(param, param) return (param, _known.get(param, 'string')) param = param.split(',') res = [] for p in param: if p != '...' or not res: res.append(_fix_param(p)) continue last = res.pop() # Workaround if res and last and isinstance(last, list) and \ last[0][0].endswith('2') and isinstance(res[-1], list) and \ res[-1] and res[-1][0][0].endswith('1'): last = res.pop() last[0] = (last[0][0][:-2], last[0][1]) name = _get_name(last) if name == '...': res.append((last, '...')) else: while res and _get_name(res[-1]) == name: res.pop() res.append((_fix_param(name), '...')) break return res _hooks = {} def hook(name): def add_hook(func): _hooks[name] = func return func return add_hook # Fix background9 @hook('background9') def _background9_hook(params): assert params[-1] == ('middle', 'string') params[-1] = param = [] param.append(('middle_x', 'number')) yield params param.append(('middle_y', 'number')) yield params param.append(('middle_x2', 'number')) param.append(('middle_y2', 'number')) yield params def _raw_parse(data): data = data.split('\nElements\n--------\n', 1)[-1].split('\n----', 1)[0] for line in data.split('\n'): if not line.startswith('### `') or not line.endswith('`'): continue elem = line[5:-2] name, params = elem.split('[', 1) if params: params = _fix_param(params.split(';')) else: params = [] if name in _hooks: for p in reversed(tuple(map(copy.deepcopy, _hooks[name](params)))): yield name, p else: yield name, params def parse(data): """ Returns a dict: { 'element_name': [ ['param1', 'param2'], ['alternate_params'], ] } """ res = {} for k, v in _raw_parse(data): if k not in res: res[k] = [] res[k].append(v) for v in res.values(): v.sort(key=len, reverse=True) return res URL = 'https://github.com/minetest/minetest/raw/master/doc/lua_api.txt' def fetch_and_parse(*, url=URL): with urllib.request.urlopen(url) as f: raw = f.read() return parse(raw.decode('utf-8', 'replace')) _comment = """ -- -- Formspec elements list. Do not update this by hand, it is auto-generated -- by make_elements.py. -- """ def main(): dirname = os.path.dirname(__file__) filename = os.path.join(dirname, 'elements.lua') print('Writing to ' + filename + '...') with open(filename, 'w') as f: f.write(_comment.lstrip()) f.write(lua_dump.serialize(fetch_and_parse())) # elems = fetch_and_parse() # for elem in sorted(elems): # for def_ in elems[elem]: # f.write('formspec_ast.register_element({}, {})\n'.format( # lua_dump.dump(elem), lua_dump.dump(def_) # )) print('Done.') if __name__ == '__main__': main()