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