stempl

Simple template language written in python

git clone git://git.janzachar.dev/stempl.git


0from dataclasses import dataclass, asdict
1import parser
2
3from context import Context
4import subprocess, json
5import page
6from pathlib import Path
7
8
9class Interpreter:
10    def __init__(self, ast: parser.Node, ctx: Context):
11        self.ast = ast
12        self.ctx = ctx
13
14    def interpret(self, node = None):
15        if node is None:
16            node = self.ast
17
18        if isinstance(node, parser.Raw):
19            return self.raw(node)
20        
21        if isinstance(node, parser.Statement):
22            return str(self.statement(node))
23
24        raise TypeError("Raw or Statement expected!")
25
26    def raw(self, node):
27        out = node.text
28        if node.right is not None:
29            out += self.interpret(node.right)
30
31        return out
32
33    def statement(self, node):
34        out = self.__getValue(node.child)
35
36        if node.right is not None:
37            out += self.interpret(node.right)
38
39        return out
40
41    def conditional(self, node):
42        condition = self.__getValue(node.condition)
43
44        if condition:
45            return self.interpret(node.body)
46        else:
47            return self.interpret(node.alternative)
48
49    def iterative(self, node):
50        ctx = Context(self.ctx)
51        inter = Interpreter(node.body, ctx)
52        sequence = self.__getValue(node.sequence)
53
54        out = ""
55        for i in sequence:
56            ctx[node.iterator.name] = i
57            out += inter.interpret()
58
59        return out
60
61    def inclusion(self, node):
62        path = self.__getValue(node.path)
63
64        if node.israw:
65            with open(path) as fp:
66                return fp.read()
67        
68        if path.endswith(".py"):
69            # ACHTUNG! SHIT CODE
70            def default(o):
71                if isinstance(o, Context):
72                    return None
73                    return o.data
74                if isinstance(o, Path):
75                    return str(o)
76                raise TypeError(f"Object of type {type(o).__name__} is not JSON serializable")
77
78            j = json.dumps(self.ctx.data, default=default)
79            p = subprocess.run(
80                ["python3", f"{self.ctx["site"]["src"]}/{path}", j],
81                capture_output=True,
82                text=True
83            )
84            print(p.stderr)
85            return p.stdout
86
87        p = page.Page(path, self.ctx)
88        return p.run()
89
90    def ident(self, node):
91        return self.ctx[node.name]
92
93    def strConst(self, node):
94        return node.data
95
96    def numConst(self, node):
97        return node.data
98
99    def __getValue(self, node) -> Node:
100        Map = {
101                parser.Identifier: self.ident,
102                parser.StrConst: self.strConst,
103                parser.NumConst: self.numConst,
104                parser.BinOp: self.binOp,
105                parser.ConditionalStatement: self.conditional,
106                parser.IterativeStatement: self.iterative,
107                parser.InclusionStatement: self.inclusion
108        }
109
110        if node.__class__ not in Map.keys():
111            raise TypeError(f"Can't retrieve value from {node}")
112
113        return Map[node.__class__](node)
114
115    def binOp(self, node) -> Node:
116        left = self.__getValue(node.left)
117        right = self.__getValue(node.right)
118
119        if node.op in [
120                "*", "/", "//", "%", "+", "-", "<<", ">>", 
121                "<", ">", "<=", ">=", "==", "!=", "&", "|"]:
122            return eval("l" + node.op + "r", {"l": left, "r": right})
123
124        if node.op == "&&":
125            return left and right
126        
127        if node.op == "||":
128            return left or right
129
130        raise TypeError(f"Invalid operator {node.op}")
131
132