diff --git a/command.py b/command.py index 83720f8..69d8b54 100644 --- a/command.py +++ b/command.py @@ -13,7 +13,7 @@ class InlineCommand(Span, Command): if len(content) == 1 and isinstance(content[0], Para): return Span(*content[0].content) else: - return Div(*content) + raise SyntaxError(f"The command {self.attributes['c']} returned multiple Paragraphs and must be executed using `::: {{c={self.attributes['c']}}}\\n:::`.") pass class MultilineCommand(Div, Command): diff --git a/formatitko.py b/formatitko.py index 341e3fa..626bfc1 100755 --- a/formatitko.py +++ b/formatitko.py @@ -1,16 +1,16 @@ #!/usr/bin/env python3 -# Import local files -from whitespace import * -from command import * -from util import * -import builtin_commands from panflute import * import re import sys from typing import List +# Import local files +from whitespace import * +from command import * +from util import * + from mj_show import show flags = ["dog"] @@ -20,14 +20,14 @@ def executeCommand(source: Code, element: Element) -> List[Element]: mode = 'empty' text = "" content = [] - def print(s: str): + 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' text += s - def println(s: str): + def println(s: str=""): print(s+"\n") def appendChild(e: Element): @@ -52,6 +52,9 @@ def executeCommand(source: Code, element: Element) -> List[Element]: def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element to transform """Transform the AST, making format-agnostic changes.""" + + global commands + global flags if isinstance(e, Whitespace) and bavlna(e): e = NBSP() @@ -60,11 +63,11 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t # `if` attribute. Only show this element if flag is set. if "if" in e.attributes: if not e.attributes["if"] in flags: - return Null() + return nullify(e) # `ifn` attribute. Only show this element if flag is NOT set if "ifn" in e.attributes: if e.attributes["ifn"] in flags: - return Null() + return nullify(e) # `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: @@ -72,32 +75,54 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t e = MultilineCommand(*e.content, identifier=e.identifier, classes=e.classes, attributes=e.attributes) else: 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) - return Null() + return nullify(e) - # Define commands + ## 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() + return nullify(e) 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() + return nullify(e) - # 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 = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]}) - - # Execute commands + if isinstance(e, Span) and len(e.content) == 1 and isinstance(e.content[0], Str): + print(isinstance(e, Span)) + ## Handle special command shorthand [!commandname]{} + if re.match(r"^![\w.]+$", e.content[0].text): + e = InlineCommand(identifier=e.identifier, classes=e.classes, attributes={**e.attributes, "c": e.content[0].text[1:]}) + + ## Handle include [>path/file.md]{} TODO: implement properly as commands + # This is for including content from files with their own flags + # and commands without affecting the state of the current + # document. + elif re.match(r"^>.+$", e.content[0].text): + includedDoc = convert_text(open(e.content[0].text[1:], "r").read(), standalone=True) # TODO: Put into function + oCommands = commands.copy() + oFlags = flags[:] + includedDoc = includedDoc.walk(transform) + commands = oCommands + flags = oFlags + #e = Div(*includedDoc.content) + + ## Handle import [#path/file.md]{} TODO: implement properly as commands + # This is the exact opposite of include. We take the commands + # and flags but drop the content. + elif re.match(r"^#.+$", e.content[0].text): + importedDoc = convert_text(open(e.content[0].text[1:], "r").read(), standalone=True) # TODO: Put into function + importedDoc.walk(transform) + return nullify(e) + + ## Execute commands # Walk transforms the children first, then the root element, # so the content of the element the command receives is # already transformed. @@ -110,7 +135,8 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t return e -doc = convert_text(open(sys.argv[1], "r").read(), standalone=True, extra_args=["-f", "markdown+fenced_code_attributes"]) +doc = convert_text(open(sys.argv[1], "r").read(), standalone=True) # TODO: Put into function so we can specify extensions and parameters globally. + print(show(doc)) doc = doc.walk(transform) diff --git a/test-import.md b/test-import.md new file mode 100644 index 0000000..50b67c3 --- /dev/null +++ b/test-import.md @@ -0,0 +1,7 @@ +``` {.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.") +``` diff --git a/test-include.md b/test-include.md new file mode 100644 index 0000000..d4f3c09 --- /dev/null +++ b/test-include.md @@ -0,0 +1,14 @@ +I am a little piece of content + +# With a title! + +And things... + +``` {.python .run} +# I set my own flags! +flags.append("cat") +``` + +:::{if=cat} +This should be only shown to included cats. +::: diff --git a/test.md b/test.md index ba43a5f..ac89762 100644 --- a/test.md +++ b/test.md @@ -1,15 +1,14 @@ -``` {.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.") -``` +--- +title: 'Wooooo a title' +--- +[#test-import.md]{} # Hello world! This is an *example* **yay**! +[>test-include.md]{} + :::{if=cat} This should only be shown to cats ::: diff --git a/util.py b/util.py index bf8540a..190aaca 100644 --- a/util.py +++ b/util.py @@ -1,4 +1,4 @@ -from panflute import Element, Block, Inline +from panflute import Element, Block, Inline, Null, Str import re def replaceEl(e: Element, r: Element) -> Element: @@ -8,3 +8,9 @@ def replaceEl(e: Element, r: Element) -> Element: return r def deleteEl(e: Element): del e.parent.content[e.index] + +def nullify(e: Element): + if isinstance(e, Inline): + return Str("") + elif isinstance(e, Block): + return Null()