tablegen.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670
  1. #!/usr/bin/env python3
  2. # This code runs compatibly under Python 2 and 3.x for x >= 2.
  3. # Preserve this property!
  4. """Generate AIS code from text file.
  5. This tool is intended to automate away the drudgery in bring up support
  6. for a new AIS message type. It parses the tabular description of a message
  7. and generates various useful code snippets from that. It can also be used to
  8. correct offsets in the tables themselves.
  9. Requires the AIVDM.txt file on standard input. Takes a single argument,
  10. which must match a string in a //: Type comment. Things you can generate:
  11. * -t: A corrected version of the table. It will redo all the offsets to be
  12. in conformance with the bit widths. (The other options rely only on the
  13. bit widths). If the old and new tables are different, an error message
  14. describing the corrections will be emitted to standard error.
  15. * -s: A structure definition capturing the message info, with member
  16. names extracted from the table and types computed from it.
  17. * -c: Bit-extraction code for the AIVDM driver. Grind out the right sequence
  18. of UBITS, SBITS, and UCHARS macros, and assignments to structure members,
  19. guaranteed correct if the table offsets and widths are.
  20. * -d: Code to dump the contents of the unpacked message structure as JSON. If
  21. the structure has float members, you'll get an if/then/else guarded by
  22. the scaled flag.
  23. * -r: A Python initializer stanza for jsongen.py, which is in turn used to
  24. generate the specification structure for a JSON parse that reads JSON
  25. into an instance of the message structure.
  26. * -a: Generate all of -s, -d, -c, and -r, and -t, not to stdout but to
  27. files named with 'tablegen' as a distinguishing part of the stem.
  28. The stem name can be overridden with the -o option.
  29. This generates almost all the code required to support a new message type.
  30. It's not quite "Look, ma, no handhacking!" You'll need to add default
  31. values to the Python stanza. If the structure definition contains character
  32. arrays, you'll have to fill in the dimensions by hand. You'll need to add
  33. a bit of glue to ais_json.c so that json_ais_read() actually calls the parser
  34. handing it the specification structure as a control argument.
  35. The -a, -c, -s, -d, and -r modes all take an argument, which should be a
  36. structure reference prefix to be prepended (before a dot) to each fieldname.
  37. Usually you'll need this to look something like "ais->typeN", but it could be
  38. "ais->typeN.FOO" if the generated code has to operate on a union member
  39. inside a type 6 or 8, or something similar.
  40. The -S and -E options allow you to generate code only for a specified span
  41. of fields in the table. This may be useful for dealing with groups of
  42. messages that have a common head section.
  43. This code interprets magic comments in the input
  44. //: Type
  45. The token following "Type" is the name of the table
  46. //: xxxx vocabulary
  47. A subtable describing a controlled vocabulary for field xxxx in the
  48. preceding table.
  49. TO-DO: generate code for ais.py.
  50. """
  51. from __future__ import absolute_import, print_function, division
  52. import getopt
  53. import sys
  54. def correct_table(wfp):
  55. """Writes the corrected table."""
  56. print("Total bits:", base, file=sys.stderr)
  57. for (idx, t) in enumerate(table):
  58. if offsets[idx].strip():
  59. print("|" + offsets[idx] + t[owidth+1:].rstrip(), file=wfp)
  60. else:
  61. print(t.rstrip(), file=wfp)
  62. def make_driver_code(wfp):
  63. """Writes calls to bit-extraction macros.
  64. Requires UBITS, SBITS, UCHARS to act as they do in the AIVDM driver.
  65. Also relies on bitlen to be the message bit length, and i to be
  66. available as abn index variable."""
  67. record = after is None
  68. arrayname = None
  69. lbase = '\t'
  70. step = " " * 4
  71. indent = lbase
  72. last = 0
  73. for (i, t) in enumerate(table):
  74. if '|' in t:
  75. filds = [s.strip() for s in t.split('|')]
  76. width = filds[2]
  77. name = filds[4]
  78. ftype = filds[5]
  79. if after == name:
  80. record = True
  81. continue
  82. if before == name:
  83. record = False
  84. continue
  85. if not record:
  86. continue
  87. if ftype == 'x':
  88. print("\t/* skip %s bit%s */" % (width,
  89. ["", "s"][width > '1']), file=wfp)
  90. continue
  91. if ftype[0] == 'a':
  92. arrayname = name
  93. explicit = ftype[1] == '^'
  94. print('#define ARRAY_BASE %s' % offsets[i].strip(), file=wfp)
  95. print('#define ELEMENT_SIZE %s' % trailing, file=wfp)
  96. if explicit:
  97. lengthfield = last
  98. print(indent + "for (i = 0; i < %s; i++) {" % lengthfield,
  99. file=wfp)
  100. else:
  101. lengthfield = "n" + arrayname
  102. print(indent + "for (i = 0; ARRAY_BASE + "
  103. "(ELEMENT_SIZE*i) < bitlen; i++) {", file=wfp)
  104. indent += step
  105. print(indent + "int a = ARRAY_BASE + (ELEMENT_SIZE*i);",
  106. file=wfp)
  107. continue
  108. offset = offsets[i].split('-')[0]
  109. if arrayname:
  110. target = "%s.%s[i].%s" % (structnme, arrayname, name)
  111. offset = "a + " + offset
  112. else:
  113. target = "%s.%s" % (structname, name)
  114. if ftype[0].lower() in ('u', 'i', 'e'):
  115. print(indent + "%s\t= %sBITS(%s, %s);" %
  116. (target,
  117. {'u': 'U', 'e': 'U', 'i': 'S'}[ftype[0].lower()],
  118. offset, width), file=wfp)
  119. elif ftype == 't':
  120. print(indent + "UCHARS(%s, %s);" % (offset, target), file=wfp)
  121. elif ftype == 'b':
  122. print(indent + "%s\t= (bool)UBITS(%s, 1);" % (target, offset),
  123. file=wfp)
  124. else:
  125. print(indent + "/* %s bits of type %s */" %
  126. (width, ftype), file=wfp)
  127. last = name
  128. if arrayname:
  129. indent = lbase
  130. print(indent + "}", file=wfp)
  131. if not explicit:
  132. print(indent + "%s.%s = ind;" %
  133. (structname, lengthfield), file=wfp)
  134. print("#undef ARRAY_BASE", file=wfp)
  135. print("#undef ELEMENT_SIZE", file=wfp)
  136. def make_structure(wfp):
  137. """Write a structure definition corresponding to the table."""
  138. global structname
  139. record = after is None
  140. last = 0
  141. baseindent = 8
  142. step = 4
  143. inwards = step
  144. arrayname = None
  145. def tabify(n):
  146. """convert to tabs."""
  147. return ('\t' * (n // 8)) + (" " * (n % 8))
  148. print(tabify(baseindent) + "struct {", file=wfp)
  149. for (i, t) in enumerate(table):
  150. if '|' in t:
  151. fields = [s.strip() for s in t.split('|')]
  152. width = fields[2]
  153. description = fields[3].strip()
  154. name = fields[4]
  155. ftype = fields[5]
  156. if after == name:
  157. record = True
  158. continue
  159. if before == name:
  160. record = False
  161. continue
  162. if ftype == 'x' or not record:
  163. continue
  164. if ftype[0] == 'a':
  165. arrayname = name
  166. if ftype[1] == '^':
  167. lengthfield = last
  168. ftype = ftype[1:]
  169. else:
  170. lengthfield = "n%s" % arrayname
  171. print(tabify(baseindent + inwards) +
  172. "signed int %s;" % lengthfield, file=wfp)
  173. if arrayname.endswith("s"):
  174. typename = arrayname[:-1]
  175. else:
  176. typename = arrayname
  177. print(tabify(baseindent + inwards) + "struct %s_t {" %
  178. typename, file=wfp)
  179. inwards += step
  180. arraydim = ftype[1:]
  181. continue
  182. elif ftype == 'u' or ftype == 'e' or ftype[0] == 'U':
  183. decl = "unsigned int %s;\t/* %s */" % (name, description)
  184. elif ftype == 'i' or ftype[0] == 'I':
  185. decl = "signed int %s;\t/* %s */" % (name, description)
  186. elif ftype == 'b':
  187. decl = "bool %s;\t/* %s */" % (name, description)
  188. elif ftype == 't':
  189. stl = int(width) // 6
  190. decl = "char %s[%d+1];\t/* %s */" % (name, stl, description)
  191. else:
  192. decl = "/* %s bits of type %s */" % (width, ftype)
  193. print(tabify(baseindent + inwards) + decl, file=wfp)
  194. last = name
  195. if arrayname:
  196. inwards -= step
  197. print(tabify(baseindent + inwards) + "} %s[%s];"
  198. % (arrayname, arraydim), file=wfp)
  199. if "->" in structname:
  200. typename = structname.split("->")[1]
  201. if "." in typename:
  202. structname = structname.split(".")[1]
  203. print(tabify(baseindent) + "} %s;" % typename, file=wfp)
  204. def make_json_dumper(wfp):
  205. """Write the skeleton of a JSON dump corresponding to the table.
  206. Also, if there are subtables, some initializers."""
  207. last = 0
  208. if subtables:
  209. for (name, lines) in subtables:
  210. wfp.write(" const char *%s_vocabulary[] = {\n" % name)
  211. for ln in lines:
  212. value = ln[1]
  213. if value.endswith(" (default)"):
  214. value = value[:-10]
  215. wfp.write(' "%s",\n' % value)
  216. wfp.write(" };\n")
  217. wfp.write('#define DISPLAY_%s(n) (((n) < '
  218. '(unsigned int)NITEMS(%s_vocabulary)) ? '
  219. '%s_vocabulary[n] : "INVALID %s")\n' %
  220. (name.upper(), name, name, name.upper()))
  221. wfp.write("\n")
  222. record = after is None
  223. # Elements of each tuple type except 'a':
  224. # 1. variable name,
  225. # 2. unscaled printf format
  226. # 3. wrapper for unscaled variable reference
  227. # 4. scaled printf format
  228. # 5. wrapper for scaled variable reference
  229. # Elements of 'a' tuple:
  230. # 1. Name of array field
  231. # 2. None
  232. # 3. None
  233. # 4. None
  234. # 5. Name of length field
  235. tuples = []
  236. vocabularies = [x[0] for x in subtables]
  237. for (i, t) in enumerate(table):
  238. if '|' in t:
  239. fields = [s.strip() for s in t.split('|')]
  240. name = fields[4]
  241. ftype = fields[5]
  242. if after == name:
  243. record = True
  244. continue
  245. if before == name:
  246. record = False
  247. continue
  248. if ftype == 'x' or not record:
  249. continue
  250. fmt = r'\"%s\":' % name
  251. fmt_text = r'\"%s_text\":' % name
  252. if ftype == 'u':
  253. tuples.append((name,
  254. fmt+"%u", "%s",
  255. None, None))
  256. elif ftype == 'e':
  257. tuples.append((name,
  258. fmt+"%u", "%s",
  259. None, None))
  260. if vocabularies:
  261. this = vocabularies.pop(0)
  262. ref = "DISPLAY_%s(%%s)" % (this.upper())
  263. else:
  264. ref = 'FOO[%s]'
  265. tuples.append((name,
  266. fmt_text+r"\"%s\"", ref,
  267. None, None))
  268. elif ftype == 'i':
  269. tuples.append((name,
  270. fmt+"%d", "%s",
  271. None, None))
  272. elif ftype == 't':
  273. tuples.append((name,
  274. fmt+r'\"%s\"', "%s",
  275. None, None))
  276. elif ftype == 'b':
  277. tuples.append((name,
  278. fmt+r'\"%s\"', "JSON_BOOL(%s)",
  279. None, None))
  280. elif ftype[0] == 'd':
  281. print("Cannot generate code for data members", file=sys.stderr)
  282. sys.exit(1)
  283. elif ftype[0] == 'U':
  284. tuples.append((name,
  285. fmt+"%u", "%s",
  286. fmt+"%%.%sf" % ftype[1], '%s / SCALE'))
  287. elif ftype[0] == 'I':
  288. tuples.append((name,
  289. fmt+"%d", "%s",
  290. fmt+"%%.%sf" % ftype[1], '%s / SCALE'))
  291. elif ftype[0] == 'a':
  292. ftype = ftype[1:]
  293. if ftype[0] == '^':
  294. lengthfield = last
  295. else:
  296. lengthfield = "n" + name
  297. tuples.append((name, None, None, None, lengthfield))
  298. else:
  299. print("Unknown type code", ftype, file=sys.stderr)
  300. sys.exit(1)
  301. last = name
  302. startspan = 0
  303. def scaled(idx):
  304. """Check if scaled."""
  305. return tuples[idx][3] is not None
  306. def tslice(e, idx):
  307. """Missing docstring."""
  308. return [x[idx] for x in tuples[startspan:e+1]]
  309. lbase = " " * 8
  310. step = " " * 4
  311. inarray = None
  312. header = "(void)snprintf(buf + strlen(buf), buflen - strlen(buf),"
  313. for (i, (var, uf, uv, sf, sv)) in enumerate(tuples):
  314. if uf is not None:
  315. print(lbase + "for (i = 0; i < %s.%s; i++) {" % (structname, sv),
  316. file=wfp)
  317. inarray = var
  318. lbase = " " * 12
  319. startspan = i+1
  320. continue
  321. # At end of tuples, or if scaled flag changes, or if next op is array,
  322. # flush out dump code for a span of fields.
  323. if i+1 == len(tuples):
  324. endit = '}",'
  325. elif tuples[i+1][1] is not None:
  326. endit = r',\"%s\":[",' % tuples[i+1][0]
  327. elif scaled(i) != scaled(i + 1):
  328. endit = ',",'
  329. else:
  330. endit = None
  331. if endit:
  332. if not scaled(i):
  333. print(lbase + header, file=wfp)
  334. if inarray:
  335. prefix = '{"'
  336. else:
  337. prefix = '"'
  338. print(lbase + step + prefix + ','.join(tslice(i, 1)) + endit,
  339. file=wfp)
  340. for (j, t) in enumerate(tuples[startspan:i+1]):
  341. if inarray:
  342. ref = structname + "." + inarray + "[i]." + t[0]
  343. else:
  344. ref = structname + "." + t[0]
  345. wfp.write(lbase + step + t[2] % ref)
  346. if j == i - startspan:
  347. wfp.write(");\n")
  348. else:
  349. wfp.write(",\n")
  350. else:
  351. print(lbase + "if (scaled)", file=wfp)
  352. print(lbase + step + header, file=wfp)
  353. print(lbase + step * 2 + '"' + ','.join(tslice(i, 3)) + endit,
  354. file=wfp)
  355. for (j, t) in enumerate(tuples[startspan:i+1]):
  356. if inarray:
  357. ref = structname + "." + inarray + "[i]." + t[0]
  358. else:
  359. ref = structname + "." + t[0]
  360. wfp.write(lbase + step*2 + t[4] % ref)
  361. if j == i - startspan:
  362. wfp.write(");\n")
  363. else:
  364. wfp.write(",\n")
  365. print(lbase + "else", file=wfp)
  366. print(lbase + step + header, file=wfp)
  367. print(lbase + step * 2 + '"' + ','.join(tslice(i, 1)) + endit,
  368. file=wfp)
  369. for (j, t) in enumerate(tuples[startspan:i+1]):
  370. if inarray:
  371. ref = structname + "." + inarray + "[i]." + t[0]
  372. else:
  373. ref = structname + "." + t[0]
  374. wfp.write(lbase + step*2 + t[2] % ref)
  375. if j == i - startspan:
  376. wfp.write(");\n")
  377. else:
  378. wfp.write(",\n")
  379. startspan = i+1
  380. # If we were looking at a trailing array, close scope
  381. if inarray:
  382. lbase = " " * 8
  383. print(lbase + "}", file=wfp)
  384. print(lbase + "if (buf[strlen(buf)-1] == ',')", file=wfp)
  385. print(lbase + step + r"buf[strlen(buf)-1] = '\0';", file=wfp)
  386. print(lbase + "(void)strlcat(buf, \"]}\", buflen - strlen(buf));",
  387. file=wfp)
  388. def make_json_generator(wfp):
  389. """Write a stanza for jsongen.py.in describing how to generate a
  390. JSON parser initializer from this table. You need to fill in
  391. __INITIALIZER__ and default values after this is generated."""
  392. extra = ""
  393. last = 0
  394. arrayname = None
  395. record = after is None
  396. print('''\
  397. {
  398. "initname" : "__INITIALIZER__",
  399. "headers": ("AIS_HEADER",),
  400. "structname": "%s",
  401. "fieldmap":(
  402. # fieldname type default''' % (structname,), file=wfp)
  403. for (i, t) in enumerate(table):
  404. if '|' in t:
  405. fields = [s.strip() for s in t.split('|')]
  406. name = fields[4]
  407. ftype = fields[5]
  408. if after == name:
  409. record = True
  410. continue
  411. if before == name:
  412. record = False
  413. continue
  414. if ftype == 'x' or not record:
  415. continue
  416. if ftype[0] == 'a':
  417. arrayname = name
  418. if arrayname.endswith("s"):
  419. typename = arrayname[:-1]
  420. else:
  421. typename = arrayname
  422. readtype = 'array'
  423. dimension = ftype[1:]
  424. if dimension[0] == '^':
  425. lengthfield = last
  426. dimension = dimension[1:]
  427. else:
  428. lengthfield = "n" + arrayname
  429. extra = " " * 8
  430. print(" ('%s',%s 'array', (" %
  431. (arrayname, " "*(10-len(arrayname))), file=wfp)
  432. print(" ('%s_t', '%s', (" % (typename, lengthfield),
  433. file=wfp)
  434. else:
  435. # Depends on the assumption that the read code
  436. # always sees unscaled JSON.
  437. readtype = {
  438. 'u': "uinteger",
  439. 'U': "uinteger",
  440. 'e': "uinteger",
  441. 'i': "integer",
  442. 'I': "integer",
  443. 'b': "boolean",
  444. 't': "string",
  445. 'd': "string",
  446. }[ftype[0]]
  447. typedefault = {
  448. 'u': "'PUT_DEFAULT_HERE'",
  449. 'U': "'PUT_DEFAULT_HERE'",
  450. 'e': "'PUT DEFAULT HERE'",
  451. 'i': "'PUT_DEFAULT_HERE'",
  452. 'I': "'PUT_DEFAULT_HERE'",
  453. 'b': "\'false\'",
  454. 't': "None",
  455. }[ftype[0]]
  456. namedefaults = {
  457. "month": "'0'",
  458. "day": "'0'",
  459. "hour": "'24'",
  460. "minute": "'60'",
  461. "second": "'60'",
  462. }
  463. default = namedefaults.get(name) or typedefault
  464. print(extra + " ('%s',%s '%s',%s %s)," %
  465. (name, " "*(10-len(name)), readtype,
  466. " "*(8-len(readtype)), default), file=wfp)
  467. if ftype[0] == 'e':
  468. print(extra + " ('%s_text',%s'ignore', None)," %
  469. (name, " "*(6-len(name))), file=wfp)
  470. last = name
  471. if arrayname:
  472. print(" )))),", file=wfp)
  473. print(" ),", file=wfp)
  474. print(" },", file=wfp)
  475. if __name__ == '__main__':
  476. try:
  477. # FIXME: Convert to argparse
  478. (options, arguments) = getopt.getopt(sys.argv[1:], "a:tc:s:d:S:E:r:o:")
  479. except getopt.GetoptError as msg:
  480. print("tablecheck.py: " + str(msg))
  481. raise SystemExit(1)
  482. generate = maketable = makestruct = makedump = readgen = doall = False
  483. after = before = None
  484. filestem = "tablegen"
  485. for (switch, val) in options:
  486. if switch == '-a':
  487. doall = True
  488. structname = val
  489. elif switch == '-c':
  490. generate = True
  491. structname = val
  492. elif switch == '-s':
  493. makestruct = True
  494. structname = val
  495. elif switch == '-t':
  496. maketable = True
  497. elif switch == '-d':
  498. makedump = True
  499. structname = val
  500. elif switch == '-r':
  501. readgen = True
  502. structname = val
  503. elif switch == '-S':
  504. after = val
  505. elif switch == '-E':
  506. before = val
  507. elif switch == '-o':
  508. filestem = val
  509. if ((not generate and not maketable and not makestruct and
  510. not makedump and not readgen and not doall)):
  511. print("tablecheck.py: no mode selected", file=sys.stderr)
  512. sys.exit(1)
  513. # First, read in the table.
  514. # Sets the following:
  515. # table - the table lines
  516. # widths - array of table widths
  517. # ranges - array of table offsets
  518. # trailing - bit length of the table or trailing array element
  519. # subtables - list of following vocabulary tables.
  520. tablename = arguments[0]
  521. table = []
  522. ranges = []
  523. subtables = []
  524. state = 0
  525. for line in sys.stdin:
  526. if state == 0 and line.startswith("//: Type") and tablename in line:
  527. state = 1
  528. continue
  529. elif state == 1: # Found table tag
  530. if line.startswith("|="):
  531. state = 2
  532. continue
  533. elif state == 2: # Found table header
  534. if line.startswith("|="):
  535. state = 3
  536. continue
  537. elif line[0] == '|':
  538. fields = line.split("|")
  539. trailing = fields[1]
  540. ranges.append(fields[1].strip())
  541. fields[1] = " " * len(fields[1])
  542. line = "|".join(fields)
  543. else:
  544. ranges.append('')
  545. table.append(line)
  546. continue
  547. elif state == 3: # Found table end
  548. state = 4
  549. continue
  550. elif state == 4: # Skipping until subsidiary table
  551. if line.startswith("//:") and "vocabulary" in line:
  552. subtable_name = line.split()[1]
  553. subtable_content = []
  554. state = 5
  555. elif state == 5: # Seen subtable header
  556. if line.startswith("|="):
  557. state = 6
  558. continue
  559. elif state == 6: # Parsing subtable content
  560. if line.startswith("|="):
  561. subtables.append((subtable_name, subtable_content))
  562. state = 4
  563. continue
  564. elif line[0] == '|':
  565. subtable_content.append(
  566. [f.strip() for f in line[1:].strip().split("|")])
  567. continue
  568. if state == 0:
  569. print("Can't find named table.", file=sys.stderr)
  570. sys.exit(1)
  571. elif state < 3:
  572. print("Ill-formed table (in state %d)." % state, file=sys.stderr)
  573. sys.exit(1)
  574. table = table[1:]
  575. ranges = ranges[1:]
  576. widths = []
  577. for line in table:
  578. fields = line.split('|')
  579. if '|' not in line: # Continuation line
  580. widths.append('')
  581. elif fields[5][0] == 'a': # Array boundary indicator
  582. widths.append(None)
  583. else:
  584. widths.append(fields[2].strip())
  585. if '-' in trailing:
  586. trailing = trailing.split('-')[1]
  587. trailing = str(int(trailing)+1)
  588. # Compute offsets for an AIVDM message breakdown, given the bit widths.
  589. offsets = []
  590. base = 0
  591. corrections = False
  592. for w in widths:
  593. if w is None:
  594. offsets.append(repr(base))
  595. base = 0
  596. elif w == '':
  597. offsets.append('')
  598. else:
  599. w = int(w)
  600. offsets.append("%d-%d" % (base, base + w - 1))
  601. base += w
  602. if [p for p in zip(ranges, offsets) if p[0] != p[1]]:
  603. corrections = True
  604. print("Offset corrections:")
  605. for (old, new) in zip(ranges, offsets):
  606. if old != new:
  607. print(old, "->", new, file=sys.stderr)
  608. owidth = max(*list(map(len, offsets)))
  609. for (i, off) in enumerate(offsets):
  610. offsets[i] += " " * (owidth - len(offsets[i]))
  611. # Here's where we generate useful output.
  612. if doall:
  613. if corrections:
  614. correct_table(open(filestem + ".txt", "w"))
  615. make_driver_code(open(filestem + ".c", "w"))
  616. make_structure(open(filestem + ".h", "w"))
  617. make_json_dumper(open(filestem + "_json.c", "w"))
  618. make_json_generator(open(filestem + ".py", "w"))
  619. elif maketable:
  620. correct_table(sys.stdout)
  621. elif generate:
  622. make_driver_code(sys.stdout)
  623. elif makestruct:
  624. make_structure(sys.stdout)
  625. elif makedump:
  626. make_json_dumper(sys.stdout)
  627. elif readgen:
  628. make_json_generator(sys.stdout)
  629. # end