import re class HR_Epytext: def __init__(self, text): self.html_translation = { "&" : "&", "<" : "<", ">" : ">", "\"": """, "'" : "'" } identifier = r"[a-zA-Z_][a-zA-Z_0-9]*" parameter = r"{}(?:=[^,)]*)?".format(identifier) macro_tail = r"(?:[^,}]+,)*(?:[^,}]+})?" self.re_non_blank = re.compile(r"[^ ]") self.re_identifier = re.compile(r" *({})" \ .format(identifier)) self.re_signature = re.compile(r"^ *{} *\( *({} *(, *{} *)*)?\) *(->.*)?$" \ .format(identifier, parameter, parameter)) self.re_ident_list = re.compile(r" *({}(?: *, *{})*)" \ .format(identifier, identifier)) self.re_macro = re.compile(r"\\ *({}) *{{({})" \ .format(identifier, macro_tail)) self.re_macro_more = re.compile(macro_tail) self.blocks = self._blocks(text) def html(self): text = "" for block in self.blocks: text += block.format() return text def _blocks(self, text): state = state_t() blocks = [] fields = False flen = 0 stack = [] in_macro = False def end(): # close the current paragraph (if it has text at all) if state.text: while stack and state.indent <= stack[-1][0]: # nest blocks stack.pop() parent = stack[-1][1] if stack else None block = parblock_t(parent, fields, flen, state.indent, state.text, state.bullet) stack.append( (state.indent, block) ) blocks.append(block) state.reset() lines = text.split("\n") for line in lines: m = self.re_non_blank.search(line) start = m.start() if m else 0 line = line[start:] if in_macro: m = self.re_macro_more.match(line) if m: macro_tail = self._more_macro_tail(macro_tail, m.group(0)) if not self._is_macro_end(macro_tail): continue else: # broken macro end; finish it here self._verb("broken macro \"{}\" {{ \"{}\", end: \"{}\"".format( macro_name, macro_tail, line)) in_macro = False state.text += self._expand_macro(macro_name, macro_tail) line = line[m.end():] else: m = self.re_macro.search(line) if m: self._process_line(start, line[:m.start()], state, end) macro_name = m.group(1) macro_tail = m.group(2) in_macro = not self._is_macro_end(macro_tail) if in_macro: continue state.text += self._expand_macro(macro_name, macro_tail) line = line[m.end():] self._process_line(start, line, state, end) end() blocks = self._collect_bullet_lists(blocks) # set up nested blocks top_blocks = [] for block in blocks: if block.parent is None: top_blocks.append(block) else: block.parent.add_child(block) return top_blocks def _process_line(self, start, line, state, end): m = self.re_signature.match(line) if m: end() state.text = "{}".format( self._escape(line)) end() return if not line: end() return if line[0] == "@": end() # as per the EpyText spec, all lines from now on are fields fields = True flen = 1 m = self.re_identifier.match(line, 1) if m: mb = self.re_non_blank.search(line, m.end()) flen = mb.start() if mb else m.end() if m.group(1) == "param": cls = "epy_parameter" mp = self.re_ident_list.match(line, m.end()) if mp: m = mp else: cls = "epy_tag" line = "{}" \ .format(cls, m.group(1)) \ + self._escape(line[m.end():]) # Require at least one blank after the "-"; # f.i. "-1 (C++: int)" is not a bullet item. # Allow also "* " as bullet marker. elif line[:2] in ("- ", "* "): end() m = self.re_non_blank.search(line, 1) line = self._escape(line[m.start():]) if m else "" state.bullet = True else: line = self._escape(line) if start != state.indent: end() state.indent = start if state.text: state.text += "\n" state.text += line def _more_macro_tail(self, tail, more_tail): # hack around textwrap if tail[-1:] == "-": return tail + more_tail return tail + " " + more_tail def _is_macro_end(self, tail): return tail[-1:] == "}" def _expand_macro(self, name, tail): params = self._parse_macro_tail(tail) if name == "sq": self._adjust_macro_params(params, 4, 4) params = [p.strip() for p in params] table = """
| {} | {} |
| {} | {} |