#! /usr/bin/env python # MMD by David A. Moon is licensed under a # Creative Commons Attribution-ShareAlike 4.0 International License # http://creativecommons.org/licenses/by-sa/4.0/ # # Please inform me if you find this useful, or any of the ideas embedded in it. # Comments and criticisms to dave underscore moon atsign alum dot mit dot edu. # Incorporates details_shim by Tyler Uebele licensed as follows: # # Copyright (c) 2013 Tyler Uebele # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # Convert MMD to HTML import codecs import math import os import re import sys import time default_code_style = "display:table; border:1px solid black; background-color:whitesmoke; padding-left:5px; padding-right:5px; padding-top:5px; padding-bottom:5px" # Global state # g is bound to it in most functions class MMD_globals: pass def MMD(input_stream, output_directory): self = MMD_globals() self.input_stream = input_stream self.output_stream = False self.output_directory = output_directory self.line_number = 0 self.title_prefix = False self.page_number = 0 self.current_chapter_name = False self.page_titles = [] #[[chapter [section subsection...]...]...] self.all_references = [] #[[see_arg href]...] self.all_anchors = [] #{"chapter#anchor"...} self.pre_html = [] self.post_html = [] self.this_page_file = False self.previous_page_file = False self.current_mode = 'normal' self.nesting_level = 0 self.code_style = default_code_style return self dot_handlers = dict() # maps a string to a list of before, main, after function # Define the handler(s) with def first, then call this # Handler arguments are (g, line, directive_name) def defdot(directive_name, handler, before=None, after=None): dot_handlers[directive_name] = [before, handler, after] # Main Program def main(argv): if len(argv) < 2 or len(argv) > 3: print("Usage: mmd input_pathname [output_directory]") return 1 else: input_pathname = argv[1] output_directory = None if len(argv) == 3: output_directory = argv[2] else: output_directory = os.path.join(os.path.dirname(input_pathname), "HTML") g = MMD(codecs.open(input_pathname, mode='r', encoding='utf-8'), output_directory) try: os.mkdir(g.output_directory) except OSError: pass try: process_lines(g) return 0 finally: g.input_stream.close() if g.output_stream: g.output_stream.close() # Process lines until end of input file def process_lines(g): while True: line = next_line(g) if line == False: break line2 = line.lstrip() # Remove leading whitespace if len(line2) == 0: # Blank line separates paragraphs if g.output_stream: change_mode(g, 'normal') output_raw(g, "

") elif line2[0] == '.': directive_line(g, line2) elif line2[0] == '*': list_line(g, line2, 'UL') elif line2[0] == '#': list_line(g, line2, 'OL') elif line2[0] == '|': table_line(g, line2) else: change_mode(g, 'normal') output_with_flags_and_translations(g, line) # Input file exhausted end_page(g, False) # Check for broken links for x in g.all_references: see_arg = x[0] href = x[1] if not (href in g.all_anchors): error_output("Broken link: .see " + see_arg + "\n") # Generate index.html file index_path = os.path.join(g.output_directory, "index.html") g.output_stream = open(index_path, "w") output_index(g) index_path # Line Handlers def undefined_handler(g, line, directive_name): error("Unrecognized directive ." + directive_name + " on line " + str(g.line_number)) undefined_handlers = [undefined_handler, undefined_handler, undefined_handler] def directive_line(g, line): change_mode(g, 'normal') space = line.find(" ") directive_end = len(line) if space >= 0: directive_end = space directive_name = line[1 : directive_end] # 1 skips initial period dot_name = directive_name.lower() handler = undefined_handlers if dot_name in dot_handlers: handler = dot_handlers[dot_name] before = handler[0] main_handler = handler[1] after = handler[2] if before: before(g, line, directive_name) if space >= 0: main_handler(g, line[space+1 : len(line)], directive_name) else: end_line = ".end " + directive_name while True: this_line = next_line(g) if this_line == False: break if this_line == end_line: break main_handler(g, this_line, directive_name) if after: after(g, line, directive_name) def list_line(g, line, mode): change_mode(g, mode) # number of leading stars or splats level = 0 while level < len(line) and line[level] == line[0]: level = level + 1 while g.nesting_level > level: output_raw(g, "") g.nesting_level = g.nesting_level - 1 while g.nesting_level < level: type = '1' if g.nesting_level < 3: type = "1ai"[g.nesting_level] if mode == 'UL': output_raw(g, "