mapres 3.dev21__py3-none-any.whl → 3.dev22__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,109 @@ 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)
117
- self.advance()
118
- continue
119
-
120
- if self.match('>'):
121
- flush_text()
122
- self.emit(TokenType.ANGLE_CLOSE)
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())
123
104
  self.advance()
124
105
  continue
125
106
 
126
- # --- mixed delimiters (dollars) ---
127
-
107
+ # --- ${IDENT} ---
128
108
  if self.match('${'):
129
109
  flush_text()
130
- self.emit(TokenType.DOLLAR_OPEN)
131
- self.in_dollar = True
110
+ start = self.i
132
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()
133
122
  continue
134
123
 
135
- if ch == '}':
124
+ # --- <IDENT> ---
125
+ if ch == '<':
136
126
  flush_text()
137
- if self.in_dollar:
138
- self.emit(TokenType.DOLLAR_CLOSE)
139
- self.in_dollar = False
140
- else:
141
- buf.append('}')
127
+ start = self.i
128
+ self.advance()
129
+ ident = self._read_ident()
130
+ if ident is not None and self.peek() == '>':
131
+ self.emit(TokenType.ANGLE_OPEN)
132
+ self.emit(TokenType.IDENT, ident)
133
+ self.advance()
134
+ self.emit(TokenType.ANGLE_CLOSE)
135
+ continue
136
+ self.i = start
137
+ buf.append(self.peek())
142
138
  self.advance()
143
139
  continue
144
140
 
145
- # --- toggle delimiters ---
146
-
147
- # pipes |...|
148
- if self.match('|'):
141
+ # --- |IDENT| ---
142
+ if ch == '|':
149
143
  flush_text()
150
- if not self.in_pipe:
144
+ start = self.i
145
+ self.advance()
146
+ ident = self._read_ident()
147
+ if ident is not None and self.peek() == '|':
151
148
  self.emit(TokenType.PIPE_OPEN)
152
- self.in_pipe = True
153
- else:
149
+ self.emit(TokenType.IDENT, ident)
150
+ self.advance()
154
151
  self.emit(TokenType.PIPE_CLOSE)
155
- self.in_pipe = False
152
+ continue
153
+ self.i = start
154
+ buf.append(self.peek())
156
155
  self.advance()
157
156
  continue
158
157
 
159
- # percents %...%
160
- if self.match('%'):
158
+ # --- %IDENT% ---
159
+ if ch == '%':
161
160
  flush_text()
162
- if not self.in_percent:
161
+ start = self.i
162
+ self.advance()
163
+ ident = self._read_ident()
164
+ if ident is not None and self.peek() == '%':
163
165
  self.emit(TokenType.PERCENT_OPEN)
164
- self.in_percent = True
165
- else:
166
+ self.emit(TokenType.IDENT, ident)
167
+ self.advance()
166
168
  self.emit(TokenType.PERCENT_CLOSE)
167
- self.in_percent = False
169
+ continue
170
+ self.i = start
171
+ buf.append(self.peek())
168
172
  self.advance()
169
173
  continue
170
174
 
171
- # colons :...:
172
- if self.match(':'):
175
+ # --- :IDENT: ---
176
+ if ch == ':':
173
177
  flush_text()
174
- if not self.in_colon:
178
+ start = self.i
179
+ self.advance()
180
+ ident = self._read_ident()
181
+ if ident is not None and self.peek() == ':':
175
182
  self.emit(TokenType.COLON_OPEN)
176
- self.in_colon = True
177
- else:
183
+ self.emit(TokenType.IDENT, ident)
184
+ self.advance()
178
185
  self.emit(TokenType.COLON_CLOSE)
179
- self.in_colon = False
186
+ continue
187
+ self.i = start
188
+ buf.append(self.peek())
180
189
  self.advance()
181
190
  continue
182
191
 
183
192
  # --- parens ---
184
-
185
193
  if ch == '(':
186
194
  flush_text()
187
195
  self.emit(TokenType.LPAREN)
@@ -194,39 +202,30 @@ class Tokenizer:
194
202
  self.advance()
195
203
  continue
196
204
 
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
-
205
+ # fallback TEXT
228
206
  buf.append(ch)
229
207
  self.advance()
230
208
 
231
209
  flush_text()
232
210
  return self.tokens
211
+
212
+ def _read_ident(self) -> str | None:
213
+ start = self.i
214
+ ch = self.peek()
215
+ if not ch or not ch.isalnum():
216
+ return None
217
+
218
+ self.advance()
219
+ while True:
220
+ nxt = self.peek()
221
+ if not nxt:
222
+ break
223
+ if nxt.isalnum() or nxt in '._':
224
+ self.advance()
225
+ else:
226
+ break
227
+
228
+ ident = self.text[start:self.i]
229
+ if ident.startswith('_') or ident.endswith('_') or '-' in ident:
230
+ return None
231
+ return ident
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapres
3
- Version: 3.dev21
3
+ Version: 3.dev22
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=0iBJBiqY4-_7jKUpY38HzLoZ2m_l2WJJvhBqcRUwo50,7182
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.dev22.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
15
+ mapres-3.dev22.dist-info/METADATA,sha256=3gas02EXpAh_Gh52KVRfuhJQ0IKhUQLiwYRGYd-IbFo,1476
16
+ mapres-3.dev22.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
17
+ mapres-3.dev22.dist-info/top_level.txt,sha256=5LMVQqJdtyjxfzv7wJBOZ-360m0Yc79w_-Y7s3n6cTw,7
18
+ mapres-3.dev22.dist-info/RECORD,,