import smp.exceptions import subprocess import urllib.request import urllib.error import datetime import markdown from gfm import AutolinkExtension, TaskListExtension def smp_builtin_define(macro_processor, macro_name, macro_value=None): macro_name = macro_processor.process_input(macro_name) if macro_value is not None: macro_value = macro_processor.process_input(macro_value) macro_processor.macros[macro_name] = macro_value else: macro_processor.macros[macro_name] = "" return "" def smp_builtin_undefine(macro_processor, macro_name): if macro_name in macro_processor.macros: del macro_processor[macro_name] return "" def smp_builtin_define_array(macro_processor, macro_name): macro_name = macro_processor.process_input(macro_name) macro_processor.macros[macro_name] = list() return "" def smp_builtin_ifdef(macro_processor, macro_name, iftrue, iffalse=None): if macro_name in macro_processor.macros: return macro_processor.process_input(iftrue) if iffalse is not None: return macro_processor.process_input(iffalse) return "" def smp_builtin_ifndef(macro_processor, macro_name, iftrue, iffalse=None): if macro_name not in macro_processor.macros: return macro_processor.process_input(iftrue) if iffalse is not None: return macro_processor.process_input(iffalse) return "" def smp_builtin_ifeq(macro_processor, a, b, iftrue, iffalse=None): a = macro_processor.process_input(a) b = macro_processor.process_input(b) if a == b: return macro_processor.process_input(iftrue) if iffalse is not None: return macro_processor.process_input(iffalse) return "" def smp_builtin_ifneq(macro_processor, a, b, iftrue, iffalse=None): a = macro_processor.process_input(a) b = macro_processor.process_input(b) if a != b: return macro_processor.process_input(iftrue) if iffalse is not None: return macro_processor.process_input(iffalse) return "" def smp_builtin_include(macro_processor, filename): filename = macro_processor.process_input(filename) with open(filename, "r") as f: file_content = f.read() return macro_processor.process_input(file_content) def smp_builtin_include_verbatim(macro_processor, filename): filename = macro_processor.process_input(filename) with open(filename, "r") as f: file_content = f.read() return file_content def smp_builtin_shell(macro_processor, cmd_args): cmd_args = macro_processor.process_input(cmd_args) return subprocess.check_output(cmd_args, shell=True).decode() def smp_builtin_eval(macro_processor, expression): r = eval(expression, macro_processor.py_global_env, macro_processor.macros) return r def smp_builtin_array_push(macro_processor, array_name, *values): if array_name not in macro_processor.macros: raise Exception(f"{array_name} is not a macro") if not isinstance(macro_processor.macros[array_name], list): raise Exception(f"{array_name} is not a array") for value in values: macro_processor.macros[array_name].append(value) return "" def smp_builtin_array_size(macro_processor, array_name): if array_name not in macro_processor.macros: raise Exception(f"{array_name} is not a macro") if not isinstance(macro_processor.macros[array_name], list): raise Exception(f"{array_name} is not a array") return str(len(macro_processor.macros[array_name])) def smp_builtin_array_each(macro_processor, array_name, template): if array_name not in macro_processor.macros: raise Exception(f"{array_name} is not a macro") if not isinstance(macro_processor.macros[array_name], list): raise Exception(f"{array_name} is not a array") out = "" for el in macro_processor.macros[array_name]: if isinstance(el, str): el = [el] out += macro_processor.process_input(macro_processor.expand_macro(template, el)) return out def smp_builtin_explode(macro_processor, array_name, delimiter, input): if array_name not in macro_processor.macros: raise Exception(f"{array_name} is not a macro") if not isinstance(macro_processor.macros[array_name], list): raise Exception(f"{array_name} is not a array") delimiter = macro_processor.process_input(delimiter) for el in macro_processor.process_input(input).split(delimiter): macro_processor.macros[array_name].append(el) return "" def smp_builtin_format_time(macro_processor, format, time): timestamp = macro_processor.process_input(time) dobj = datetime.datetime.fromisoformat(timestamp) return dobj.strftime(format) def smp_builtin_html_from_markdown(macro_processor, text, extensions=list()): # Get rid of quoting, I don't remember why, but the rust implementation does it like this. for _ in range(2): text = macro_processor.process_input(text) extensions.append(AutolinkExtension()) extensions.append(TaskListExtension(max_depth=2)) return markdown.markdown(text, extensions=extensions) global LINK_CACHE LINK_CACHE = dict() def smp_builtin_wodl(macro_processor, link, timeout_seconds=5): if link in LINK_CACHE: return LINK_CACHE[link] try: r = urllib.request.urlopen(link, timeout=timeout_seconds) working_link = (r.status == 200) and (r.reason == "OK") LINK_CACHE[link] = (working_link, r.status, r.reason) if not working_link: macro_processor.warnings.append( f"Dead link {link} ({r.status} {r.reason})!" ) except urllib.error.URLError as e: macro_processor.warnings.append(f"Dead link {link} ({e})!") return "" def smp_builtin_dumpenv(macro_processor): out = "" out += "━ Macros ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" for key, val in macro_processor.macros.items(): out += f"{repr(key)}: {repr(val)}\n" out += "━ Globals ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n" for key, val in macro_processor.py_global_env.items(): if key == "__builtins__": continue out += f"{repr(key)}: {repr(val)}\n" out += "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" return out