import os, argparse, sys, re
parser = argparse.ArgumentParser(description='Converts ZippyDoc source files to HTML.')
parser.add_argument('files', metavar='FILE', type=str, nargs='+',
help='files to convert to HTML')
args = parser.parse_args()
options = vars(args)
files = options["files"]
template = """
%s
"""
class TreeLevel:
def __init__(self, indentation, data):
self.elements = []
self.indentation = indentation
self.data = data
def add(self, element):
self.elements.append(element)
def output(self):
return self.render()
def render_children(self):
child_output = ""
for child in self.elements:
child_output += child.output()
return '%s
' % child_output
def process_inline_markup(self, text):
text = re.sub("`([^`]+)`", '\\1', text) # Emphasized
text = re.sub("\*\*([^*]+)\*\*", "\\1", text) # Emphasized
text = re.sub("__([^_]+)__", "\\1", text) # Strong
text = re.sub("{>([^}]+)}\(([^)]+)\)", '\\2', text) # Hyperlink with text
text = re.sub("{>([^}]+)}", '\\1', text) # Hyperlink
text = re.sub("{([^}]+:[^}]+)}\(([^)]+)\)", '\\2', text) # External hyperlink with text
text = re.sub("{([^}]+:[^}]+)}", '\\1', text) # External hyperlink
text = re.sub("{<([^}]+)}\(([^)]+)\)", '\\2', text) # Forced external hyperlink with text
text = re.sub("{<([^}]+)}", '\\1', text) # Forced external hyperlink
return text
def fix_preformatted(self, text):
return text.replace("<", "<").replace(">", ">")
def clear_markup(self, text):
return re.sub("\*\*([^*]+)\*\*", "\\1", text)
def render(self):
return self.render_children()
class Example(TreeLevel):
def render(self):
return 'Example: %s %s
' % (self.data, self.render_children())
class Code(TreeLevel):
def render(self):
return 'Code:%s
' % self.fix_preformatted(self.data)
class Output(TreeLevel):
def render(self):
return 'Output:%s
' % self.fix_preformatted(self.data)
class Definition(TreeLevel):
def get_anchor(self):
first = self.clear_markup(self.data.splitlines()[0])
anchor = first.replace("...", "")
anchor = anchor.replace(".", "_")
anchor = re.sub("[^a-zA-Z0-9_]", "", anchor)
return anchor
def get_description(self):
for element in self.elements:
if element.__class__.__name__ == "Text":
data = self.process_inline_markup(element.data)
if len(data) > 80:
matches = re.match("^(.{0,80})\W", data)
return matches.group(1) + "..."
else:
return data
return ""
def render(self):
return '' % (self.get_anchor(), self.process_inline_markup(self.data.replace("\n", "
")), self.render_children())
class Exclamation(TreeLevel):
def render(self):
return 'Important: %s
' % self.process_inline_markup(self.data)
class Argument(TreeLevel):
def __init__(self, indentation, data, argname):
self.elements = []
self.indentation = indentation
self.argname = argname
self.data = data
def render(self):
return '- %s
- %s
' % (self.argname, self.process_inline_markup(self.data))
class Header(TreeLevel):
def __init__(self, indentation, data, depth):
self.elements = []
self.indentation = indentation
self.depth = depth
self.data = data
def render(self):
if self.depth <= 7:
title_type = "h%d" % self.depth
else:
title_type = "h7"
return "<%s>%s%s>" % (title_type, self.data, title_type)
class Text(TreeLevel):
def render(self):
return '%s
' % self.process_inline_markup(self.data)
class Index(TreeLevel):
def render(self):
global toc_items
rendered = ""
for item in toc_items:
forms = item.data.splitlines()
first = self.clear_markup(forms[0])
if len(forms) > 1:
rest = '(also: ' + ', '.join(self.clear_markup(form) for form in forms[1:]) + ")"
else:
rest = ""
anchor = item.get_anchor()
description = item.get_description()
rendered += '%s %s %s' % (anchor, first, description, rest)
return '' % rendered
for zpy in files:
destination = os.path.splitext(zpy)[0] + ".html"
f = open(zpy, "r")
data = f.read()
f.close()
paragraphs = re.split("\s*\n\s*\n", data)
toc_items = []
current_level = 0
current_paragraph = 0
current_elements = {0: TreeLevel(0, "root")}
for paragraph in paragraphs:
if paragraph.strip() == "":
continue
current_paragraph += 1
indentation = len(paragraph) - len(paragraph.lstrip("\t")) + 1
if indentation > current_level + 1:
raise Exception("Invalid indentation found in paragraph %d" % current_paragraph)
element_type = TreeLevel
start = indentation - 1
lines = [line[start:] for line in paragraph.splitlines()]
if lines[0].startswith("#"):
element_type = Header
depth = len(lines[0]) - len(lines[0].lstrip("#"))
lines[0] = lines[0].lstrip("# ")
data = " ".join(lines)
elif lines[0].startswith("^"):
element_type = Definition
lines[0] = lines[0].lstrip("^ ")
data = "\n".join(lines)
elif lines[0].startswith("@"):
element_type = Example
lines[0] = lines[0].lstrip("@ ")
data = " ".join(lines)
elif lines[0].startswith("$$") and current_elements[current_level].__class__.__name__ == "Code":
current_elements[current_level].data += "\n\n" + "\n".join(lines).lstrip("$ ")
continue
elif lines[0].startswith("$"):
element_type = Code
lines[0] = lines[0].lstrip("$ ")
data = "\n".join(lines)
elif lines[0].startswith(">>") and current_elements[current_level].__class__.__name__ == "Output":
current_elements[current_level].data += "\n\n" + "\n".join(lines).lstrip("> ")
continue
elif lines[0].startswith(">"):
element_type = Output
lines[0] = lines[0].lstrip("> ")
data = "\n".join(lines)
elif lines[0].startswith("!"):
element_type = Exclamation
lines[0] = lines[0].lstrip("! ")
data = " ".join(lines)
elif re.match(".*::\s*$", lines[0]):
element_type = Argument
argname = lines[0][:-2]
data = " ".join(line.lstrip() for line in lines[1:])
elif lines[0].strip() == "{TOC}":
element_type = Index
data = ""
else:
element_type = Text
data = " ".join(lines)
#print "Found element of type %s at indentation %d with data %s" % (element_type.__name__, indentation, data[:80])
if element_type.__name__ == "Header":
element = Header(indentation, data, depth)
elif element_type.__name__ == "Argument":
element = Argument(indentation, data, argname)
else:
element = element_type(indentation, data)
if element_type.__name__ == "Definition":
toc_items.append(element)
current_elements[indentation - 1].add(element)
current_level = indentation
current_elements[current_level] = element
rendered = template % (current_elements[0].output())
f = open(destination, "w")
f.write(rendered)
f.close()
print "Rendered %s" % destination