From 75007b20b4071ad4c6f7168b98c3218739693769 Mon Sep 17 00:00:00 2001 From: John MacFarlane Date: Thu, 15 Jan 2015 16:08:03 -0800 Subject: Re-added tools/{makespec.py, template.html, template.tex}. These were inadvertently dropped in the commit that described them as being moved to tools/. --- tools/makespec.py | 165 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100755 tools/makespec.py (limited to 'tools/makespec.py') 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 '{0}'.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
\n".format(example, section)) + mdlines.append("\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("
\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'# +', '# '.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 = '
\n\n' + '\n'.join(toclines) + '\n\n
\n\n' + prog = "build/src/cmark" + [retcode, result, err] = pipe_through_prog(prog, toc + mdtext) + if retcode == 0: + result = re.sub(r'␣', ' ', result) + result = re.sub(r'<\/a> ', + "", result) + # put plural s inside links for better visuals: + result = re.sub(r'<\/a>s', "s", 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) -- cgit v1.2.3