From 313e059f367f2ac291cd409e77d22673f9595848 Mon Sep 17 00:00:00 2001 From: Qrius Date: Wed, 5 Mar 2025 08:41:05 +0100 Subject: First iteration of py skaldpress version --- src/smp/__init__.py | 13 +++++--- src/smp/builtins.py | 6 ++-- src/smp/macro_processor.py | 76 ++++++++++++++++++++++++++++------------------ 3 files changed, 58 insertions(+), 37 deletions(-) (limited to 'src/smp') diff --git a/src/smp/__init__.py b/src/smp/__init__.py index 63cecaf..22085ae 100644 --- a/src/smp/__init__.py +++ b/src/smp/__init__.py @@ -2,6 +2,11 @@ __version__ = "0.0.1" import smp.macro_processor import smp.builtins +__all__ = [ + "smp.macro_processor", + "smp.builtins", +] + def repl(): print("=Skaldpress Macro Processor (REPL)") @@ -15,8 +20,8 @@ def read_stdin(): import sys data = sys.stdin.read() - smp = macro_processor.MacroProcessor() - res = smp.process_input(data) + macro_processor = smp.macro_processor.MacroProcessor() + res = macro_processor.process_input(data) print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", file=sys.stderr) print(res) @@ -39,7 +44,7 @@ def main(): with open(sys.argv[1], "r") as f: file_content = f.read() - smp = macro_processor.MacroProcessor() - res = smp.process_input(file_content) + macro_processor = smp.macro_processor.MacroProcessor() + res = macro_processor.process_input(file_content) print("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━", file=sys.stderr) print(res) diff --git a/src/smp/builtins.py b/src/smp/builtins.py index f463a24..9a27864 100644 --- a/src/smp/builtins.py +++ b/src/smp/builtins.py @@ -1,10 +1,10 @@ -import smp.exceptions +# import smp.exceptions import subprocess import urllib.request import urllib.error import datetime import markdown -from gfm import AutolinkExtension, TaskListExtension +from gfm import AutolinkExtension, TaskListExtension # type: ignore def smp_builtin_define(macro_processor, macro_name, macro_value=None): @@ -147,7 +147,7 @@ def smp_builtin_html_from_markdown(macro_processor, text, extensions=list()): global LINK_CACHE -LINK_CACHE = dict() +LINK_CACHE: dict[str, tuple[bool, int, str]] = dict() def smp_builtin_wodl(macro_processor, link, timeout_seconds=5): diff --git a/src/smp/macro_processor.py b/src/smp/macro_processor.py index e85fbe9..8fa9d91 100644 --- a/src/smp/macro_processor.py +++ b/src/smp/macro_processor.py @@ -41,6 +41,8 @@ class MacroProcessor: warnings: list[Any] """ Global environment for python execution """ py_global_env: dict + py_local_env_alt: dict + py_local_env_current: dict special_macros: dict[str, tuple[Any, Any]] @@ -49,32 +51,40 @@ class MacroProcessor: self.macro_invocations = list() self.warnings = list() self.py_global_env = dict() - self._define_builtins(prefix=prefix) - - def _define_builtins(self, prefix=""): - self.macros[f"{prefix}define"] = smp.builtins.smp_builtin_define - self.macros[f"{prefix}undefine"] = smp.builtins.smp_builtin_undefine - self.macros[f"{prefix}define_array"] = smp.builtins.smp_builtin_define_array - self.macros[f"{prefix}ifdef"] = smp.builtins.smp_builtin_ifdef - self.macros[f"{prefix}ifndef"] = smp.builtins.smp_builtin_ifndef - self.macros[f"{prefix}ifeq"] = smp.builtins.smp_builtin_ifeq - self.macros[f"{prefix}ifneq"] = smp.builtins.smp_builtin_ifneq - self.macros[f"{prefix}include"] = smp.builtins.smp_builtin_include - self.macros[f"{prefix}include_verbatim"] = ( - smp.builtins.smp_builtin_include_verbatim - ) - self.macros[f"{prefix}shell"] = smp.builtins.smp_builtin_shell - self.macros[f"{prefix}dumpenv"] = smp.builtins.smp_builtin_dumpenv - self.macros[f"{prefix}eval"] = smp.builtins.smp_builtin_eval - self.macros[f"{prefix}array_push"] = smp.builtins.smp_builtin_array_push - self.macros[f"{prefix}array_each"] = smp.builtins.smp_builtin_array_each - self.macros[f"{prefix}array_size"] = smp.builtins.smp_builtin_array_size - self.macros[f"{prefix}explode"] = smp.builtins.smp_builtin_explode - self.macros[f"{prefix}format_time"] = smp.builtins.smp_builtin_format_time - self.macros[f"{prefix}html_from_markdown"] = ( - smp.builtins.smp_builtin_html_from_markdown - ) - self.macros[f"{prefix}wodl"] = smp.builtins.smp_builtin_wodl + self.py_local_env_alt = dict() + self.py_local_env_current = self.macros + self.indent_level = "" + + self._define_builtins(self.macros, prefix=prefix) + self._define_builtins(self.py_local_env_alt, prefix=prefix) + + def _define_builtins(self, env, prefix=""): + env[f"{prefix}macro_processor"] = self + env[f"{prefix}define"] = smp.builtins.smp_builtin_define + env[f"{prefix}undefine"] = smp.builtins.smp_builtin_undefine + env[f"{prefix}define_array"] = smp.builtins.smp_builtin_define_array + env[f"{prefix}ifdef"] = smp.builtins.smp_builtin_ifdef + env[f"{prefix}ifndef"] = smp.builtins.smp_builtin_ifndef + env[f"{prefix}ifeq"] = smp.builtins.smp_builtin_ifeq + env[f"{prefix}ifneq"] = smp.builtins.smp_builtin_ifneq + env[f"{prefix}include"] = smp.builtins.smp_builtin_include + env[f"{prefix}include_verbatim"] = smp.builtins.smp_builtin_include_verbatim + env[f"{prefix}shell"] = smp.builtins.smp_builtin_shell + env[f"{prefix}dumpenv"] = smp.builtins.smp_builtin_dumpenv + env[f"{prefix}eval"] = smp.builtins.smp_builtin_eval + env[f"{prefix}array_push"] = smp.builtins.smp_builtin_array_push + env[f"{prefix}array_each"] = smp.builtins.smp_builtin_array_each + env[f"{prefix}array_size"] = smp.builtins.smp_builtin_array_size + env[f"{prefix}explode"] = smp.builtins.smp_builtin_explode + env[f"{prefix}format_time"] = smp.builtins.smp_builtin_format_time + env[f"{prefix}html_from_markdown"] = smp.builtins.smp_builtin_html_from_markdown + env[f"{prefix}wodl"] = smp.builtins.smp_builtin_wodl + + def define_macro_string(self, macro_name, macro_value): + self.define_macro(macro_name, str(macro_value)) + + def define_macro(self, macro_name, macro_value): + self.macros[macro_name] = macro_value def expand_macro(self, macro_name: str, args: list[str] = list()) -> str: # Ignore trailing underscore in macro name, the parser will pop a space in front if @@ -104,14 +114,18 @@ class MacroProcessor: if callable(macro): signature = inspect.signature(macro) - macro_args = [] + macro_args: list[Any] = [] if ( "macro_processor" in signature.parameters or "smp" in signature.parameters ): macro_args.append(self) macro_args.extend(args) - return str(macro(*macro_args)) + try: + return str(macro(*macro_args)) + except Exception as e: + s = f"{macro_name}({','.join([repr(x) for x in macro_args])})" + raise Exception(s) if isinstance(macro, str): expanded = macro for i, arg in enumerate(args): @@ -143,8 +157,11 @@ class MacroProcessor: skip_next_line_ending = False + line_begin = True + # We should keep track of filename, linenumber, and character number on line here # So we can give sensible error messages + # Probably add to python stack trace? quote_level = 0 parens_level = 0 @@ -153,7 +170,6 @@ class MacroProcessor: while i < len(input): c = input[i] peek = None if i + 1 >= len(input) else input[i + 1] - # import sys # print(f"[{i:4}] {repr(c):4} -> {repr(peek):4} [{state}] = {repr(output)}", file=sys.stderr) @@ -264,7 +280,7 @@ class MacroProcessor: try: f = StringIO() with redirect_stdout(f): - exec(py_expr, self.py_global_env, self.macros) + exec(py_expr, self.py_global_env, self.py_local_env_current) s = f.getvalue() if s != "": output += s -- cgit v1.2.3