diff --git a/ChangeLog b/ChangeLog index 39a7e0e90dd6119342856bd3ed3a021b661f132d..14789d9024d4920d22249c7494c34a78dad4e23c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,13 @@ 2022-05-30 Per Cederqvist <ceder@lysator.liu.se> + checkargs: Generate protocol-a.json. + * doc/checkargs.py: Generate protocol-a.json. This is basically + the same info as in protocol-a-full.txt, but in a format that is + easier for a program to parse. It is less easy for a human to + read, though. + * doc/Makefile.am (DISTCLEANFILES): Add protocol-a.json. + * doc/.gitignore: Ignore protocol-a.json. + checkargs: Simplify argument processing. * doc/checkargs.py (lexer.__parse_request_arg): Append the argument to the request object, so that the caller doesn't have to diff --git a/doc/.gitignore b/doc/.gitignore index 205306c9b14a646e105abae208a74259f91dcb01..bbbb64dd30bd081039f27df99132457eedbcd358 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -43,6 +43,7 @@ protocol-a.fns protocol-a.html protocol-a.info protocol-a.info-* +protocol-a.json protocol-a.ky protocol-a.log protocol-a.pdf diff --git a/doc/Makefile.am b/doc/Makefile.am index 8733267a1cbf56ce695802a574d7f0dc55da0a4b..c53ab4bf68d7c184acd4b67e5c25aee99675935c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -35,7 +35,7 @@ MOSTLYCLEANFILES = Protocol-A.notab lyskomd.notab *.tmp \ Protocol-A.dvi DISTCLEANFILES = Protocol-A.pdf protocol-a-current.txt \ - protocol-a-recommended.txt protocol-a-full.txt + protocol-a-recommended.txt protocol-a-full.txt protocol-a.json EXTRA_DIST = \ IDEAS \ diff --git a/doc/checkargs.py b/doc/checkargs.py index 48f42d23605aec8d8e2d68265a1ae9dd6da07b2c..2d89f0322974cf3d3bb42a042b5a45722f3578f8 100644 --- a/doc/checkargs.py +++ b/doc/checkargs.py @@ -28,6 +28,7 @@ import sys import types +import json import re import os @@ -1376,6 +1377,9 @@ class lexer: typedef = prot_a_alternate(line, name, token) token = self.__get_token() else: + if array: + sys.stderr.write("unexpected ARRAY for simple\n") + sys.exit(1) typedef = prot_a_simple(line, name, array) if token != ';': @@ -2362,6 +2366,169 @@ def generate_summary_output(filename): fp.close() os.rename(filename + ".tmp", filename) +def generate_json_output(filename): + m = {} + m["%%PROTOEDITION"] = set_values["PROTOEDITION"] + m["%%PROTOVER"] = set_values["PROTOVER"] + m["%%LYSKOMDVERSION"] = set_values["VERSION"] + + tlist = list(tt.keys()) + tlist.sort() + + for tn in tlist: + if tn != tt[tn] and tn not in ["Conf-List-Archaic"]: + m.setdefault("type-alias", {})[tt[tn]] = tn + + rlist = list(rt.keys()) + rlist.sort() + + for rn in rlist: + if rn != rt[rn] and rn not in ["lookup-name-1"]: + m.setdefault("request-alias", {})[rt[rn]] = rn + + alist = list(at.keys()) + alist.sort() + + for an in alist: + if an != at[an]: + m.setdefault("async-alias", {})[at[an]] = an + + tlist = list(tr.keys()) + tlist.sort() + + for tn in tlist: + if tn in ["Conf-List-Archaic-1"]: + continue + t = defined_types[tr[tn]] + if isinstance(t, prot_a_builtin): + m.setdefault("builtin", []).append(tn) + elif isinstance(t, prot_a_simple): + if t.array(): + m.setdefault("array-types", {})[tn] = tt[t.base_type()] + else: + m.setdefault("simple-types", {})[tn] = tt[t.base_type()] + elif isinstance(t, prot_a_alternate): + m.setdefault("derived-types", {})[tn] = ( + "union", tt[t.type_a()], tt[t.type_b()]) + elif isinstance(t, prot_a_struct): + lst = [] + for field_name, field_type, is_array in t.fields(): + field = { + 'name': field_name, + 'type': field_type, + } + if is_array: + field["array"] = True + lst.append(field) + m.setdefault("derived-types", {})[tn] = ("struct", lst) + elif isinstance(t, prot_a_bitstring): + m.setdefault("derived-types", {})[tn] = ("bitstring", t.bits()) + elif isinstance(t, prot_a_selection): + alts = {} + for (nr, name, tailname, tailtype, array) in t.fields(): + nr = int(nr) + alts[name] = { + 'tag': nr, + 'tailname': tailname, + 'type': tailtype, + } + if array: + alts[name]["array"] = True + m.setdefault("derived-types", {})[tn] = ("selection", alts) + elif isinstance(t, prot_a_enumeration_of): + m.setdefault("derived-types", {})[tn] = ("enumeration-of", + tt[t.base_type()]) + else: + sys.stderr.write("bad type %s" % repr(t)) + sys.exit(1) + + for rn in rlist: + if rn in ["lookup-name-1"]: + continue + r = defined_request_names[rn] + info = { + 'tag': int(r.request_nr()), + 'name': rn, + 'stable-name': rt[rn], + 'protocol-version': int(r.protover()), + } + if r.recommended(): + info["status"] = "recommended" + elif r.experimental(): + info["status"] = "experimental" + elif r.obsolete(): + info["status"] = "obsolete" + info["obsoleted-by"] = int(r.obsver()) + else: + sys.stderr.write("No status found\n") + sys.exit(1) + info["documented-errors"] = r.error_codes() + args = [] + for argname, argtype, argarray in r.arguments(): + a = { + "name": argname, + "type": argtype, + "stable-type": tt[argtype], + } + if argarray: + a["array"] = True + args.append(a) + info["args"] = args + if r.return_type() is None: + info["return-type"] = None + else: + a = { + "type": r.return_type(), + "stable-type": tt[r.return_type()], + } + if r.array(): + a["array"] = True + info["return-type"] = a + m.setdefault("requests", {})[rt[r.request_name()]] = info + + alist = list(ar.keys()) + alist.sort() + + for an in alist: + r = defined_async_names[ar[an]] + info = { + 'tag': int(r.request_nr()), + 'name': ar[an], + 'stable-name': an, + 'protocol-version': int(r.protover()), + } + if r.recommended(): + info["status"] = "recommended" + elif r.experimental(): + info["status"] = "experimental" + elif r.obsolete(): + info["status"] = "obsolete" + info["obsoleted-by"] = int(r.obsver()) + else: + sys.stderr.write("No status found\n") + sys.exit(1) + + args = [] + for argname, argtype, argarray in r.arguments(): + a = { + "name": argname, + "type": argtype, + "stable-type": tt[argtype], + } + if argarray: + a["array"] = True + args.append(a) + info["args"] = args + m.setdefault("asyncs", {})[an] = info + + m["errors"] = defined_error_codes + + fp = open(filename + ".tmp", "w") + fp.write(json.dumps(m, sort_keys=True, indent=2)) + fp.close() + os.rename(filename + ".tmp", filename) + return + if __name__ == '__main__': l = lexer(sys.argv[1]) ret = l.run() @@ -2369,6 +2536,7 @@ if __name__ == '__main__': generate_stable_output("protocol-a-full.txt", 0) generate_stable_output("protocol-a-recommended.txt", 1) generate_summary_output("protocol-a-current.txt") + generate_json_output("protocol-a.json") elif ret == 1: sys.exit(1) else: