You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

95 lines
2.9 KiB

from panflute import Div,Span,Para
from typing import List
# Import local files
from util import *
from context import Context
from mj_show import show
class Command:
pass
# This distinction is needed because while transforming the tree, inline
# elements cannot be replaced with block ones
class InlineCommand(Span, Command):
def replaceSelf(self, content: List[Element]) -> Span:
try:
return Span(*content)
except TypeError:
if len(content) == 1 and isinstance(content[0], Para):
return Span(*content[0].content)
else:
raise SyntaxError(f"The command {self.attributes['c']} returned multiple Paragraphs and must be executed using `::: {{c={self.attributes['c']}}}\\n:::`.")
pass
class BlockCommand(Div, Command):
def replaceSelf(self, content: List[Element]) -> Div:
return Div(*content)
pass
# This function is called in trasform.py, defining a command which can be
# called later using the function below
def handle_command_define(e: Element, c: Context):
if "define" in e.attributes:
if not c.get_command(e.attributes["define"]):
c.set_command(e.attributes["define"], compile(e.text, '<string>', 'exec'))
return nullify(e)
else:
raise NameError(f"Command already defined: '{e.attributes['define']}'")
if "redefine" in e.attributes:
c.set_command(e.attributes["redefine"], compile(e.text, '<string>', 'exec'))
return nullify(e)
return e
# This function executes commands and inline runnable code blocks (see
# transform.py for their syntax). Context can be accessed using `ctx` and there
# are four functions available to create output from these commands and the
# element the command has been called on (including its .content) can be
# accessed using `element`. Arguments can be passed down to the comand using
# the element's attributes.
#
# print and println append text to a buffer which is then interpreted as
# markdown with the current context.
#
# appendChild and appendChildren append panflute elements to a list which is
# then transformed. A command which does nothing looks like this:
# ```python {define=nop}
# appendChildren(element.content)
# ```
#
# These two types, appending and printing, cannot be mixed.
def executeCommand(source, element: Element, ctx: Context) -> 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'
text += s
def println(s: str=""):
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)
import panflute as pf
exec(source)
if mode == 'text':
return import_md(text, standalone=False)
if mode == 'elements':
return content
return []