diff --git a/command.py b/command.py index 2e8f3e6..10ebdc9 100644 --- a/command.py +++ b/command.py @@ -1,22 +1,20 @@ from panflute import Div,Span from util import * -from typing import Callable +from typing import List class Command: pass class InlineCommand(Span, Command): - def call(self, f: Callable) -> Span: - r = f(self) + def replaceSelf(self, content: List[Element]) -> Span: if "c" in r.attributes: del r.attributes["c"] - return replaceEl(self, Span(*r.content, identifier=r.identifier, classes=r.classes, attributes=r.attributes)) + return replaceEl(self, Span(*content)) pass class MultilineCommand(Div, Command): - def call(self, f: Callable) -> Div: - r = f(self) + def replaceSelf(self, content: List[Element]) -> Div: if "c" in r.attributes: del r.attributes["c"] - return replaceEl(self, Div(*r.content, identifier=r.identifier, classes=r.classes, attributes=r.attributes)) + return replaceEl(self, Div(*content)) pass diff --git a/formatitko.py b/formatitko.py old mode 100644 new mode 100755 index 9c15dbe..60c6940 --- a/formatitko.py +++ b/formatitko.py @@ -9,12 +9,44 @@ import builtin_commands from panflute import * import re import sys +from typing import List from mj_show import show flags = ["dog"] +commands = [] -def transform(e: Element): +def executeCommand(source: Code, element: Element) -> List[Element]: + mode = 'empty' + text = "" + content = [] + def print(s: str): + if mode == 'elements': + raise SyntaxError("Cannot use `print` and `appendChild` in one command at the same time.") + mode = 'text' + text += s + + def println(s: str): + print(s+"\n") + + def appendChild(e: Element): + if mode == 'text': + raise SyntaxError("Cannot use `print` and `appendChild` in one command at the same time.") + mode = 'elements' + content.append(e) + + exec(source) + + if mode == 'text': + return convert_text(text) + if mode == 'elements': + return content + + + + + +def transform(e: Element) -> Element: # Returns next sibling element to transform """Transform the AST, making format-agnostic changes.""" if isinstance(e, Whitespace) and bavlna(e): @@ -40,49 +72,53 @@ def transform(e: Element): e = replaceEl(e, InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)) + # Execute python code inside source code block if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and "run" in e.classes: exec(e.text) deleteEl(e) return + + if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, attributes): + if "define" in e.attributes: + if not e.attributes["define"] in commands: + commands[e.attributes["define"]] = compile(e.text, '', 'exec') + else: + raise NameError(f"Command already defined: '{e.attributes['define']}'") + if "redefine" in e.attributes: + commands[e.attributes["redefine"]] = compile(e.text, '', 'exec') + # Handle special command shorthand [!commandname]{} if isinstance(e, Span) and len(e.content) == 1 and isinstance(e.content[0], Str) and re.match(r"^![\w.]+$", e.content[0].text): e = replaceEl(e, InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]})) # The command is executed first and then its contents are transformed. TODO: Is this what we want? if isinstance(e, Command): - if re.match(r"^[\w.]+$", e.attributes["c"]): - try: - e = e.call(eval(e.attributes["c"])) - except NameError: - raise NameError(f"Command not found: '{e.attributes['c']}'.") - else: - raise SyntaxError(f"Invalid command syntax: '{e.attributes['c']}'.") - - # Recursively transform all contents. - if hasattr(e, "content"): - for c in e.content: - # Deleted elements are only marked to be deleted with a class, because otherwise - # it would mess up this for cycle. - transform(c) - - # Delete elements marked to be deleted. This is somewhat inelegant. - ci = 0 - while ci < len(e.content): - c = e.content[ci] - if hasattr(c, "classes") and "deleted" in c.classes: - del e.content[ci] - else: - ci+=1 + if not e.attributes["c"] in commands: + raise NameError(f"Command not defined '{e.attributes['c']}'.") + e = e.replaceSelf(executeCommand(commands[e.attributes["c"]], e)) + + # Transformation of other elements. This is not done using a for cycle, + # because deletions of elements would mess with it. + children = ((child_name, getattr(e, child_name)) for child_name in e._children) + for child_name, child in children: + if hasattr(e, "content") and len(e.content) > 0: + next = transform(e.content[0]) + while next != None: + next = transform(next) + print(next) + # Return nearest sibling to transform + return e.next doc = convert_text(open(sys.argv[1], "r").read(), standalone=True, extra_args=["-f", "markdown+fenced_code_attributes"]) print(show(doc)) -transform(doc) -print("---------------------") -print(convert_text(doc, input_format="panflute", output_format="markdown")) +print(doc.content._children) +#transform(doc) +#print("---------------------") +#print(convert_text(doc, input_format="panflute", output_format="markdown")) diff --git a/util.py b/util.py index cfade95..d70cc0d 100644 --- a/util.py +++ b/util.py @@ -7,4 +7,4 @@ def replaceEl(e: Element, r: Element) -> Element: r.parent = parent return r def deleteEl(e: Element): - e.classes.append("deleted") + del e.parent.content[e.index]