aboutsummaryrefslogtreecommitdiff
path: root/makespec.py
blob: 23616366cf8e522e593cccffbdfe17d85aabde87 (plain)
  1. #!/usr/bin/env python3
  2. import re
  3. import sys
  4. from subprocess import *
  5. from string import Template
  6. if len(sys.argv) == 2:
  7. specformat = sys.argv[1]
  8. if not (specformat in ["html", "markdown"]):
  9. sys.stderr.write("Format must be html or markdown\n")
  10. exit(1)
  11. else:
  12. sys.stderr.write("Usage: makespec.py [html|markdown]\n")
  13. exit(1)
  14. def toIdentifier(s):
  15. return re.sub(r'\s+', '-', re.sub(r'\W+', ' ', s.strip().lower()))
  16. def parseYaml(yaml):
  17. metadata = {}
  18. def parseField(match):
  19. key = match.group(1)
  20. val = match.group(2).strip()
  21. if re.match(r'^\'', val):
  22. val = val[1:len(val) - 1]
  23. metadata[key] = val
  24. fieldre = re.compile('^(\w+):(.*)$', re.MULTILINE)
  25. re.sub(fieldre, parseField, yaml)
  26. return metadata
  27. def pipe_through_prog(prog, text):
  28. p1 = Popen(prog.split(), stdout=PIPE, stdin=PIPE, stderr=PIPE)
  29. [result, err] = p1.communicate(input=text.encode('utf-8'))
  30. return [p1.returncode, result.decode('utf-8'), err]
  31. def replaceAnchor(match):
  32. refs.append("[{0}]: #{1}".format(match.group(1), match.group(2)))
  33. if specformat == "html":
  34. return '<a id="{1}" href="#{1}" class="definition">{0}</a>'.format(match.group(1), match.group(2))
  35. else:
  36. return match.group(0)
  37. stage = 0
  38. example = 0
  39. section = ""
  40. sections = []
  41. mdlines = []
  42. refs = []
  43. lastnum = []
  44. finishedMeta = False
  45. yamllines = []
  46. with open('spec.txt', 'r', encoding='utf-8') as spec:
  47. for ln in spec:
  48. if not finishedMeta:
  49. yamllines.append(ln)
  50. if re.match(r'^\.\.\.$', ln):
  51. finishedMeta = True
  52. elif re.match(r'^\.$', ln):
  53. if stage == 0:
  54. example += 1
  55. mdlines.append("\n<div class=\"example\" id=\"example-{0}\" data-section=\"{1}\">\n".format(example, section))
  56. mdlines.append("<div class=\"examplenum\"><a href=\"#example-{0}\">Example {0}</a>&nbsp;&nbsp;<a class=\"dingus\" title=\"open in interactive dingus\">(interact)</a></div>\n\n".format(example))
  57. mdlines.append("````````````````````````````````````````````````````````` markdown\n")
  58. stage = 1
  59. elif stage == 1:
  60. mdlines.append("`````````````````````````````````````````````````````````\n\n")
  61. mdlines.append("````````````````````````````````````````````````````````` html\n")
  62. stage = 2
  63. elif stage == 2:
  64. mdlines.append("`````````````````````````````````````````````````````````\n\n")
  65. mdlines.append("</div>\n")
  66. stage = 0
  67. else:
  68. sys.stderr.out("Encountered unknown stage {0}\n".format(stage))
  69. sys.exit(1)
  70. else:
  71. if stage == 0:
  72. match = re.match(r'^(#{1,6}) *(.*)', ln)
  73. if match:
  74. section = match.group(2)
  75. lastlevel = len(lastnum)
  76. level = len(match.group(1))
  77. if re.search(r'{-}$', section):
  78. section = re.sub(r' *{-} *$', '', section)
  79. number = ''
  80. else:
  81. if lastlevel == level:
  82. lastnum[level - 1] = lastnum[level - 1] + 1
  83. elif lastlevel < level:
  84. while len(lastnum) < level:
  85. lastnum.append(1)
  86. else: # lastlevel > level
  87. lastnum = lastnum[0:level]
  88. lastnum[level - 1] = lastnum[level - 1] + 1
  89. number = '.'.join([str(x) for x in lastnum])
  90. ident = toIdentifier(section)
  91. ln = re.sub(r' ', ' ' + number + ' ', ln, count=1)
  92. sections.append(dict(level=level,
  93. contents=section,
  94. ident=ident,
  95. number=number))
  96. refs.append("[{0}]: #{1}".format(section, ident))
  97. ln = re.sub(r'# +', '# <a id="{0}"></a> '.format(ident),
  98. ln, count=1)
  99. else:
  100. ln = re.sub(r'\[([^]]*)\]\(@([^)]*)\)', replaceAnchor, ln)
  101. else:
  102. ln = re.sub(r' ', '␣', ln)
  103. mdlines.append(ln)
  104. mdtext = ''.join(mdlines) + '\n\n' + '\n'.join(refs) + '\n'
  105. yaml = ''.join(yamllines)
  106. metadata = parseYaml(yaml)
  107. if specformat == "markdown":
  108. sys.stdout.write(yaml + '\n\n' + mdtext)
  109. elif specformat == "html":
  110. with open("template.html", "r", encoding="utf-8") as templatefile:
  111. template = Template(templatefile.read())
  112. toclines = []
  113. for section in sections:
  114. indent = ' ' * (section['level'] - 1)
  115. toclines.append(indent + '* [' + section['number'] + ' ' +
  116. section['contents'] + '](#' + section['ident'] + ')')
  117. toc = '<div id="TOC">\n\n' + '\n'.join(toclines) + '\n\n</div>\n\n'
  118. prog = "build/src/cmark"
  119. [retcode, result, err] = pipe_through_prog(prog, toc + mdtext)
  120. if retcode == 0:
  121. result = re.sub(r'␣', '<span class="space"> </span>', result)
  122. sys.stdout.write(template.substitute(metadata, body=result))
  123. else:
  124. sys.stderr.write("Error converting markdown version of spec:\n")
  125. sys.stderr.write(err)
  126. exit(1)
  127. exit(0)