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 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 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, '', '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, '', 'exec')) return nullify(e) 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) exec(source) if mode == 'text': return import_md(text, standalone=False) if mode == 'elements': return content return []