#! /usr/bin/env python # Reads mixed source/text markdown files and outputs HTML and/or C++ source. # Usage: # ./transform.py --o-html OUT.html --o-cxx OUT.cc in.md import argparse import mistune import re import sys parser = argparse.ArgumentParser(description = "Translate markdown tutorials.", epilog = "At least one output file must be specified") parser.add_argument("--o-html", metavar = "HTML", dest = "html_file", help = "HTML output file name") parser.add_argument("--o-cxx", metavar = "CXX", dest = "cxx_file", help = "C++ output file name") parser.add_argument("md_file", metavar = "MD") def pretty_print(text, prefix, start_with_prefix = True): words = text.split() index = 0 if start_with_prefix: result = prefix else: result = "" while index < len(words): column = len(prefix) result += words[index] column += len(words[index]) index += 1 while index < len(words) and column + 1 + len(words[index]) <= 80: result += " " result += words[index] column += 1 + len(words[index]) index += 1 result += "\n" if index < len(words): result += prefix return result class HtmlRenderer(mistune.Renderer): def __init__(self): super(HtmlRenderer, self).__init__() self._definitions = {} def header(self, text, level, raw = None): if level == 2: if "title" not in self._definitions: self._definitions["title"] = text return super(HtmlRenderer, self).header(text, level, raw) def paragraph(self, text): if text.startswith("%%"): tokens = text[2:].split("=", 1) if len(tokens) == 2: pass key = tokens[0].strip() value = tokens[1].strip() self._definitions[key] = value return "" return super(HtmlRenderer, self).paragraph(text) def block_code(self, code, lang): escaped = mistune.escape(re.sub("\n*$", "", code)) replaced = re.sub("<em>", "", escaped) replaced = re.sub("</em>", "", replaced) replaced = re.sub("#(ifn?def|endif).*\n?", "", replaced) if lang == "comment": start_tag = "
"
else:
start_tag = ""
return start_tag + replaced + ""
def definitions(self):
return self._definitions
def to_html(text):
renderer = HtmlRenderer()
html = mistune.Markdown(renderer = renderer).render(text)
definitions = renderer.definitions()
definitions["body"] = html
return definitions
class CxxRenderer(mistune.Renderer):
def __init__(self):
super(CxxRenderer, self).__init__()
self._not_in_list()
self._not_paragraph()
def header(self, text, level, raw = None):
self._not_in_list()
return self._join_paragraph() + pretty_print(text, "// ")
def paragraph(self, text):
if text.startswith("%%"):
return ""
self._not_in_list()
return self._join_paragraph() + pretty_print(text, "// ")
def codespan(self, text):
return text
def list(self, body, ordered = True):
return self._join_paragraph() + body
def list_item(self, text):
return ("// %i. " % self._number_list_item()) + \
pretty_print(text, "// ", False)
def block_code(self, code, lang):
self._not_in_list()
code = re.sub("?em>", "", code)
if lang == "comment":
code = re.sub("^(.)", "// \g<1>", code, flags = re.MULTILINE)
code = re.sub("^$", "//", code, flags = re.MULTILINE)
return self._join_paragraph() + code + "\n"
else:
self._not_paragraph()
return "\n" + code
def hrule(self):
self._not_in_list()
self._not_paragraph()
return ""
def footnote_ref(self, key, index):
return ""
def footnotes(self, text):
return ""
def _not_in_list(self):
self._list_index = None
def _number_list_item(self):
if self._list_index == None:
self._list_index = 2
return 1
else:
result = self._list_index
self._list_index += 1
return result
def _not_paragraph(self):
self._join = False
def _paragraph(self):
self._join = True
def _join_paragraph(self):
if self._join:
result = "//\n"
else:
result = ""
self._join = True
return result
def main(md_file, html_file, cxx_file):
markdown = open(md_file, "r").read()
if html_file != None:
html = mistune.markdown(markdown)
open(html_file, "w").write(html)
if cxx_file != None:
renderer = CxxRenderer()
source = mistune.Markdown(renderer = renderer).render(markdown)
source = re.sub(r"\n*$", "\n", source)
source = "// This file was generated automatically\n\n" + source
open(cxx_file, "w").write(source)
if __name__ == "__main__":
arguments = parser.parse_args()
if arguments.html_file == None and arguments.cxx_file == None:
parser.print_help()
sys.exit(1)
main(arguments.md_file, arguments.html_file, arguments.cxx_file)