From 0a11b2e466053af1cce69d3e9ac3e528783ee313 Mon Sep 17 00:00:00 2001 From: Greenscreener Date: Tue, 31 Jan 2023 13:38:12 +0100 Subject: [PATCH] A much cleaner system that doesn't rely on global variables for context. --- context.py | 39 +++++++++++++++++++++++++++++++++++++++ formatitko.py | 42 +++++++++++++++++------------------------- test-partial.md | 2 +- test.md | 2 +- 4 files changed, 58 insertions(+), 27 deletions(-) create mode 100644 context.py diff --git a/context.py b/context.py new file mode 100644 index 0000000..0f5a42c --- /dev/null +++ b/context.py @@ -0,0 +1,39 @@ + +from panflute import Doc + +class Context: + def __init__(self, doc: Doc=None, parent: 'Context'=None): + self.parent = parent + self._commands = {} + self._flags = {} + self.doc = doc + + def get_command(self, command: str): + if command in self._commands: + return self._commands[command] + elif self.parent: + return self.parent.get_command(command) + else: + return None + + def set_command(self, command: str, val): + self._commands[command] = val + + def unset_command(self, command: str): + del self._commands[command] + + def is_flag_set(self, flag: str): + if flag in self._flags and self._flags[flag]: + return True + elif self.parent: + return self.parent.is_set(flag) + else: + return False + + def set_flag(self, flag: str, val: bool): + self._flags[flag] = val + + def unset_flag(self, flag): + del self._flags[flag] + + diff --git a/formatitko.py b/formatitko.py index 56b2644..deaa1a0 100755 --- a/formatitko.py +++ b/formatitko.py @@ -10,13 +10,11 @@ from typing import List from whitespace import * from command import * from util import * +from context import * from mj_show import show -flags = ["dog"] -commands = {} - -def executeCommand(source: Code, element: Element) -> List[Element]: +def executeCommand(source, element: Element, ctx: Context) -> List[Element]: mode = 'empty' text = "" content = [] @@ -50,11 +48,8 @@ def executeCommand(source: Code, element: Element) -> List[Element]: return [] -def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element to transform +def transform(e: Element, context: Context) -> 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() @@ -62,11 +57,11 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t 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: + if not context.is_flag_set(e.attributes["if"]): 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: + if context.is_flag_set(e.attributes["ifn"]): return nullify(e) # `c` attribute. Execute a command with the name saved in this attribute. @@ -82,29 +77,25 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t # document. if (isinstance(e, Div)) and "partial" in e.attributes: includedDoc = import_md(open(e.attributes["partial"], "r").read()) - oFlags = flags[:] - oCommands = commands.copy() - includedDoc = includedDoc.walk(transform) - flags = oFlags - commands = oCommands + nContext = Context(includedDoc, context) + includedDoc = includedDoc.walk(transform, nContext) e = Div(*includedDoc.content) # 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 nullify(e) + e = executeCommand(e.text, None, context) ## Command defines # possible 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') + if not context.get_command(e.attributes["define"]): + context.set_command(e.attributes["define"], compile(e.text, '', 'exec')) 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') + context.set_command(e.attributes["redefine"], compile(e.text, '', 'exec')) return nullify(e) ## Shorthands @@ -118,7 +109,7 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t # and flags but drop the content. elif re.match(r"^#.+$", e.content[0].text): importedDoc = import_md(open(e.content[0].text[1:], "r").read()) - importedDoc.walk(transform) + importedDoc.walk(transform, context) return nullify(e) ## Execute commands @@ -126,10 +117,10 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t # so the content of the element the command receives is # already transformed. if isinstance(e, Command): - if not e.attributes["c"] in commands: + if not context.get_command(e.attributes["c"]): raise NameError(f"Command not defined '{e.attributes['c']}'.") - e = e.replaceSelf(executeCommand(commands[e.attributes["c"]], e)) - e.walk(transform) + e = e.replaceSelf(executeCommand(context.get_command(e.attributes["c"]), e, context)) + e.walk(transform, context) return e @@ -138,7 +129,8 @@ doc = import_md(open(sys.argv[1], "r").read()) print(show(doc)) -doc = doc.walk(transform) +context = Context(doc) +doc = doc.walk(transform, context) print("---------------------") #print(show(doc)) print(convert_text(doc, input_format="panflute", output_format="markdown")) diff --git a/test-partial.md b/test-partial.md index cca0af5..8b366f4 100644 --- a/test-partial.md +++ b/test-partial.md @@ -9,7 +9,7 @@ And things... ``` {.python .run} # I set my own flags! -flags.append("cat") +ctx.set_flag("cat", True) ``` :::{if=cat} diff --git a/test.md b/test.md index de23e1a..565a5ea 100644 --- a/test.md +++ b/test.md @@ -16,7 +16,7 @@ This should only be shown to cats ``` {.python .run} -flags.append("cat") +ctx.set_flag("cat", True) ```