summaryrefslogtreecommitdiff
path: root/jsongen.py
diff options
context:
space:
mode:
authorEric S. Raymond <esr@thyrsus.com>2009-11-26 05:36:30 +0000
committerEric S. Raymond <esr@thyrsus.com>2009-11-26 05:36:30 +0000
commit4e2ca0e299b64e5eaa87e3a42af35a021db3c466 (patch)
treee71e48aa55f60bb6e631571fd9303322a6de5afd /jsongen.py
parentc1986735fcb98805d3eccdacc3e72731ca89f20c (diff)
downloadgpsd-4e2ca0e299b64e5eaa87e3a42af35a021db3c466.tar.gz
This generator file should have been in the repo long since.
Diffstat (limited to 'jsongen.py')
-rwxr-xr-xjsongen.py492
1 files changed, 492 insertions, 0 deletions
diff --git a/jsongen.py b/jsongen.py
new file mode 100755
index 00000000..bf8ec671
--- /dev/null
+++ b/jsongen.py
@@ -0,0 +1,492 @@
+#!/usr/bin/python
+#
+# Never hand-hack what you can generate...
+#
+# This code generates template declarations for AIS-JSON parsing from a
+# declarative specification of a JSON structure - and generates those
+# declarative specification from the Python format strings for
+# dumping the structures
+#
+import sys, getopt
+
+#
+# Here is the information that makes it all work - attribute, type, and
+# defult information for all fields. We can generate either a JSON
+# parser template spec or the C code for the correspondong dump functio
+# from this information. Doing it this way guarantees consistency.
+#
+ais_specs = (
+ {
+ "initname" : "json_ais1",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type1",
+ "fieldmap":(
+ # fieldname type default
+ ('status', 'uinteger', '0'),
+ ('turn', 'integer', 'AIS_TURN_NOT_AVAILABLE'),
+ ('speed', 'uinteger', 'AIS_SPEED_NOT_AVAILABLE'),
+ ('accuracy', 'boolean', 'false'),
+ ('lon', 'integer', 'AIS_LON_NOT_AVAILABLE'),
+ ('lat', 'integer', 'AIS_LAT_NOT_AVAILABLE'),
+ ('course', 'uinteger', 'AIS_COURSE_NOT_AVAILABLE'),
+ ('heading', 'uinteger', 'AIS_HEADING_NOT_AVAILABLE'),
+ ('second', 'uinteger', 'AIS_SEC_NOT_AVAILABLE'),
+ ('maneuver', 'uinteger', 'AIS_SEC_INOPERATIVE'),
+ ('raim', 'boolean', 'false'),
+ ('radio', 'uinteger', '0'),
+ ),
+ },
+ # Message types 2 and 3 duplicate 1
+ {
+ "initname" : "json_ais4",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type4",
+ "fieldmap":(
+ # fieldname type default
+ ('timestamp', 'string', None),
+ ('accuracy', 'boolean', "true"),
+ ('lon', 'integer', "AIS_LON_NOT_AVAILABLE"),
+ ('lat', 'integer', "AIS_LAT_NOT_AVAILABLE"),
+ ('epfd', 'uinteger', "0"),
+ ('raim', 'boolean', "false"),
+ ('radio', 'uinteger', "0"),
+ ),
+ "stringbuffered":("timestamp",),
+ },
+ {
+ "initname" : "json_ais5",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type5",
+ "fieldmap":(
+ # fieldname type default
+ ('imo', 'uinteger', '0'),
+ ('ais_version', 'uinteger', '0'),
+ ('callsign', 'string', None),
+ ('shipname', 'string', None),
+ ('shiptype', 'uinteger', '0'),
+ ('to_bow', 'uinteger', '0'),
+ ('to_stern', 'uinteger', '0'),
+ ('to_port', 'uinteger', '0'),
+ ('to_starboard', 'uinteger', '0'),
+ ('epfd', 'uinteger', '0'),
+ ('eta', 'string', None),
+ ('draught', 'uinteger', '0'),
+ ('destination', 'string', None),
+ ('dte', 'uinteger', '1'),
+ ),
+ "stringbuffered":("eta",),
+ },
+ {
+ "initname" : "json_ais6",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type6",
+ "fieldmap":(
+ # fieldname type default
+ ('seqno', 'uinteger', '0'),
+ ('dest_mmsi', 'uinteger', '0'),
+ ('retransmit', 'boolean', 'false'),
+ ('app_id', 'uinteger', '0'),
+ ('data', 'string', None),
+ ),
+ "stringbuffered":("data",),
+ },
+ {
+ "initname" : "json_ais7",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type7",
+ "fieldmap":(
+ # fieldname type default
+ ('mmsi1', 'uinteger', '0'),
+ ('mmsi2', 'uinteger', '0'),
+ ('mmsi3', 'uinteger', '0'),
+ ('mmsi4', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais8",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type8",
+ "fieldmap":(
+ # fieldname type default
+ ('app_id', 'uinteger', '0'),
+ ('data', 'string', None),
+ ),
+ "stringbuffered":("data",),
+ },
+ {
+ "initname" : "json_ais9",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type9",
+ "fieldmap":(
+ # fieldname type default
+ ('alt', 'uinteger', 'AIS_ALT_NOT_AVAILABLE'),
+ ('speed', 'uinteger', 'AIS_SPEED_NOT_AVAILABLE'),
+ ('accuracy', 'boolean', 'false'),
+ ('lon', 'integer', 'AIS_LON_NOT_AVAILABLE'),
+ ('lat', 'integer', 'AIS_LAT_NOT_AVAILABLE'),
+ ('course', 'uinteger', 'AIS_COURSE_NOT_AVAILABLE'),
+ ('second', 'uinteger', 'AIS_SEC_NOT_AVAILABLE'),
+ ('regional', 'uinteger', '0'),
+ ('dte', 'uinteger', '1'),
+ ('raim', 'boolean', 'false'),
+ ('radio', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais10",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type10",
+ "fieldmap":(
+ # fieldname type default
+ ('dest_mmsi', 'uinteger', '0'),
+ ),
+ },
+ # Message type 11 duplicates 4
+ {
+ "initname" : "json_ais12",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type12",
+ "fieldmap":(
+ # fieldname type default
+ ('seqno', 'uinteger', '0'),
+ ('dest_mmsi', 'uinteger', '0'),
+ ('retransmit', 'boolean', '0'),
+ ('text', 'string', None),
+ ),
+ },
+ # Message type 13 duplicates 7
+ {
+ "initname" : "json_ais14",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type14",
+ "fieldmap":(
+ # fieldname type default
+ ('text', 'string', None),
+ ),
+ },
+ {
+ "initname" : "json_ais15",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type15",
+ "fieldmap":(
+ # fieldname type default
+ ('mmsi1', 'uinteger', '0'),
+ ('type1_1', 'uinteger', '0'),
+ ('offset1_1', 'uinteger', '0'),
+ ('type1_2', 'uinteger', '0'),
+ ('offset1_2', 'uinteger', '0'),
+ ('mmsi2', 'uinteger', '0'),
+ ('type2_1', 'uinteger', '0'),
+ ('offset2_1', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais16",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type16",
+ "fieldmap":(
+ # fieldname type default
+ ('mmsi1', 'uinteger', '0'),
+ ('offset1', 'uinteger', '0'),
+ ('increment1', 'uinteger', '0'),
+ ('mmsi2', 'uinteger', '0'),
+ ('offset2', 'uinteger', '0'),
+ ('increment2', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais17",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type17",
+ "fieldmap":(
+ # fieldname type default
+ ('lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('data', 'string', None),
+ ),
+ "stringbuffered":("data",),
+ },
+ {
+ "initname" : "json_ais18",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type18",
+ "fieldmap":(
+ # fieldname type default
+ ('reserved', 'uinteger', '0'),
+ ('speed', 'uinteger', 'AIS_SPEED_NOT_AVAILABLE'),
+ ('accuracy', 'boolean', 'false'),
+ ('lon', 'integer', 'AIS_LON_NOT_AVAILABLE'),
+ ('lat', 'integer', 'AIS_LAT_NOT_AVAILABLE'),
+ ('course', 'uinteger', 'AIS_COURSE_NOT_AVAILABLE'),
+ ('heading', 'uinteger', 'AIS_HEADING_NOT_AVAILABLE'),
+ ('second', 'uinteger', 'AIS_SEC_NOT_AVAILABLE'),
+ ('regional', 'uinteger', '0'),
+ ('cs', 'boolean', 'false'),
+ ('display', 'boolean', 'false'),
+ ('dsc', 'boolean', 'false'),
+ ('band', 'boolean', 'false'),
+ ('msg22', 'boolean', 'false'),
+ ('raim', 'boolean', 'false'),
+ ('radio', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais19",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type19",
+ "fieldmap":(
+ # fieldname type default
+ ('reserved', 'uinteger', '0'),
+ ('speed', 'uinteger', 'AIS_SPEED_NOT_AVAILABLE'),
+ ('accuracy', 'boolean', 'false'),
+ ('lon', 'integer', 'AIS_LON_NOT_AVAILABLE'),
+ ('lat', 'integer', 'AIS_LAT_NOT_AVAILABLE'),
+ ('course', 'uinteger', 'AIS_COURSE_NOT_AVAILABLE'),
+ ('heading', 'uinteger', 'AIS_HEADING_NOT_AVAILABLE'),
+ ('second', 'uinteger', 'AIS_SEC_NOT_AVAILABLE'),
+ ('regional', 'uinteger', '0'),
+ ('shipname', 'string', None),
+ ('shiptype', 'uinteger', '0'),
+ ('to_bow', 'uinteger', '0'),
+ ('to_stern', 'uinteger', '0'),
+ ('to_port', 'uinteger', '0'),
+ ('to_starboard', 'uinteger', '0'),
+ ('epfd', 'uinteger', '0'),
+ ('raim', 'boolean', 'false'),
+ ('dte', 'uinteger', '1'),
+ ('assigned', 'boolean', 'false'),
+ ),
+ },
+ {
+ "initname" : "json_ais20",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type20",
+ "fieldmap":(
+ # fieldname type default
+ ('offset1', 'uinteger', '0'),
+ ('number1', 'uinteger', '0'),
+ ('timeout1', 'uinteger', '0'),
+ ('increment1', 'uinteger', '0'),
+ ('offset2', 'uinteger', '0'),
+ ('number2', 'uinteger', '0'),
+ ('timeout2', 'uinteger', '0'),
+ ('increment2', 'uinteger', '0'),
+ ('offset3', 'uinteger', '0'),
+ ('number3', 'uinteger', '0'),
+ ('timeout3', 'uinteger', '0'),
+ ('increment3', 'uinteger', '0'),
+ ('offset4', 'uinteger', '0'),
+ ('number4', 'uinteger', '0'),
+ ('timeout4', 'uinteger', '0'),
+ ('increment4', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais21",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type21",
+ "fieldmap":(
+ # fieldname type default
+ ('aid_type', 'uinteger', '0'),
+ ('name', 'string', None),
+ ('accuracy', 'boolean', 'false'),
+ ('lon', 'integer', 'AIS_LON_NOT_AVAILABLE'),
+ ('lat', 'integer', 'AIS_LAT_NOT_AVAILABLE'),
+ ('to_bow', 'uinteger', '0'),
+ ('to_stern', 'uinteger', '0'),
+ ('to_port', 'uinteger', '0'),
+ ('to_starboard', 'uinteger', '0'),
+ ('epfd', 'uinteger', '0'),
+ ('second', 'uinteger', '0'),
+ ('regional', 'uinteger', '0'),
+ ('off_position', 'boolean', 'false'),
+ ('raim', 'boolean', 'false'),
+ ('virtual_aid', 'boolean', 'false'),
+ ),
+ },
+ {
+ "initname" : "json_ais22",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type22",
+ "fieldmap":(
+ # fieldname type default
+ ('channel_a', 'uinteger', '0'),
+ ('channel_b', 'uinteger', '0'),
+ ('txrx', 'uinteger', '0'),
+ ('power', 'boolean', 'false'),
+ ('ne_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('ne_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('sw_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('sw_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('addressed', 'boolean', 'false'),
+ ('band_a', 'boolean', 'false'),
+ ('band_b', 'boolean', 'false'),
+ ('zonesize', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais23",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type23",
+ "fieldmap":(
+ # fieldname type default
+ ('ne_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('ne_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('sw_lon', 'integer', 'AIS_GNS_LON_NOT_AVAILABLE'),
+ ('sw_lat', 'integer', 'AIS_GNS_LAT_NOT_AVAILABLE'),
+ ('stationtype', 'uinteger', '0'),
+ ('shiptype', 'uinteger', '0'),
+ ('txrx', 'uinteger', '0'),
+ ('interval', 'uinteger', '0'),
+ ('quiet', 'uinteger', '0'),
+ ),
+ },
+ {
+ "initname" : "json_ais24",
+ "header": "\tAIS_HEADER,",
+ "structname": "ais->type24",
+ "fieldmap":(
+ # fieldname type default
+ ('shipname', 'string', None), # Part A
+ ('shiptype', 'uinteger', '0'), # Part B
+ ('vendorid', 'string', None), # Part B
+ ('callsign', 'string', None), # Part B
+ ('mothership_mmsi', 'uinteger', '0'), # Part B
+ ('dim.to_bow', 'uinteger', '0'), # Part B
+ ('dim.to_stern', 'uinteger', '0'), # Part B
+ ('dim.to_port', 'uinteger', '0'), # Part B
+ ('dim.to_starboard', 'uinteger', '0'), # Part B
+ ),
+ },
+)
+
+
+# Give this global the string spec you need to convert with -g
+# We do it this mildly odd way only because passing Python multiline
+# string literals on the command line is inconvenient.
+stringspec = ""
+
+# You should not need to modify anything below this line.
+
+def generate(spec):
+ global outboard
+ for (attr, itype, default) in spec["fieldmap"]:
+ if attr in spec.get("stringbuffered", []):
+ if attr not in outboard:
+ print " char %s[JSON_VAL_MAX+1];" % attr
+ outboard.append(attr)
+ print " const struct json_attr_t %s[] = {" % spec["initname"]
+ if "header" in spec:
+ print spec["header"]
+ for (attr, itype, default) in spec["fieldmap"]:
+ structname = spec["structname"]
+ if itype == "string":
+ deref = ""
+ else:
+ deref = "&"
+ if attr in spec.get("stringbuffered", []):
+ target = attr
+ else:
+ target = structname + "." + attr
+ if "." in attr:
+ attr = attr[attr.rfind(".")+1:]
+ print '\t{"%s",%s%s,%s.addr.%s = %s%s,' % \
+ (attr, " "*(12-len(attr)), itype, " "*(10-len(itype)), itype, deref, target)
+ leader = " " * 35
+ if itype == "string":
+ print leader + ".len = sizeof(%s)}," % target
+ else:
+ print leader + ".dflt.%s = %s}," % (itype, default)
+
+ print """\
+ {NULL},
+ };
+"""
+
+# No code for generating dump functions yet.
+# When we do this, remembeer that we have to suppress dump function
+# generation from any spec with a union dot in the field names;
+# those will have to be coded by hand.
+
+def string_to_specifier(strspec):
+ "Compile a Python-style format string to an attribute-type fieldmap."
+ # Map C and Python-type format letters to JSON parser datatypes
+ fmtmap = {
+ "d": "integer",
+ "u": "uinteger",
+ "f": "real",
+ "s": "boolean", # Only booleans print as unquoted strings
+ "\"": "string",
+ }
+ dftmap = {
+ "integer": "0",
+ "uinteger": "0",
+ "real": "0.0",
+ "string": None,
+ "boolean": "false"
+ }
+ strspec = strspec.strip()
+ if strspec[-1] == "}":
+ strspec = strspec[:-1]
+ else:
+ print "Missing terminating }"
+ sys.exit(1)
+ print ' "fieldmap":('
+ for item in strspec.split(","):
+ itype = fmtmap[item[-1]]
+ attr = item[:item.find(":")]
+ if attr[0] == '"':
+ attr = attr[1:]
+ if attr[-1] == '"':
+ attr = attr[:-1]
+ dflt = dftmap[itype]
+ if dflt is not None:
+ dflt = `dflt`
+ print " ('%s',%s'%s',%s%s)," % (attr, " "*(14-len(attr)), itype, " "*(14-len(itype)), dflt)
+ print " )"
+
+
+if __name__ == '__main__':
+ try:
+ # The --ais and --target= options are (required) placeho;lders.
+ # In the future, this script will generate more different kinds
+ # of code.
+ (options, arguments) = getopt.getopt(sys.argv[1:], "g", ["ais", "target="])
+ except getopt.GetoptError, msg:
+ print "jsongen.py: " + str(msg)
+ raise SystemExit, 1
+
+ specify = False
+ spec = None
+ target = None
+ for (switch, val) in options:
+ if switch == '-g':
+ specify = True
+ elif switch == '--ais':
+ spec = ais_specs
+ elif switch == '--target':
+ target = val
+
+ if not specify and (not target or not spec):
+ print "jsongen.py: must specify type and target."
+ sys,exit(1)
+
+
+ if specify:
+ string_to_specifier(stringspec)
+ else:
+ print """/*
+ * This is code generated by jsongen.py. Do not hand-hack it!
+ */
+
+/*@ -fullinitblock */
+
+"""
+ outboard = []
+ for description in spec:
+ generate(description)
+ print """
+/*@ +fullinitblock */
+
+/* Generated code ends. */
+"""