stempl

Simple template language written in python

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


0from time import sleep
1
2class Token:
3    def __init__(self, text):
4        self.text = text
5        self.valid = True
6
7
8
9class Operator(Token):
10    OPERATORS = sorted([
11        "+",  "-",  "*",  "/",  "//",  "%",
12        "==", "!=", "&", "|", "^", "&&", "||"
13        "(", ")", "{{", "}}", ","
14    ], key=len, reverse=True)
15
16    def __init__(self, text):
17        self.valid = False
18        for o in self.OPERATORS:
19            if text.startswith(o):
20                self.text = o
21                self.valid = True
22                return
23
24
25class Keyword(Token):
26    KEYWORDS = [
27        "if", "elif", "else",
28        "for", "in",
29        "paste", "raw",
30        "with", "end",
31        "global",
32    ]
33
34    def __init__(self, text):
35        self.valid = False
36        for k in self.KEYWORDS:
37            if not text.startswith(k):
38                continue
39
40            if len(text) > len(k) and text[len(k)].isalnum():
41                continue
42
43            self.text = k
44            self.valid = True
45            return
46
47
48class Identifier(Token):
49    def __init__(self, text):
50        if not text[0].isalpha():
51            self.valid = False
52            return
53
54        ptr = 0
55        while ptr < len(text) and text[ptr].isalnum():
56            ptr += 1
57
58        self.text = text[:ptr]
59        if Operator(self.text).valid or Keyword(self.text).valid:
60            self.valid = False
61            return
62
63        self.valid = True
64
65class StrConst(Token):
66    def __init__(self, text):
67        self.valid = False
68
69        if text[0] != '"':
70            return
71
72        i = 1
73        flag = False
74        while i < len(text):
75            if text[i] == '\\':
76                flag = not flag
77            elif text[i] == '"' and flag == False:
78                break
79            else:
80                flag = False
81
82            i += 1
83
84        if i == len(text):
85            return
86
87        self.valid = True
88        self.text = text[:i+1]
89
90
91class NumConst(Token):
92    def __init__(self, text):
93        self.valid = False
94
95        is_float = False
96        i = 0
97        while i < len(text):
98            if text[i] == '.':
99                if is_float:
100                    return
101                is_float = True
102            elif not text[i].isdigit():
103                break
104
105            i += 1
106
107        if i == 0 or text[:i] == ".":
108            return
109
110        self.valid = True
111        self.text = text[:i]
112
113class Raw(Token):
114    def __init__(self, text):
115        u = text.find("{{")
116        
117        self.valid = True
118        if u != -1:
119            self.text = text[:u]
120        else:
121            self.text = text
122
123def expr(text):
124    tok = []
125    while len(text):
126        if text[0].isspace():
127            text = text[1:]
128            continue
129
130        for C in (Operator, Keyword, Identifier, NumConst, StrConst):
131            t = C(text)
132            if not t.valid:
133                continue
134
135            tok.append(t)
136            text = text[len(t.text):]
137            break
138        else:
139            raise ValueError("Unrecognised token :(")
140
141        if isinstance(tok[-1], Operator) and tok[-1].text == "}}":
142            break
143    return tok, text
144
145def lex(text):
146    tok = []
147    while len(text):
148        r = Raw(text)
149        text = text[len(r.text):]
150        if len(r.text):
151            tok.append(r)
152
153        e, text = expr(text)
154        tok.extend(e)
155    return tok
156
157