import re import sys import os.path import traceback DEBUG = 0 class Node(object): node_pattern = re.compile(r'^(?P\s*)<<(?P[^<]+)>>\s*') def __init__(self, text, name, **opts): """text will be passed by doctuils, and it'll be a list of string""" self.text = text self.pieces = [] self.output = None self.name = Node.compressname(name) self.outputfile = opts.get('file', None) self.init() self.parent_nodelist = add_node(self) def render(self, nodelist): if self.output is None: buf = [] for p in self.pieces: if isinstance(p, (str, unicode)): buf.append(p) else: buf.extend(p.render(nodelist)) self.output = buf return self.output def init(self): for i in self.text: b = Node.node_pattern.search(i) if b: nodename = b.groupdict()['nodename'] indent = len(b.groupdict()['blank']) self.pieces.append(LinkNode(Node.compressname(nodename), indent)) else: self.pieces.append(i) def compressname(name): return name.replace(' ', '') compressname = staticmethod(compressname) class LinkNode(object): def __init__(self, name, indent=0): self.name = name self.indent = indent def render(self, nodelist): node = nodelist.get(self.name, None) if node: return [' '*self.indent + x for x in node.render(nodelist)] else: return [' '*self.indent + '<<' + self.name + '>>'] class OrderedDict(dict): def __init__(self, d=None): super(dict, self).__init__(d) self._sequence = [] def __setitem__(self, key, val): if not self.has_key(key): self._sequence.append(key) dict.__setitem__(self, key, val) def __delitem__(self, key): dict.__delitem__(self, key) self._sequence.remove(key) def getlist(self): return self._sequence class NodeList(object): def __init__(self): self.list = {} self.currentfile = None def add_node(self, node): if node.outputfile: self.currentfile = node.outputfile nodelist = self.list.setdefault(self.currentfile, OrderedDict({})) nodelist[node.name] = node return nodelist def render(self): for filename, nodelist in self.list.items(): if filename: basedir, filen = os.path.split(filename) if basedir and not os.path.exists(basedir): try: print 'create dir', basedir os.makedirs(basedir) except: error_output('Error: there is something wrong with create directory ' + basedir) try: print 'create file', filename f = file(filename, 'w') except: error_output('Error: there is something wrong with create file ' + filename) else: f = sys.stdout f.write(self._render(nodelist)) f.write('\n') f.close() def _render(self, nodelist): main = self.find_mainnode(nodelist) return '\n'.join(flatlist(main.render(nodelist))) def find_mainnode(self, nodelist): """if there is a node named main, then it's the main node, if there is none, so the first node is the main node""" main = nodelist.get("main", None) if not main: main = nodelist.get(nodelist.getlist()[0]) return main _nodelist = NodeList() def add_node(node): return _nodelist.add_node(node) def get_root_nodelist(): return _nodelist def render(): get_root_nodelist().render() def error_output(msg): print msg if DEBUG: traceback.print_exc() else: print 'You can set --debug to see the traceback' sys.exit(1) def flatlist(alist): buf = [] for i in alist: if isinstance(i, list): buf.extend(flatlist(i)) else: buf.append(i) return buf if __name__ == '__main__': text = """Test program << node 1 >> << node 2 >> """ node = Node(text.splitlines(), "main") node1text = """if __name__ == '__main__': """ node1 = Node(node1text.splitlines(), "node1") node2text = """print "hello, world" """ node2 = Node(node2text.splitlines(), "node2") render()