diff options
author | John MacFarlane <[email protected]> | 2015-01-15 16:08:03 -0800 |
---|---|---|
committer | John MacFarlane <[email protected]> | 2015-01-15 16:08:03 -0800 |
commit | 75007b20b4071ad4c6f7168b98c3218739693769 (patch) | |
tree | 56f11b338bd13085547be8245d484554b2ee859c /tools/makespec.py | |
parent | 604c15c301a634a7c237e2336296d2d86d771fcf (diff) |
Re-added tools/{makespec.py, template.html, template.tex}.
These were inadvertently dropped in the commit that described
them as being moved to tools/.
Diffstat (limited to 'tools/makespec.py')
-rwxr-xr-x | tools/makespec.py | 165 |
1 files changed, 165 insertions, 0 deletions
diff --git a/tools/makespec.py b/tools/makespec.py new file mode 100755 index 0000000..84663f2 --- /dev/null +++ b/tools/makespec.py @@ -0,0 +1,165 @@ +#!/usr/bin/env python3 +import re +import sys +from subprocess import * +from string import Template + +if len(sys.argv) == 2: + specformat = sys.argv[1] + if not (specformat in ["html", "markdown"]): + sys.stderr.write("Format must be html or markdown\n") + exit(1) +else: + sys.stderr.write("Usage: makespec.py [html|markdown]\n") + exit(1) + +def toIdentifier(s): + return re.sub(r'\s+', '-', re.sub(r'\W+', ' ', s.strip().lower())) + +def parseYaml(yaml): + metadata = {} + def parseField(match): + key = match.group(1) + val = match.group(2).strip() + if re.match(r'^\'', val): + val = val[1:len(val) - 1] + metadata[key] = val + fieldre = re.compile('^(\w+):(.*)$', re.MULTILINE) + re.sub(fieldre, parseField, yaml) + return metadata + +def pipe_through_prog(prog, text): + p1 = Popen(prog.split(), stdout=PIPE, stdin=PIPE, stderr=PIPE) + [result, err] = p1.communicate(input=text.encode('utf-8')) + return [p1.returncode, result.decode('utf-8'), err] + +def replaceAnchor(match): + refs.append("[{0}]: #{1}".format(match.group(1), match.group(2))) + if specformat == "html": + return '<a id="{1}" href="#{1}" class="definition">{0}</a>'.format(match.group(1), match.group(2)) + else: + return match.group(0) + +stage = 0 +example = 0 +section = "" +sections = [] +mdlines = [] +refs = [] +lastnum = [] +finishedMeta = False +yamllines = [] + +with open('spec.txt', 'r', encoding='utf-8') as spec: + for ln in spec: + if not finishedMeta: + yamllines.append(ln) + if re.match(r'^\.\.\.$', ln): + finishedMeta = True + elif re.match(r'^\.$', ln): + if stage == 0: + example += 1 + mdlines.append("\n<div class=\"example\" id=\"example-{0}\" data-section=\"{1}\">\n".format(example, section)) + mdlines.append("<div class=\"examplenum\"><a href=\"#example-{0}\">Example {0}</a> <a class=\"dingus\" title=\"open in interactive dingus\">(interact)</a></div>\n\n".format(example)) + mdlines.append("````````````````````````````````````````````````````````` markdown\n") + stage = 1 + elif stage == 1: + mdlines.append("`````````````````````````````````````````````````````````\n\n") + mdlines.append("````````````````````````````````````````````````````````` html\n") + stage = 2 + elif stage == 2: + mdlines.append("`````````````````````````````````````````````````````````\n\n") + mdlines.append("</div>\n") + stage = 0 + else: + sys.stderr.out("Encountered unknown stage {0}\n".format(stage)) + sys.exit(1) + else: + if stage == 0: + match = re.match(r'^(#{1,6}) *(.*)', ln) + if match: + section = match.group(2) + lastlevel = len(lastnum) + level = len(match.group(1)) + if re.search(r'{-}$', section): + section = re.sub(r' *{-} *$', '', section) + if specformat == 'html': + ln = re.sub(r' *{-} *$', '', ln) + number = '' + else: + if lastlevel == level: + lastnum[level - 1] = lastnum[level - 1] + 1 + elif lastlevel < level: + while len(lastnum) < level: + lastnum.append(1) + else: # lastlevel > level + lastnum = lastnum[0:level] + lastnum[level - 1] = lastnum[level - 1] + 1 + number = '.'.join([str(x) for x in lastnum]) + ident = toIdentifier(section) + ln = re.sub(r' ', ' ' + number + ' ', ln, count=1) + sections.append(dict(level=level, + contents=section, + ident=ident, + number=number)) + refs.append("[{0}]: #{1}".format(section, ident)) + ln = re.sub(r'# +', '# <a id="{0}"></a> '.format(ident), + ln, count=1) + else: + ln = re.sub(r'\[([^]]*)\]\(@([^)]*)\)', replaceAnchor, ln) + else: + ln = re.sub(r' ', '␣', ln) + mdlines.append(ln) + +mdtext = ''.join(mdlines) + '\n\n' + '\n'.join(refs) + '\n' +yaml = ''.join(yamllines) +metadata = parseYaml(yaml) + +if specformat == "markdown": + sys.stdout.write(yaml + '\n\n' + mdtext) +elif specformat == "html": + with open("template.html", "r", encoding="utf-8") as templatefile: + template = Template(templatefile.read()) + toclines = [] + for section in sections: + indent = ' ' * (section['level'] - 1) + toclines.append(indent + '* [' + section['number'] + ' ' + + section['contents'] + '](#' + section['ident'] + ')') + toc = '<div id="TOC">\n\n' + '\n'.join(toclines) + '\n\n</div>\n\n' + prog = "build/src/cmark" + [retcode, result, err] = pipe_through_prog(prog, toc + mdtext) + if retcode == 0: + result = re.sub(r'␣', '<span class="space"> </span>', result) + result = re.sub(r'<h([1-6])><a id="([^\"]*)"><\/a> ', + "<h\\1 id=\"\\2\">", result) + # put plural s inside links for better visuals: + result = re.sub(r'<\/a>s', "s</a>", result) + sys.stdout.write(template.substitute(metadata, body=result)) + + # check for errors: + idents = [] + for ident in re.findall(r'id="([^"]*)"', result): + if ident in idents: + sys.stderr.write("WARNING: duplicate identifier '" + ident + + "'\n") + else: + idents.append(ident) + for href in re.findall(r'href="#([^"]*)"', result): + if not (href in idents): + sys.stderr.write("WARNING: internal link with no anchor '" + + href + "'\n") + reftexts = [] + for ref in refs: + ref = re.sub('].*',']',ref).upper() + if ref in reftexts: + sys.stderr.write("WARNING: duplicate reference link '" + + ref + "'\n") + else: + reftexts.append(ref) + + else: + sys.stderr.write("Error converting markdown version of spec:\n") + sys.stderr.write(err) + exit(1) + +exit(0) |