From da65b68f1f3a806fc000743b88dd6f56fb44ceb0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= <hugo@lysator.liu.se>
Date: Tue, 19 Sep 2023 07:08:05 +0200
Subject: [PATCH] Emit puppet code instead of match objects.

As a steping stone, the raw match data was emited to the frontend. This
just properly renders it.
---
 muppet/format.py | 51 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 49 insertions(+), 2 deletions(-)

diff --git a/muppet/format.py b/muppet/format.py
index 9e5be76..7294f66 100644
--- a/muppet/format.py
+++ b/muppet/format.py
@@ -11,7 +11,9 @@ import html
 import re
 from typing import (
     Tuple,
+    Any,
 )
+import types
 
 from .puppet.parser import puppet_parser
 import logging
@@ -28,7 +30,11 @@ from .puppet.strings import (
 )
 from muppet.puppet.ast import build_ast
 # from muppet.puppet.format import to_string
-from muppet.parser_combinator import ParserCombinator, ParseError
+from muppet.parser_combinator import (
+    ParserCombinator,
+    ParseError,
+    MatchCompound,
+)
 from muppet.puppet.format.parser import ParserFormatter
 
 
@@ -39,6 +45,47 @@ logger = logging.getLogger(__name__)
 param_doc: dict[str, str] = {}
 
 
+def reserialize(obj: Any) -> str:
+    """
+    Reconstruct puppet code after parsing it.
+
+    After building the parser, and parsing the puppet code into a tree
+    of MatchObjects; this procedure returns it into puppet code.
+    Difference being that we now have metadata, meaning that syntax
+    highlighting and variable hyperlinks can be inserted.
+
+    :param obj:
+        Should be assumed to be a list of MatchObject's, or something similar.
+
+        MatchCompound objects are serialized as
+
+        .. code-block:: html
+
+            <span class="{type}">{body}</span>
+
+        strings as themselves, and lists have reserialize mapped over them.
+
+    """
+    out: list[str] = []
+    # logger.info("obj = %a", obj)
+    match obj:
+        case str(s):
+            out += [s]
+        case MatchCompound(type=type, matched=xs):
+            # logger.warning("xs = %a", xs)
+            body = ''.join(reserialize(x) for x in xs)
+            out += [f'<span class="{type}">{body}</span>']
+        case [*xs]:
+            out += [reserialize(x for x in xs)]
+        case rest:
+            if isinstance(rest, types.GeneratorType):
+                out += [reserialize(x) for x in rest]
+            else:
+                logger.error("Unknown type: %a", rest)
+
+    return ''.join(out)
+
+
 def parse_puppet(source: str, file: str) -> str:
     """
     Parse and syntax highlight the given puppet source.
@@ -56,7 +103,7 @@ def parse_puppet(source: str, file: str) -> str:
     # Run the generatefd parser, giving us a list of match objects
     match_objects = ParserCombinator(source, file).get(parser)
     # logger.error("match_objects: %a", match_objects)
-    return '\n'.join(repr(m) for m in match_objects)
+    return reserialize(match_objects)
 
 # --------------------------------------------------
 
-- 
GitLab