Johan Rong 2 дней назад
Родитель
Сommit
f884c77285
1 измененных файлов с 95 добавлено и 0 удалено
  1. 95 0
      src/zelkelc/expander/expander.py

+ 95 - 0
src/zelkelc/expander/expander.py

@@ -0,0 +1,95 @@
+import re
+from itertools import count
+
+from src.zelkelc.ast import types
+from src.zelkelc.ast.parser import ParseError, Parser
+from src.zelkelc.lexer import token
+
+
+class MacroError(Exception):
+    pass
+
+
+_PH = re.compile(r"\{\{|\}\}|\{(\d*)(?::(\w+))?\}")
+
+
+def render_llvm(template: str, vals: list[str], tys: list[str], eid: int) -> str:
+    auto = iter(range(len(vals)))
+
+    def sub(m: re.Match) -> str:
+        if m.group(0) == "{{":
+            return "{"
+        if m.group(0) == "}}":
+            return "}"
+        idx, spec = m.group(1), m.group(2)
+        if spec == "id":
+            return str(eid)
+        i = int(idx) if idx else next(auto)
+        if i >= len(vals):
+            raise MacroError("#llvm placeholder out of range")
+        if spec == "ty":
+            return tys[i]
+        return vals[i]
+
+    return _PH.sub(sub, template)
+
+
+class Expander:
+    def __init__(self, ids: count):
+        self.ids = ids
+        self.expansion_id = 0
+
+    def run(self, nodes: list[types.Node]) -> list[types.Node]:
+        return self.expand_nodes(nodes, depth=0)
+
+    def expand_nodes(self, nodes: list[types.Node], depth: int) -> list[types.Node]:
+        if depth > 64:
+            raise MacroError("macro recursion limit")
+        out: list[types.Node] = []
+        for n in nodes:
+            if isinstance(n, types.MacroCall):
+                expanded = self.expand_call(n)
+                out.extend(self.expand_nodes(expanded, depth + 1))
+            elif isinstance(n, types.FunctionDeclaration):
+                n.body = self.expand_nodes(n.body, depth)
+                out.append(n)
+            elif isinstance(n, types.ClassDeclaration):
+                for m in n.methods:
+                    m.body = self.expand_nodes(m.body, depth)
+                out.append(n)
+            else:
+                out.append(n)
+        return out
+
+    def expand_call(self, call: types.MacroCall) -> list[types.Node]:
+        if call.name == "llvm":
+            return [self.expand_llvm(call)]
+        raise MacroError(f"unknown macro #{call.name}")
+
+    def expand_llvm(self, call: types.MacroCall) -> types.InlineIR:
+        tokens = call.args
+        if not tokens:
+            raise MacroError("#llvm requires at least a template string")
+        if not isinstance(tokens[0], token.String):
+            raise MacroError("#llvm first argument must be a string template")
+
+        template = tokens[0].value
+        arg_exprs: list[types.Expression] = []
+        i = 1
+        while i < len(tokens):
+            if isinstance(tokens[i], token.Comma):
+                i += 1
+                continue
+            sub = Parser(tokens, i, self.ids)
+            try:
+                expr = sub.parse_expression()
+            except ParseError as e:
+                raise MacroError(f"#llvm argument parse error: {e}") from e
+            if expr is None:
+                raise MacroError("#llvm expected expression argument")
+            arg_exprs.append(expr)
+            i = sub.cursor
+
+        eid = self.expansion_id
+        self.expansion_id += 1
+        return types.InlineIR(template=template, args=arg_exprs, expansion_id=eid)