From eab36a19af4dad6d4a6c62029165aa5308fe01bb Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Sun, 29 Jan 2023 20:32:48 +0100 Subject: [PATCH] A reimplementation of the command system. Also transform now doesn't do the traversing by itself but is called by panflute's walk. --- builtin_commands.py | 7 ----- command.py | 16 ++++++----- command_wrappers.py | 10 ------- formatitko.py | 69 ++++++++++++++++++++++----------------------- test.md | 14 +++++++-- util.py | 2 +- 6 files changed, 54 insertions(+), 64 deletions(-) delete mode 100644 builtin_commands.py delete mode 100644 command_wrappers.py diff --git a/builtin_commands.py b/builtin_commands.py deleted file mode 100644 index 608d24d..0000000 --- a/builtin_commands.py +++ /dev/null @@ -1,7 +0,0 @@ -from panflute import Element, Span -from command_wrappers import * - -def nop(e: Element) -> Element: - return e - -opendatatask = basicTextCommand("Toto je praktická open-data úloha. V [odevzdávátku](https://ksp.mff.cuni.cz/h/odevzdavatko/) si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.") diff --git a/command.py b/command.py index 10ebdc9..83720f8 100644 --- a/command.py +++ b/command.py @@ -1,4 +1,4 @@ -from panflute import Div,Span +from panflute import Div,Span,Para from util import * from typing import List @@ -7,14 +7,16 @@ class Command: class InlineCommand(Span, Command): def replaceSelf(self, content: List[Element]) -> Span: - if "c" in r.attributes: - del r.attributes["c"] - return replaceEl(self, Span(*content)) + try: + return Span(*content) + except TypeError: + if len(content) == 1 and isinstance(content[0], Para): + return Span(*content[0].content) + else: + return Div(*content) pass class MultilineCommand(Div, Command): def replaceSelf(self, content: List[Element]) -> Div: - if "c" in r.attributes: - del r.attributes["c"] - return replaceEl(self, Div(*content)) + return Div(*content) pass diff --git a/command_wrappers.py b/command_wrappers.py deleted file mode 100644 index a024fed..0000000 --- a/command_wrappers.py +++ /dev/null @@ -1,10 +0,0 @@ -from typing import Callable -from panflute import Element, Span, convert_text - -def basicTextCommand(s: str) -> Callable: - content = convert_text(s)[0].content - def f(e: Element) -> Element: - element = Span() - element.content = content - return element - return f diff --git a/formatitko.py b/formatitko.py index 60c6940..341e3fa 100755 --- a/formatitko.py +++ b/formatitko.py @@ -14,13 +14,14 @@ from typing import List from mj_show import show flags = ["dog"] -commands = [] +commands = {} def executeCommand(source: Code, element: Element) -> List[Element]: mode = 'empty' text = "" content = [] def print(s: str): + nonlocal mode, text if mode == 'elements': raise SyntaxError("Cannot use `print` and `appendChild` in one command at the same time.") mode = 'text' @@ -30,10 +31,15 @@ def executeCommand(source: Code, element: Element) -> List[Element]: print(s+"\n") def appendChild(e: Element): + nonlocal mode, content if mode == 'text': raise SyntaxError("Cannot use `print` and `appendChild` in one command at the same time.") mode = 'elements' content.append(e) + + def appendChildren(l: List[Element]): + for e in l: + appendChild(e) exec(source) @@ -42,83 +48,74 @@ def executeCommand(source: Code, element: Element) -> List[Element]: if mode == 'elements': return content - - + return [] - -def transform(e: Element) -> Element: # Returns next sibling element to transform +def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element to transform """Transform the AST, making format-agnostic changes.""" if isinstance(e, Whitespace) and bavlna(e): - e = replaceEl(e, NBSP()) + e = NBSP() if hasattr(e, "attributes"): # `if` attribute. Only show this element if flag is set. if "if" in e.attributes: if not e.attributes["if"] in flags: - deleteEl(e) - return + return Null() # `ifn` attribute. Only show this element if flag is NOT set if "ifn" in e.attributes: if e.attributes["ifn"] in flags: - deleteEl(e) - return + return Null() # `c` attribute. Execute a command with the name saved in this attribute. if (isinstance(e, Div) or isinstance(e, Span)) and "c" in e.attributes: if isinstance(e, Div): - e = replaceEl(e, MultilineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)) + e = MultilineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) else: - e = replaceEl(e, InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)) + e = InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) + print(f"Created inline command {e}") # 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 - + return Null() - if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, attributes): + # Define commands + # TODO: def/longdef? + 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') + return Null() else: raise NameError(f"Command already defined: '{e.attributes['define']}'") if "redefine" in e.attributes: commands[e.attributes["redefine"]] = compile(e.text, '', 'exec') + return Null() # 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:]})) + 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? + # Execute commands + # Walk transforms the children first, then the root element, + # so the content of the element the command receives is + # already transformed. + # TODO: Transform content that comes out of commands. if isinstance(e, Command): 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 - + + return e doc = convert_text(open(sys.argv[1], "r").read(), standalone=True, extra_args=["-f", "markdown+fenced_code_attributes"]) print(show(doc)) -print(doc.content._children) -#transform(doc) -#print("---------------------") -#print(convert_text(doc, input_format="panflute", output_format="markdown")) +doc = doc.walk(transform) +print("---------------------") +#print(show(doc)) +print(convert_text(doc, input_format="panflute", output_format="markdown")) diff --git a/test.md b/test.md index 840b123..ba43a5f 100644 --- a/test.md +++ b/test.md @@ -1,3 +1,11 @@ +``` {.python define=nop} +appendChildren(element.content) +``` + +``` {.python define=opendatatask} +print("Toto je praktická open-data úloha. V [odevzdávátku](https://ksp.mff.cuni.cz/h/odevzdavatko/) si necháte vygenerovat vstupy a odevzdáte příslušné výstupy. Záleží jen na vás, jak výstupy vyrobíte.") +``` + # Hello world! This is an *example* **yay**! @@ -18,14 +26,14 @@ This should only be shown to cats the second time This should be seen by all. -[!builtin_commands.opendatatask]{} +[!opendatatask]{} [This too!]{if=cat} [What]{.co} -[An inline command with contents and **bold** and another [!builtin_commands.nop]{} inside!]{c=builtin_commands.nop} +[An inline command with contents and **bold** and another [!nop]{} inside!]{c=nop} -[!builtin_commands.nop]{a=b} +[!nop]{a=b} diff --git a/util.py b/util.py index d70cc0d..bf8540a 100644 --- a/util.py +++ b/util.py @@ -1,4 +1,4 @@ -from panflute import Element +from panflute import Element, Block, Inline import re def replaceEl(e: Element, r: Element) -> Element: