diff --git a/template.html b/template.html
new file mode 100644
index 0000000..d8ea410
--- /dev/null
+++ b/template.html
@@ -0,0 +1,161 @@
+
+
+
+
+
+
+ {CONTENT}
+
+
diff --git a/zippydoc/__init__.py b/zippydoc/__init__.py
new file mode 100644
index 0000000..e2e7e54
--- /dev/null
+++ b/zippydoc/__init__.py
@@ -0,0 +1,2 @@
+from block_markup import *
+from parser import *
diff --git a/zippydoc/block_markup.py b/zippydoc/block_markup.py
new file mode 100644
index 0000000..b7f07c2
--- /dev/null
+++ b/zippydoc/block_markup.py
@@ -0,0 +1,141 @@
+import re
+
+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) # Fixed-width
+ 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 clear_markup(self, text):
+ text = re.sub("`([^`]+)`", '\\1', text) # Fixed-width
+ 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 render(self):
+ return self.render_children()
+
+class Header(TreeLevel):
+ def __init__(self, indentation, data, depth):
+ self.elements = []
+ self.indentation = indentation
+ self.data = data
+ self.depth = depth
+
+ 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 Exclamation(TreeLevel):
+ def render(self):
+ return 'Important: %s
' % self.process_inline_markup(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 Argument(TreeLevel):
+ def __init__(self, indentation, data, argname):
+ self.elements = []
+ self.indentation = indentation
+ self.data = data
+ self.argname = argname
+
+ def render(self):
+ return '- %s
- %s%s
' % (self.argname, self.process_inline_markup(self.data), 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 Index(TreeLevel):
+ def render(self):
+ rendered = ""
+
+ for item in self.data.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
diff --git a/zippydoc/parser.py b/zippydoc/parser.py
new file mode 100644
index 0000000..57cdbc0
--- /dev/null
+++ b/zippydoc/parser.py
@@ -0,0 +1,86 @@
+from block_markup import *
+
+class Parser():
+ def __init__(self, template):
+ self.template = template
+
+ def render(self, text):
+ paragraphs = re.split("\s*\n\s*\n", text)
+ self.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 = self
+ else:
+ element_type = Text
+ data = " ".join(lines)
+
+ 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":
+ self.toc_items.append(element)
+
+ current_elements[indentation - 1].add(element)
+
+ current_level = indentation
+ current_elements[current_level] = element
+
+ return self.template.replace("{CONTENT}", current_elements[0].output())
diff --git a/zpy2html.py b/zpy2html.py
index 7968f34..f391d23 100644
--- a/zpy2html.py
+++ b/zpy2html.py
@@ -1,4 +1,5 @@
import os, argparse, sys, re
+import zippydoc
parser = argparse.ArgumentParser(description='Converts ZippyDoc source files to HTML.')
@@ -10,301 +11,7 @@ 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%s
' % (self.argname, self.process_inline_markup(self.data), self.render_children())
-
-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
+docparser = zippydoc.Parser(open("template.html").read())
for zpy in files:
destination = os.path.splitext(zpy)[0] + ".html"
@@ -313,87 +20,7 @@ for zpy in files:
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())
+ rendered = docparser.render(data)
f = open(destination, "w")
f.write(rendered)