mapres 3.dev21__py3-none-any.whl → 3.dev23__py3-none-any.whl

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/parser.py CHANGED
@@ -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"Expected {type_}, got {tok}")
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.parse_colon_token()
57
+ return self.parse_colon()
63
58
 
64
59
  if tok.type is TokenType.BRACE_OPEN:
65
- return self.parse_brace_token()
60
+ return self.parse_brace()
66
61
 
67
62
  if tok.type is TokenType.DOLLAR_OPEN:
68
- return self.parse_dollar_token()
63
+ return self.parse_dollar()
69
64
 
70
65
  if tok.type is TokenType.ANGLE_OPEN:
71
- return self.parse_angle_token()
66
+ return self.parse_angle()
72
67
 
73
68
  if tok.type is TokenType.PIPE_OPEN:
74
- return self.parse_pipe_token()
69
+ return self.parse_pipe()
75
70
 
76
71
  if tok.type is TokenType.PERCENT_OPEN:
77
- return self.parse_percent_token()
72
+ return self.parse_percent()
78
73
 
79
- raise ParserError(f"Unexpected token {tok}")
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() # consume '('
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
- # --- syntax-bound token handlers ---
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, "colons")
92
+ return self._parse_ident_or_call(TokenType.COLON_CLOSE, 'colons')
106
93
 
107
- def parse_brace_token(self) -> Node:
94
+ def parse_brace(self) -> Node:
108
95
  self.expect(TokenType.BRACE_OPEN)
109
- return self._parse_ident_or_call(TokenType.BRACE_CLOSE, "braces")
96
+ return self._parse_ident_or_call(TokenType.BRACE_CLOSE, 'braces')
110
97
 
111
- def parse_dollar_token(self) -> Node:
98
+ def parse_dollar(self) -> Node:
112
99
  self.expect(TokenType.DOLLAR_OPEN)
113
- return self._parse_ident_or_call(TokenType.DOLLAR_CLOSE, "dollars")
100
+ return self._parse_ident_or_call(TokenType.DOLLAR_CLOSE, 'dollars')
114
101
 
115
- def parse_angle_token(self) -> Node:
102
+ def parse_angle(self) -> Node:
116
103
  self.expect(TokenType.ANGLE_OPEN)
117
- return self._parse_ident_or_call(TokenType.ANGLE_CLOSE, "angles")
104
+ return self._parse_ident_or_call(TokenType.ANGLE_CLOSE, 'angles')
118
105
 
119
- def parse_pipe_token(self) -> Node:
106
+ def parse_pipe(self) -> Node:
120
107
  self.expect(TokenType.PIPE_OPEN)
121
- return self._parse_ident_or_call(TokenType.PIPE_CLOSE, "pipes")
108
+ return self._parse_ident_or_call(TokenType.PIPE_CLOSE, 'pipes')
122
109
 
123
- def parse_percent_token(self) -> Node:
110
+ def parse_percent(self) -> Node:
124
111
  self.expect(TokenType.PERCENT_OPEN)
125
- return self._parse_ident_or_call(TokenType.PERCENT_CLOSE, "percents")
112
+ return self._parse_ident_or_call(TokenType.PERCENT_CLOSE, 'percents')
mapres/tokenizer.py CHANGED
@@ -33,37 +33,29 @@ class Token:
33
33
 
34
34
 
35
35
  class Tokenizer:
36
- def __init__(self, text):
36
+ def __init__(self, text: str):
37
37
  self.text = text
38
38
  self.i = 0
39
39
  self.n = len(text)
40
- self.tokens = []
40
+ self.tokens: list[Token] = []
41
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):
42
+ def peek(self, k: int = 0) -> str:
51
43
  idx = self.i + k
52
44
  if idx >= self.n:
53
45
  return ''
54
46
  return self.text[idx]
55
47
 
56
- def advance(self, k=1):
48
+ def advance(self, k: int = 1) -> None:
57
49
  self.i += k
58
50
 
59
- def match(self, s):
51
+ def match(self, s: str) -> bool:
60
52
  return self.text.startswith(s, self.i)
61
53
 
62
- def emit(self, type_, value=None):
54
+ def emit(self, type_, value=None) -> None:
63
55
  self.tokens.append(Token(type_, value, self.i))
64
56
 
65
- def tokenize(self):
66
- buf = []
57
+ def tokenize(self) -> list[Token]:
58
+ buf: list[str] = []
67
59
 
68
60
  def flush_text():
69
61
  if buf:
@@ -95,93 +87,126 @@ class Tokenizer:
95
87
  if self.match(r'\)'):
96
88
  buf.append(')'); self.advance(2); continue
97
89
 
98
- # --- paired delimiters ---
99
-
100
- # braces {{ }}
90
+ # --- {{IDENT}} ---
101
91
  if self.match('{{'):
102
92
  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)
93
+ start = self.i
110
94
  self.advance(2)
111
- continue
112
-
113
- # angles < >
114
- if self.match('<'):
115
- flush_text()
116
- self.emit(TokenType.ANGLE_OPEN)
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())
117
104
  self.advance()
118
105
  continue
119
106
 
120
- if self.match('>'):
107
+ # --- ${IDENT} ---
108
+ if self.match('${'):
121
109
  flush_text()
122
- self.emit(TokenType.ANGLE_CLOSE)
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())
123
121
  self.advance()
124
122
  continue
125
123
 
126
- # --- mixed delimiters (dollars) ---
127
-
128
- if self.match('${'):
124
+ # --- $(IDENT) ---
125
+ if self.match('$('):
129
126
  flush_text()
130
- self.emit(TokenType.DOLLAR_OPEN)
131
- self.in_dollar = True
127
+ start = self.i
132
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()
133
139
  continue
134
140
 
135
- if ch == '}':
141
+ # --- <IDENT> ---
142
+ if ch == '<':
136
143
  flush_text()
137
- if self.in_dollar:
138
- self.emit(TokenType.DOLLAR_CLOSE)
139
- self.in_dollar = False
140
- else:
141
- buf.append('}')
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())
142
155
  self.advance()
143
156
  continue
144
157
 
145
- # --- toggle delimiters ---
146
-
147
- # pipes |...|
148
- if self.match('|'):
158
+ # --- |IDENT| ---
159
+ if ch == '|':
149
160
  flush_text()
150
- if not self.in_pipe:
161
+ start = self.i
162
+ self.advance()
163
+ ident = self._read_ident()
164
+ if ident is not None and self.peek() == '|':
151
165
  self.emit(TokenType.PIPE_OPEN)
152
- self.in_pipe = True
153
- else:
166
+ self.emit(TokenType.IDENT, ident)
167
+ self.advance()
154
168
  self.emit(TokenType.PIPE_CLOSE)
155
- self.in_pipe = False
169
+ continue
170
+ self.i = start
171
+ buf.append(self.peek())
156
172
  self.advance()
157
173
  continue
158
174
 
159
- # percents %...%
160
- if self.match('%'):
175
+ # --- %IDENT% ---
176
+ if ch == '%':
161
177
  flush_text()
162
- if not self.in_percent:
178
+ start = self.i
179
+ self.advance()
180
+ ident = self._read_ident()
181
+ if ident is not None and self.peek() == '%':
163
182
  self.emit(TokenType.PERCENT_OPEN)
164
- self.in_percent = True
165
- else:
183
+ self.emit(TokenType.IDENT, ident)
184
+ self.advance()
166
185
  self.emit(TokenType.PERCENT_CLOSE)
167
- self.in_percent = False
186
+ continue
187
+ self.i = start
188
+ buf.append(self.peek())
168
189
  self.advance()
169
190
  continue
170
191
 
171
- # colons :...:
172
- if self.match(':'):
192
+ # --- :IDENT: ---
193
+ if ch == ':':
173
194
  flush_text()
174
- if not self.in_colon:
195
+ start = self.i
196
+ self.advance()
197
+ ident = self._read_ident()
198
+ if ident is not None and self.peek() == ':':
175
199
  self.emit(TokenType.COLON_OPEN)
176
- self.in_colon = True
177
- else:
200
+ self.emit(TokenType.IDENT, ident)
201
+ self.advance()
178
202
  self.emit(TokenType.COLON_CLOSE)
179
- self.in_colon = False
203
+ continue
204
+ self.i = start
205
+ buf.append(self.peek())
180
206
  self.advance()
181
207
  continue
182
208
 
183
209
  # --- parens ---
184
-
185
210
  if ch == '(':
186
211
  flush_text()
187
212
  self.emit(TokenType.LPAREN)
@@ -194,39 +219,30 @@ class Tokenizer:
194
219
  self.advance()
195
220
  continue
196
221
 
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
-
222
+ # fallback TEXT
228
223
  buf.append(ch)
229
224
  self.advance()
230
225
 
231
226
  flush_text()
232
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapres
3
- Version: 3.dev21
3
+ Version: 3.dev23
4
4
  Summary: A powerfull mapping utility
5
5
  Author: iFamished
6
6
  License: MIT
@@ -5,14 +5,14 @@ mapres/datamap.py,sha256=R_5G_mE2OZpZ9YVL4tsNXAGS_SkQ6LKlx6Bz2Pr652Y,5021
5
5
  mapres/evaluator.py,sha256=KNZ9aygOClM0kgnOf3anigsupLF175EJ07z_17ubZzU,3441
6
6
  mapres/exceptions.py,sha256=D2XKnrMkZDyUXQt2PmaKbj08FObWU00yXKGHwNpjvww,222
7
7
  mapres/layers.py,sha256=sFc1eUOBg2BoB2cBXIleq8RMYcg5GZpUoCnGh-uKN6U,1380
8
- mapres/parser.py,sha256=8XLCzYosvh81bN-Jddc1AM4w0X7bsHnBzmA8sq5_72w,4018
8
+ mapres/parser.py,sha256=q3PgyvXJn9c-EJJYyibw7sHJ0Y9soirgB10OyTKaT9s,3657
9
9
  mapres/resolver.py,sha256=kQexTdBnPy70Azh_U6--hwUtnIJjoKcuaJ2KlBaG3Rg,3889
10
- mapres/tokenizer.py,sha256=js0S1s8MkltdQoH5V1cLjdY0fFqZoGqmtP_OhEJEM6M,6539
10
+ mapres/tokenizer.py,sha256=O28ZTClzC8CkHlnjMHlyGsSLXwMRKIho-JZLF-JDxj0,7795
11
11
  mapres/maps/__init__.py,sha256=lIRRfem1Fw4iynuwUQ7BY5qrC4fbJKxqLivao2_Dtm4,239
12
12
  mapres/maps/color.py,sha256=noRTGFNo9xLl5YEhX0tttV6i_oDAiptXmRVnkr5EzwM,1348
13
13
  mapres/maps/time.py,sha256=TyncycYoIGVZWps32qWlFy2a2hTayvZWVHu9uh2nid8,2050
14
- mapres-3.dev21.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
15
- mapres-3.dev21.dist-info/METADATA,sha256=SrQXRywgQfa_pKRb7Egsdagr1pNwn2F9S3bBdpTWdVQ,1476
16
- mapres-3.dev21.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
17
- mapres-3.dev21.dist-info/top_level.txt,sha256=5LMVQqJdtyjxfzv7wJBOZ-360m0Yc79w_-Y7s3n6cTw,7
18
- mapres-3.dev21.dist-info/RECORD,,
14
+ mapres-3.dev23.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
15
+ mapres-3.dev23.dist-info/METADATA,sha256=o85qNh64MYe1YMGW1HKCDgTR6qSMwriKlihRTOSUY-8,1476
16
+ mapres-3.dev23.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
17
+ mapres-3.dev23.dist-info/top_level.txt,sha256=5LMVQqJdtyjxfzv7wJBOZ-360m0Yc79w_-Y7s3n6cTw,7
18
+ mapres-3.dev23.dist-info/RECORD,,