From 4aeadd3c719c18a9bf75a58b579f1409a15af6fd Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Tue, 24 Jan 2023 03:17:30 +0100 Subject: [PATCH] Started working on command execution, added direct code execution, removed some bugs. --- builtin_commands.py | 10 ++++++---- command.py | 4 ++++ command_wrappers.py | 10 ++++++++++ formatitko.py | 44 ++++++++++++++++++++++++++++++++++++-------- test.md | 22 ++++++++++++++++------ util.py | 7 +++++-- 6 files changed, 77 insertions(+), 20 deletions(-) create mode 100644 command_wrappers.py diff --git a/builtin_commands.py b/builtin_commands.py index 685039c..608d24d 100644 --- a/builtin_commands.py +++ b/builtin_commands.py @@ -1,5 +1,7 @@ -from panflute import Element +from panflute import Element, Span +from command_wrappers import * -class commands: - def woah(e: Element) -> Element: - return e +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 9cd3312..2e8f3e6 100644 --- a/command.py +++ b/command.py @@ -8,11 +8,15 @@ class Command: class InlineCommand(Span, Command): def call(self, f: Callable) -> Span: r = f(self) + if "c" in r.attributes: + del r.attributes["c"] return replaceEl(self, Span(*r.content, identifier=r.identifier, classes=r.classes, attributes=r.attributes)) pass class MultilineCommand(Div, Command): def call(self, f: Callable) -> Div: r = f(self) + if "c" in r.attributes: + del r.attributes["c"] return replaceEl(self, Div(*r.content, identifier=r.identifier, classes=r.classes, attributes=r.attributes)) pass diff --git a/command_wrappers.py b/command_wrappers.py new file mode 100644 index 0000000..a024fed --- /dev/null +++ b/command_wrappers.py @@ -0,0 +1,10 @@ +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 07125f4..9c15dbe 100644 --- a/formatitko.py +++ b/formatitko.py @@ -4,10 +4,11 @@ from whitespace import * from command import * from util import * -from builtin_commands import commands +import builtin_commands from panflute import * import re +import sys from mj_show import show @@ -15,6 +16,7 @@ flags = ["dog"] def transform(e: Element): """Transform the AST, making format-agnostic changes.""" + if isinstance(e, Whitespace) and bavlna(e): e = replaceEl(e, NBSP()) @@ -37,24 +39,50 @@ def transform(e: Element): else: e = replaceEl(e, InlineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes)) + + if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and "run" in e.classes: + exec(e.text) + deleteEl(e) + return + # 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): + 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) - # All commands on the inside execute first while being transformed - if isinstance(e, Command): - pass # TODO: Dodělat tohle - + # 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 + -doc = load() + + +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(show(doc)) +print(convert_text(doc, input_format="panflute", output_format="markdown")) diff --git a/test.md b/test.md index de6056c..840b123 100644 --- a/test.md +++ b/test.md @@ -2,20 +2,30 @@ This is an *example* **yay**! -```python3 -def what(): - return no way -``` :::{if=cat} This should only be shown to cats ::: + +``` {.python .run} +flags.append("cat") +``` + + +::::{if=cat} +This should only be shown to cats the second time +:::: + This should be seen by all. +[!builtin_commands.opendatatask]{} + [This too!]{if=cat} -[An inline command with contents and **bold** and another [!command]{} inside!]{c=bro} +[What]{.co} + +[An inline command with contents and **bold** and another [!builtin_commands.nop]{} inside!]{c=builtin_commands.nop} -[!woah]{a=b} +[!builtin_commands.nop]{a=b} diff --git a/util.py b/util.py index 3a05f67..cfade95 100644 --- a/util.py +++ b/util.py @@ -1,7 +1,10 @@ from panflute import Element +import re def replaceEl(e: Element, r: Element) -> Element: - e.parent.content[e.index] = r + parent = e.parent + parent.content[e.index] = r + r.parent = parent return r def deleteEl(e: Element): - del e.parent.content[e.index] + e.classes.append("deleted")