mapres 3.dev21__tar.gz → 3.dev23__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mapres-3.dev21/src/mapres.egg-info → mapres-3.dev23}/PKG-INFO +1 -1
- {mapres-3.dev21 → mapres-3.dev23}/pyproject.toml +1 -1
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/parser.py +21 -34
- mapres-3.dev23/src/mapres/tokenizer.py +248 -0
- {mapres-3.dev21 → mapres-3.dev23/src/mapres.egg-info}/PKG-INFO +1 -1
- mapres-3.dev21/src/mapres/tokenizer.py +0 -232
- {mapres-3.dev21 → mapres-3.dev23}/LICENSE +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/README.md +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/setup.cfg +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/__init__.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/ast.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/cache.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/datamap.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/evaluator.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/exceptions.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/layers.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/maps/__init__.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/maps/color.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/maps/time.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres/resolver.py +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres.egg-info/SOURCES.txt +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres.egg-info/dependency_links.txt +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres.egg-info/requires.txt +0 -0
- {mapres-3.dev21 → mapres-3.dev23}/src/mapres.egg-info/top_level.txt +0 -0
|
@@ -22,11 +22,10 @@ class Parser:
|
|
|
22
22
|
def expect(self, type_: TokenType) -> Token:
|
|
23
23
|
tok = self.peek()
|
|
24
24
|
if tok is None or tok.type != type_:
|
|
25
|
-
raise ParserError(f
|
|
25
|
+
raise ParserError(f'Expected {type_}, got {tok}')
|
|
26
26
|
self.advance()
|
|
27
27
|
return tok
|
|
28
28
|
|
|
29
|
-
# top-level template parse
|
|
30
29
|
def parse(self) -> TemplateNode:
|
|
31
30
|
children: list[Node] = []
|
|
32
31
|
while self.peek() is not None:
|
|
@@ -38,88 +37,76 @@ class Parser:
|
|
|
38
37
|
children.append(self.parse_token())
|
|
39
38
|
return TemplateNode(children)
|
|
40
39
|
|
|
41
|
-
# nested template parse (for IDENT(...))
|
|
42
40
|
def parse_until(self, stop_types: tuple[TokenType, ...]) -> TemplateNode:
|
|
43
41
|
children: list[Node] = []
|
|
44
42
|
while True:
|
|
45
43
|
tok = self.peek()
|
|
46
44
|
if tok is None or tok.type in stop_types:
|
|
47
45
|
break
|
|
48
|
-
|
|
49
46
|
if tok.type is TokenType.TEXT:
|
|
50
47
|
children.append(TextNode(tok.value))
|
|
51
48
|
self.advance()
|
|
52
49
|
else:
|
|
53
50
|
children.append(self.parse_token())
|
|
54
|
-
|
|
55
51
|
return TemplateNode(children)
|
|
56
52
|
|
|
57
|
-
# dispatch by opening delimiter
|
|
58
53
|
def parse_token(self) -> Node:
|
|
59
54
|
tok = self.peek()
|
|
60
55
|
|
|
61
56
|
if tok.type is TokenType.COLON_OPEN:
|
|
62
|
-
return self.
|
|
57
|
+
return self.parse_colon()
|
|
63
58
|
|
|
64
59
|
if tok.type is TokenType.BRACE_OPEN:
|
|
65
|
-
return self.
|
|
60
|
+
return self.parse_brace()
|
|
66
61
|
|
|
67
62
|
if tok.type is TokenType.DOLLAR_OPEN:
|
|
68
|
-
return self.
|
|
63
|
+
return self.parse_dollar()
|
|
69
64
|
|
|
70
65
|
if tok.type is TokenType.ANGLE_OPEN:
|
|
71
|
-
return self.
|
|
66
|
+
return self.parse_angle()
|
|
72
67
|
|
|
73
68
|
if tok.type is TokenType.PIPE_OPEN:
|
|
74
|
-
return self.
|
|
69
|
+
return self.parse_pipe()
|
|
75
70
|
|
|
76
71
|
if tok.type is TokenType.PERCENT_OPEN:
|
|
77
|
-
return self.
|
|
72
|
+
return self.parse_percent()
|
|
78
73
|
|
|
79
|
-
raise ParserError(f
|
|
74
|
+
raise ParserError(f'Unexpected token {tok}')
|
|
80
75
|
|
|
81
|
-
# core pattern: IDENT or IDENT(arg)
|
|
82
76
|
def _parse_ident_or_call(self, closing_type: TokenType, syntax_name: str) -> Node:
|
|
83
77
|
ident_tok = self.expect(TokenType.IDENT)
|
|
84
78
|
name = ident_tok.value
|
|
85
79
|
|
|
86
|
-
# IDENT(arg)
|
|
87
80
|
if self.peek() and self.peek().type is TokenType.LPAREN:
|
|
88
|
-
self.advance()
|
|
89
|
-
|
|
90
|
-
# parse only until RPAREN
|
|
81
|
+
self.advance()
|
|
91
82
|
arg = self.parse_until((TokenType.RPAREN,))
|
|
92
|
-
|
|
93
83
|
self.expect(TokenType.RPAREN)
|
|
94
84
|
self.expect(closing_type)
|
|
95
85
|
return CallNode(name=name, arg=arg, syntax=syntax_name)
|
|
96
86
|
|
|
97
|
-
# IDENT
|
|
98
87
|
self.expect(closing_type)
|
|
99
88
|
return IdentNode(name=name, syntax=syntax_name)
|
|
100
89
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
def parse_colon_token(self) -> Node:
|
|
90
|
+
def parse_colon(self) -> Node:
|
|
104
91
|
self.expect(TokenType.COLON_OPEN)
|
|
105
|
-
return self._parse_ident_or_call(TokenType.COLON_CLOSE,
|
|
92
|
+
return self._parse_ident_or_call(TokenType.COLON_CLOSE, 'colons')
|
|
106
93
|
|
|
107
|
-
def
|
|
94
|
+
def parse_brace(self) -> Node:
|
|
108
95
|
self.expect(TokenType.BRACE_OPEN)
|
|
109
|
-
return self._parse_ident_or_call(TokenType.BRACE_CLOSE,
|
|
96
|
+
return self._parse_ident_or_call(TokenType.BRACE_CLOSE, 'braces')
|
|
110
97
|
|
|
111
|
-
def
|
|
98
|
+
def parse_dollar(self) -> Node:
|
|
112
99
|
self.expect(TokenType.DOLLAR_OPEN)
|
|
113
|
-
return self._parse_ident_or_call(TokenType.DOLLAR_CLOSE,
|
|
100
|
+
return self._parse_ident_or_call(TokenType.DOLLAR_CLOSE, 'dollars')
|
|
114
101
|
|
|
115
|
-
def
|
|
102
|
+
def parse_angle(self) -> Node:
|
|
116
103
|
self.expect(TokenType.ANGLE_OPEN)
|
|
117
|
-
return self._parse_ident_or_call(TokenType.ANGLE_CLOSE,
|
|
104
|
+
return self._parse_ident_or_call(TokenType.ANGLE_CLOSE, 'angles')
|
|
118
105
|
|
|
119
|
-
def
|
|
106
|
+
def parse_pipe(self) -> Node:
|
|
120
107
|
self.expect(TokenType.PIPE_OPEN)
|
|
121
|
-
return self._parse_ident_or_call(TokenType.PIPE_CLOSE,
|
|
108
|
+
return self._parse_ident_or_call(TokenType.PIPE_CLOSE, 'pipes')
|
|
122
109
|
|
|
123
|
-
def
|
|
110
|
+
def parse_percent(self) -> Node:
|
|
124
111
|
self.expect(TokenType.PERCENT_OPEN)
|
|
125
|
-
return self._parse_ident_or_call(TokenType.PERCENT_CLOSE,
|
|
112
|
+
return self._parse_ident_or_call(TokenType.PERCENT_CLOSE, 'percents')
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
from enum import Enum, auto
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class TokenType(Enum):
|
|
5
|
+
TEXT = auto()
|
|
6
|
+
IDENT = auto()
|
|
7
|
+
COLON_OPEN = auto()
|
|
8
|
+
COLON_CLOSE = auto()
|
|
9
|
+
BRACE_OPEN = auto()
|
|
10
|
+
BRACE_CLOSE = auto()
|
|
11
|
+
DOLLAR_OPEN = auto()
|
|
12
|
+
DOLLAR_CLOSE = auto()
|
|
13
|
+
ANGLE_OPEN = auto()
|
|
14
|
+
ANGLE_CLOSE = auto()
|
|
15
|
+
PIPE_OPEN = auto()
|
|
16
|
+
PIPE_CLOSE = auto()
|
|
17
|
+
PERCENT_OPEN = auto()
|
|
18
|
+
PERCENT_CLOSE = auto()
|
|
19
|
+
LPAREN = auto()
|
|
20
|
+
RPAREN = auto()
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Token:
|
|
24
|
+
def __init__(self, type_, value=None, pos=0):
|
|
25
|
+
self.type = type_
|
|
26
|
+
self.value = value
|
|
27
|
+
self.pos = pos
|
|
28
|
+
|
|
29
|
+
def __repr__(self):
|
|
30
|
+
if self.value is not None:
|
|
31
|
+
return f'Token({self.type}, {self.value!r}, pos={self.pos})'
|
|
32
|
+
return f'Token({self.type}, pos={self.pos})'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class Tokenizer:
|
|
36
|
+
def __init__(self, text: str):
|
|
37
|
+
self.text = text
|
|
38
|
+
self.i = 0
|
|
39
|
+
self.n = len(text)
|
|
40
|
+
self.tokens: list[Token] = []
|
|
41
|
+
|
|
42
|
+
def peek(self, k: int = 0) -> str:
|
|
43
|
+
idx = self.i + k
|
|
44
|
+
if idx >= self.n:
|
|
45
|
+
return ''
|
|
46
|
+
return self.text[idx]
|
|
47
|
+
|
|
48
|
+
def advance(self, k: int = 1) -> None:
|
|
49
|
+
self.i += k
|
|
50
|
+
|
|
51
|
+
def match(self, s: str) -> bool:
|
|
52
|
+
return self.text.startswith(s, self.i)
|
|
53
|
+
|
|
54
|
+
def emit(self, type_, value=None) -> None:
|
|
55
|
+
self.tokens.append(Token(type_, value, self.i))
|
|
56
|
+
|
|
57
|
+
def tokenize(self) -> list[Token]:
|
|
58
|
+
buf: list[str] = []
|
|
59
|
+
|
|
60
|
+
def flush_text():
|
|
61
|
+
if buf:
|
|
62
|
+
self.emit(TokenType.TEXT, ''.join(buf))
|
|
63
|
+
buf.clear()
|
|
64
|
+
|
|
65
|
+
while self.i < self.n:
|
|
66
|
+
ch = self.peek()
|
|
67
|
+
|
|
68
|
+
# escapes
|
|
69
|
+
if self.match(r'\:'):
|
|
70
|
+
buf.append(':'); self.advance(2); continue
|
|
71
|
+
if self.match(r'\<'):
|
|
72
|
+
buf.append('<'); self.advance(2); continue
|
|
73
|
+
if self.match(r'\>'):
|
|
74
|
+
buf.append('>'); self.advance(2); continue
|
|
75
|
+
if self.match(r'\%'):
|
|
76
|
+
buf.append('%'); self.advance(2); continue
|
|
77
|
+
if self.match(r'\$'):
|
|
78
|
+
buf.append('$'); self.advance(2); continue
|
|
79
|
+
if self.match(r'\{'):
|
|
80
|
+
buf.append('{'); self.advance(2); continue
|
|
81
|
+
if self.match(r'\}'):
|
|
82
|
+
buf.append('}'); self.advance(2); continue
|
|
83
|
+
if self.match(r'\|'):
|
|
84
|
+
buf.append('|'); self.advance(2); continue
|
|
85
|
+
if self.match(r'\('):
|
|
86
|
+
buf.append('('); self.advance(2); continue
|
|
87
|
+
if self.match(r'\)'):
|
|
88
|
+
buf.append(')'); self.advance(2); continue
|
|
89
|
+
|
|
90
|
+
# --- {{IDENT}} ---
|
|
91
|
+
if self.match('{{'):
|
|
92
|
+
flush_text()
|
|
93
|
+
start = self.i
|
|
94
|
+
self.advance(2)
|
|
95
|
+
ident = self._read_ident()
|
|
96
|
+
if ident is not None and self.match('}}'):
|
|
97
|
+
self.emit(TokenType.BRACE_OPEN)
|
|
98
|
+
self.emit(TokenType.IDENT, ident)
|
|
99
|
+
self.advance(2)
|
|
100
|
+
self.emit(TokenType.BRACE_CLOSE)
|
|
101
|
+
continue
|
|
102
|
+
self.i = start
|
|
103
|
+
buf.append(self.peek())
|
|
104
|
+
self.advance()
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
# --- ${IDENT} ---
|
|
108
|
+
if self.match('${'):
|
|
109
|
+
flush_text()
|
|
110
|
+
start = self.i
|
|
111
|
+
self.advance(2)
|
|
112
|
+
ident = self._read_ident()
|
|
113
|
+
if ident is not None and self.peek() == '}':
|
|
114
|
+
self.emit(TokenType.DOLLAR_OPEN)
|
|
115
|
+
self.emit(TokenType.IDENT, ident)
|
|
116
|
+
self.advance()
|
|
117
|
+
self.emit(TokenType.DOLLAR_CLOSE)
|
|
118
|
+
continue
|
|
119
|
+
self.i = start
|
|
120
|
+
buf.append(self.peek())
|
|
121
|
+
self.advance()
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
# --- $(IDENT) ---
|
|
125
|
+
if self.match('$('):
|
|
126
|
+
flush_text()
|
|
127
|
+
start = self.i
|
|
128
|
+
self.advance(2)
|
|
129
|
+
ident = self._read_ident()
|
|
130
|
+
if ident is not None and self.peek() == ')':
|
|
131
|
+
self.emit(TokenType.DOLLAR_OPEN)
|
|
132
|
+
self.emit(TokenType.IDENT, ident)
|
|
133
|
+
self.advance()
|
|
134
|
+
self.emit(TokenType.DOLLAR_CLOSE)
|
|
135
|
+
continue
|
|
136
|
+
self.i = start
|
|
137
|
+
buf.append(self.peek())
|
|
138
|
+
self.advance()
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
# --- <IDENT> ---
|
|
142
|
+
if ch == '<':
|
|
143
|
+
flush_text()
|
|
144
|
+
start = self.i
|
|
145
|
+
self.advance()
|
|
146
|
+
ident = self._read_ident()
|
|
147
|
+
if ident is not None and self.peek() == '>':
|
|
148
|
+
self.emit(TokenType.ANGLE_OPEN)
|
|
149
|
+
self.emit(TokenType.IDENT, ident)
|
|
150
|
+
self.advance()
|
|
151
|
+
self.emit(TokenType.ANGLE_CLOSE)
|
|
152
|
+
continue
|
|
153
|
+
self.i = start
|
|
154
|
+
buf.append(self.peek())
|
|
155
|
+
self.advance()
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
# --- |IDENT| ---
|
|
159
|
+
if ch == '|':
|
|
160
|
+
flush_text()
|
|
161
|
+
start = self.i
|
|
162
|
+
self.advance()
|
|
163
|
+
ident = self._read_ident()
|
|
164
|
+
if ident is not None and self.peek() == '|':
|
|
165
|
+
self.emit(TokenType.PIPE_OPEN)
|
|
166
|
+
self.emit(TokenType.IDENT, ident)
|
|
167
|
+
self.advance()
|
|
168
|
+
self.emit(TokenType.PIPE_CLOSE)
|
|
169
|
+
continue
|
|
170
|
+
self.i = start
|
|
171
|
+
buf.append(self.peek())
|
|
172
|
+
self.advance()
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
# --- %IDENT% ---
|
|
176
|
+
if ch == '%':
|
|
177
|
+
flush_text()
|
|
178
|
+
start = self.i
|
|
179
|
+
self.advance()
|
|
180
|
+
ident = self._read_ident()
|
|
181
|
+
if ident is not None and self.peek() == '%':
|
|
182
|
+
self.emit(TokenType.PERCENT_OPEN)
|
|
183
|
+
self.emit(TokenType.IDENT, ident)
|
|
184
|
+
self.advance()
|
|
185
|
+
self.emit(TokenType.PERCENT_CLOSE)
|
|
186
|
+
continue
|
|
187
|
+
self.i = start
|
|
188
|
+
buf.append(self.peek())
|
|
189
|
+
self.advance()
|
|
190
|
+
continue
|
|
191
|
+
|
|
192
|
+
# --- :IDENT: ---
|
|
193
|
+
if ch == ':':
|
|
194
|
+
flush_text()
|
|
195
|
+
start = self.i
|
|
196
|
+
self.advance()
|
|
197
|
+
ident = self._read_ident()
|
|
198
|
+
if ident is not None and self.peek() == ':':
|
|
199
|
+
self.emit(TokenType.COLON_OPEN)
|
|
200
|
+
self.emit(TokenType.IDENT, ident)
|
|
201
|
+
self.advance()
|
|
202
|
+
self.emit(TokenType.COLON_CLOSE)
|
|
203
|
+
continue
|
|
204
|
+
self.i = start
|
|
205
|
+
buf.append(self.peek())
|
|
206
|
+
self.advance()
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
# --- parens ---
|
|
210
|
+
if ch == '(':
|
|
211
|
+
flush_text()
|
|
212
|
+
self.emit(TokenType.LPAREN)
|
|
213
|
+
self.advance()
|
|
214
|
+
continue
|
|
215
|
+
|
|
216
|
+
if ch == ')':
|
|
217
|
+
flush_text()
|
|
218
|
+
self.emit(TokenType.RPAREN)
|
|
219
|
+
self.advance()
|
|
220
|
+
continue
|
|
221
|
+
|
|
222
|
+
# fallback TEXT
|
|
223
|
+
buf.append(ch)
|
|
224
|
+
self.advance()
|
|
225
|
+
|
|
226
|
+
flush_text()
|
|
227
|
+
return self.tokens
|
|
228
|
+
|
|
229
|
+
def _read_ident(self) -> str | None:
|
|
230
|
+
start = self.i
|
|
231
|
+
ch = self.peek()
|
|
232
|
+
if not ch or not ch.isalnum():
|
|
233
|
+
return None
|
|
234
|
+
|
|
235
|
+
self.advance()
|
|
236
|
+
while True:
|
|
237
|
+
nxt = self.peek()
|
|
238
|
+
if not nxt:
|
|
239
|
+
break
|
|
240
|
+
if nxt.isalnum() or nxt in '._':
|
|
241
|
+
self.advance()
|
|
242
|
+
else:
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
ident = self.text[start:self.i]
|
|
246
|
+
if ident.startswith('_') or ident.endswith('_') or '-' in ident:
|
|
247
|
+
return None
|
|
248
|
+
return ident
|
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
from enum import Enum, auto
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class TokenType(Enum):
|
|
5
|
-
TEXT = auto()
|
|
6
|
-
IDENT = auto()
|
|
7
|
-
COLON_OPEN = auto()
|
|
8
|
-
COLON_CLOSE = auto()
|
|
9
|
-
BRACE_OPEN = auto()
|
|
10
|
-
BRACE_CLOSE = auto()
|
|
11
|
-
DOLLAR_OPEN = auto()
|
|
12
|
-
DOLLAR_CLOSE = auto()
|
|
13
|
-
ANGLE_OPEN = auto()
|
|
14
|
-
ANGLE_CLOSE = auto()
|
|
15
|
-
PIPE_OPEN = auto()
|
|
16
|
-
PIPE_CLOSE = auto()
|
|
17
|
-
PERCENT_OPEN = auto()
|
|
18
|
-
PERCENT_CLOSE = auto()
|
|
19
|
-
LPAREN = auto()
|
|
20
|
-
RPAREN = auto()
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
class Token:
|
|
24
|
-
def __init__(self, type_, value=None, pos=0):
|
|
25
|
-
self.type = type_
|
|
26
|
-
self.value = value
|
|
27
|
-
self.pos = pos
|
|
28
|
-
|
|
29
|
-
def __repr__(self):
|
|
30
|
-
if self.value is not None:
|
|
31
|
-
return f'Token({self.type}, {self.value!r}, pos={self.pos})'
|
|
32
|
-
return f'Token({self.type}, pos={self.pos})'
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class Tokenizer:
|
|
36
|
-
def __init__(self, text):
|
|
37
|
-
self.text = text
|
|
38
|
-
self.i = 0
|
|
39
|
-
self.n = len(text)
|
|
40
|
-
self.tokens = []
|
|
41
|
-
|
|
42
|
-
# toggle states
|
|
43
|
-
self.in_pipe = False
|
|
44
|
-
self.in_percent = False
|
|
45
|
-
self.in_colon = False
|
|
46
|
-
|
|
47
|
-
# mixed states
|
|
48
|
-
self.in_dollar = False
|
|
49
|
-
|
|
50
|
-
def peek(self, k=0):
|
|
51
|
-
idx = self.i + k
|
|
52
|
-
if idx >= self.n:
|
|
53
|
-
return ''
|
|
54
|
-
return self.text[idx]
|
|
55
|
-
|
|
56
|
-
def advance(self, k=1):
|
|
57
|
-
self.i += k
|
|
58
|
-
|
|
59
|
-
def match(self, s):
|
|
60
|
-
return self.text.startswith(s, self.i)
|
|
61
|
-
|
|
62
|
-
def emit(self, type_, value=None):
|
|
63
|
-
self.tokens.append(Token(type_, value, self.i))
|
|
64
|
-
|
|
65
|
-
def tokenize(self):
|
|
66
|
-
buf = []
|
|
67
|
-
|
|
68
|
-
def flush_text():
|
|
69
|
-
if buf:
|
|
70
|
-
self.emit(TokenType.TEXT, ''.join(buf))
|
|
71
|
-
buf.clear()
|
|
72
|
-
|
|
73
|
-
while self.i < self.n:
|
|
74
|
-
ch = self.peek()
|
|
75
|
-
|
|
76
|
-
# escapes
|
|
77
|
-
if self.match(r'\:'):
|
|
78
|
-
buf.append(':'); self.advance(2); continue
|
|
79
|
-
if self.match(r'\<'):
|
|
80
|
-
buf.append('<'); self.advance(2); continue
|
|
81
|
-
if self.match(r'\>'):
|
|
82
|
-
buf.append('>'); self.advance(2); continue
|
|
83
|
-
if self.match(r'\%'):
|
|
84
|
-
buf.append('%'); self.advance(2); continue
|
|
85
|
-
if self.match(r'\$'):
|
|
86
|
-
buf.append('$'); self.advance(2); continue
|
|
87
|
-
if self.match(r'\{'):
|
|
88
|
-
buf.append('{'); self.advance(2); continue
|
|
89
|
-
if self.match(r'\}'):
|
|
90
|
-
buf.append('}'); self.advance(2); continue
|
|
91
|
-
if self.match(r'\|'):
|
|
92
|
-
buf.append('|'); self.advance(2); continue
|
|
93
|
-
if self.match(r'\('):
|
|
94
|
-
buf.append('('); self.advance(2); continue
|
|
95
|
-
if self.match(r'\)'):
|
|
96
|
-
buf.append(')'); self.advance(2); continue
|
|
97
|
-
|
|
98
|
-
# --- paired delimiters ---
|
|
99
|
-
|
|
100
|
-
# braces {{ }}
|
|
101
|
-
if self.match('{{'):
|
|
102
|
-
flush_text()
|
|
103
|
-
self.emit(TokenType.BRACE_OPEN)
|
|
104
|
-
self.advance(2)
|
|
105
|
-
continue
|
|
106
|
-
|
|
107
|
-
if self.match('}}'):
|
|
108
|
-
flush_text()
|
|
109
|
-
self.emit(TokenType.BRACE_CLOSE)
|
|
110
|
-
self.advance(2)
|
|
111
|
-
continue
|
|
112
|
-
|
|
113
|
-
# angles < >
|
|
114
|
-
if self.match('<'):
|
|
115
|
-
flush_text()
|
|
116
|
-
self.emit(TokenType.ANGLE_OPEN)
|
|
117
|
-
self.advance()
|
|
118
|
-
continue
|
|
119
|
-
|
|
120
|
-
if self.match('>'):
|
|
121
|
-
flush_text()
|
|
122
|
-
self.emit(TokenType.ANGLE_CLOSE)
|
|
123
|
-
self.advance()
|
|
124
|
-
continue
|
|
125
|
-
|
|
126
|
-
# --- mixed delimiters (dollars) ---
|
|
127
|
-
|
|
128
|
-
if self.match('${'):
|
|
129
|
-
flush_text()
|
|
130
|
-
self.emit(TokenType.DOLLAR_OPEN)
|
|
131
|
-
self.in_dollar = True
|
|
132
|
-
self.advance(2)
|
|
133
|
-
continue
|
|
134
|
-
|
|
135
|
-
if ch == '}':
|
|
136
|
-
flush_text()
|
|
137
|
-
if self.in_dollar:
|
|
138
|
-
self.emit(TokenType.DOLLAR_CLOSE)
|
|
139
|
-
self.in_dollar = False
|
|
140
|
-
else:
|
|
141
|
-
buf.append('}')
|
|
142
|
-
self.advance()
|
|
143
|
-
continue
|
|
144
|
-
|
|
145
|
-
# --- toggle delimiters ---
|
|
146
|
-
|
|
147
|
-
# pipes |...|
|
|
148
|
-
if self.match('|'):
|
|
149
|
-
flush_text()
|
|
150
|
-
if not self.in_pipe:
|
|
151
|
-
self.emit(TokenType.PIPE_OPEN)
|
|
152
|
-
self.in_pipe = True
|
|
153
|
-
else:
|
|
154
|
-
self.emit(TokenType.PIPE_CLOSE)
|
|
155
|
-
self.in_pipe = False
|
|
156
|
-
self.advance()
|
|
157
|
-
continue
|
|
158
|
-
|
|
159
|
-
# percents %...%
|
|
160
|
-
if self.match('%'):
|
|
161
|
-
flush_text()
|
|
162
|
-
if not self.in_percent:
|
|
163
|
-
self.emit(TokenType.PERCENT_OPEN)
|
|
164
|
-
self.in_percent = True
|
|
165
|
-
else:
|
|
166
|
-
self.emit(TokenType.PERCENT_CLOSE)
|
|
167
|
-
self.in_percent = False
|
|
168
|
-
self.advance()
|
|
169
|
-
continue
|
|
170
|
-
|
|
171
|
-
# colons :...:
|
|
172
|
-
if self.match(':'):
|
|
173
|
-
flush_text()
|
|
174
|
-
if not self.in_colon:
|
|
175
|
-
self.emit(TokenType.COLON_OPEN)
|
|
176
|
-
self.in_colon = True
|
|
177
|
-
else:
|
|
178
|
-
self.emit(TokenType.COLON_CLOSE)
|
|
179
|
-
self.in_colon = False
|
|
180
|
-
self.advance()
|
|
181
|
-
continue
|
|
182
|
-
|
|
183
|
-
# --- parens ---
|
|
184
|
-
|
|
185
|
-
if ch == '(':
|
|
186
|
-
flush_text()
|
|
187
|
-
self.emit(TokenType.LPAREN)
|
|
188
|
-
self.advance()
|
|
189
|
-
continue
|
|
190
|
-
|
|
191
|
-
if ch == ')':
|
|
192
|
-
flush_text()
|
|
193
|
-
self.emit(TokenType.RPAREN)
|
|
194
|
-
self.advance()
|
|
195
|
-
continue
|
|
196
|
-
|
|
197
|
-
# --- IDENT (your rules) ---
|
|
198
|
-
|
|
199
|
-
if ch.isalnum():
|
|
200
|
-
flush_text()
|
|
201
|
-
start = self.i
|
|
202
|
-
|
|
203
|
-
# first char: [a-zA-Z0-9]
|
|
204
|
-
self.advance()
|
|
205
|
-
|
|
206
|
-
# middle chars: [a-zA-Z0-9._]*
|
|
207
|
-
while True:
|
|
208
|
-
nxt = self.peek()
|
|
209
|
-
if not nxt:
|
|
210
|
-
break
|
|
211
|
-
if nxt.isalnum() or nxt in '._':
|
|
212
|
-
self.advance()
|
|
213
|
-
else:
|
|
214
|
-
break
|
|
215
|
-
|
|
216
|
-
ident = self.text[start:self.i]
|
|
217
|
-
|
|
218
|
-
# enforce: no leading/trailing '_', no '-'
|
|
219
|
-
if ident.startswith('_') or ident.endswith('_') or '-' in ident:
|
|
220
|
-
# treat as TEXT instead
|
|
221
|
-
buf.append(ident)
|
|
222
|
-
else:
|
|
223
|
-
self.emit(TokenType.IDENT, ident)
|
|
224
|
-
continue
|
|
225
|
-
|
|
226
|
-
# --- fallback: TEXT ---
|
|
227
|
-
|
|
228
|
-
buf.append(ch)
|
|
229
|
-
self.advance()
|
|
230
|
-
|
|
231
|
-
flush_text()
|
|
232
|
-
return self.tokens
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|