Browse Source

A much cleaner system that doesn't rely on global variables for context.

pull/28/head
Jan Černohorský 2 years ago
parent
commit
0a11b2e466
  1. 39
      context.py
  2. 42
      formatitko.py
  3. 2
      test-partial.md
  4. 2
      test.md

39
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]

42
formatitko.py

@ -10,13 +10,11 @@ from typing import List
from whitespace import * from whitespace import *
from command import * from command import *
from util import * from util import *
from context import *
from mj_show import show from mj_show import show
flags = ["dog"] def executeCommand(source, element: Element, ctx: Context) -> List[Element]:
commands = {}
def executeCommand(source: Code, element: Element) -> List[Element]:
mode = 'empty' mode = 'empty'
text = "" text = ""
content = [] content = []
@ -50,23 +48,20 @@ def executeCommand(source: Code, element: Element) -> List[Element]:
return [] 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.""" """Transform the AST, making format-agnostic changes."""
global commands
global flags
if isinstance(e, Whitespace) and bavlna(e): if isinstance(e, Whitespace) and bavlna(e):
e = NBSP() e = NBSP()
if hasattr(e, "attributes"): if hasattr(e, "attributes"):
# `if` attribute. Only show this element if flag is set. # `if` attribute. Only show this element if flag is set.
if "if" in e.attributes: if "if" in e.attributes:
if not e.attributes["if"] in flags: if not context.is_flag_set(e.attributes["if"]):
return nullify(e) return nullify(e)
# `ifn` attribute. Only show this element if flag is NOT set # `ifn` attribute. Only show this element if flag is NOT set
if "ifn" in e.attributes: if "ifn" in e.attributes:
if e.attributes["ifn"] in flags: if context.is_flag_set(e.attributes["ifn"]):
return nullify(e) return nullify(e)
# `c` attribute. Execute a command with the name saved in this attribute. # `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. # document.
if (isinstance(e, Div)) and "partial" in e.attributes: if (isinstance(e, Div)) and "partial" in e.attributes:
includedDoc = import_md(open(e.attributes["partial"], "r").read()) includedDoc = import_md(open(e.attributes["partial"], "r").read())
oFlags = flags[:] nContext = Context(includedDoc, context)
oCommands = commands.copy() includedDoc = includedDoc.walk(transform, nContext)
includedDoc = includedDoc.walk(transform)
flags = oFlags
commands = oCommands
e = Div(*includedDoc.content) e = Div(*includedDoc.content)
# Execute python code inside source code block # 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: if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and "run" in e.classes:
exec(e.text) e = executeCommand(e.text, None, context)
return nullify(e)
## Command defines ## Command defines
# possible TODO: def/longdef? # possible TODO: def/longdef?
if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, "attributes"): if isinstance(e, CodeBlock) and hasattr(e, "classes") and "python" in e.classes and hasattr(e, "attributes"):
if "define" in e.attributes: if "define" in e.attributes:
if not e.attributes["define"] in commands: if not context.get_command(e.attributes["define"]):
commands[e.attributes["define"]] = compile(e.text, '<string>', 'exec') context.set_command(e.attributes["define"], compile(e.text, '<string>', 'exec'))
return nullify(e) return nullify(e)
else: else:
raise NameError(f"Command already defined: '{e.attributes['define']}'") raise NameError(f"Command already defined: '{e.attributes['define']}'")
if "redefine" in e.attributes: if "redefine" in e.attributes:
commands[e.attributes["redefine"]] = compile(e.text, '<string>', 'exec') context.set_command(e.attributes["redefine"], compile(e.text, '<string>', 'exec'))
return nullify(e) return nullify(e)
## Shorthands ## Shorthands
@ -118,7 +109,7 @@ def transform(e: Element, doc: Doc) -> Element: # Returns next sibling element t
# and flags but drop the content. # and flags but drop the content.
elif re.match(r"^#.+$", e.content[0].text): elif re.match(r"^#.+$", e.content[0].text):
importedDoc = import_md(open(e.content[0].text[1:], "r").read()) importedDoc = import_md(open(e.content[0].text[1:], "r").read())
importedDoc.walk(transform) importedDoc.walk(transform, context)
return nullify(e) return nullify(e)
## Execute commands ## 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 # so the content of the element the command receives is
# already transformed. # already transformed.
if isinstance(e, Command): 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']}'.") raise NameError(f"Command not defined '{e.attributes['c']}'.")
e = e.replaceSelf(executeCommand(commands[e.attributes["c"]], e)) e = e.replaceSelf(executeCommand(context.get_command(e.attributes["c"]), e, context))
e.walk(transform) e.walk(transform, context)
return e return e
@ -138,7 +129,8 @@ doc = import_md(open(sys.argv[1], "r").read())
print(show(doc)) print(show(doc))
doc = doc.walk(transform) context = Context(doc)
doc = doc.walk(transform, context)
print("---------------------") print("---------------------")
#print(show(doc)) #print(show(doc))
print(convert_text(doc, input_format="panflute", output_format="markdown")) print(convert_text(doc, input_format="panflute", output_format="markdown"))

2
test-partial.md

@ -9,7 +9,7 @@ And things...
``` {.python .run} ``` {.python .run}
# I set my own flags! # I set my own flags!
flags.append("cat") ctx.set_flag("cat", True)
``` ```
:::{if=cat} :::{if=cat}

2
test.md

@ -16,7 +16,7 @@ This should only be shown to cats
``` {.python .run} ``` {.python .run}
flags.append("cat") ctx.set_flag("cat", True)
``` ```

Loading…
Cancel
Save