mapres 3.dev18__py3-none-any.whl → 3.dev20__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/datamap.py +1 -2
- mapres/evaluator.py +100 -5
- {mapres-3.dev18.dist-info → mapres-3.dev20.dist-info}/METADATA +1 -1
- {mapres-3.dev18.dist-info → mapres-3.dev20.dist-info}/RECORD +7 -7
- {mapres-3.dev18.dist-info → mapres-3.dev20.dist-info}/WHEEL +0 -0
- {mapres-3.dev18.dist-info → mapres-3.dev20.dist-info}/licenses/LICENSE +0 -0
- {mapres-3.dev18.dist-info → mapres-3.dev20.dist-info}/top_level.txt +0 -0
mapres/datamap.py
CHANGED
|
@@ -95,8 +95,7 @@ class DataMap:
|
|
|
95
95
|
return out
|
|
96
96
|
|
|
97
97
|
|
|
98
|
-
def datamap(_cls=None, *, syntax=None, mode=None, recursive=None,
|
|
99
|
-
missing_key=None, ignore_delimiters=None):
|
|
98
|
+
def datamap(_cls=None, *, syntax=None, mode=None, recursive=None, missing_key=None, ignore_delimiters=None):
|
|
100
99
|
|
|
101
100
|
def wrap(cls):
|
|
102
101
|
cls.__syntax__ = syntax
|
mapres/evaluator.py
CHANGED
|
@@ -14,45 +14,73 @@ class Evaluator:
|
|
|
14
14
|
self.ctx = ctx or {}
|
|
15
15
|
self._seen = set() # cycle detection
|
|
16
16
|
|
|
17
|
+
# public api
|
|
17
18
|
def evaluate(self, node):
|
|
18
19
|
return self._eval(node)
|
|
19
20
|
|
|
21
|
+
# internal dispatch
|
|
20
22
|
def _eval(self, node):
|
|
21
23
|
if isinstance(node, TemplateNode):
|
|
22
24
|
return self._eval_template(node)
|
|
25
|
+
|
|
23
26
|
if isinstance(node, TextNode):
|
|
24
27
|
return node.text
|
|
28
|
+
|
|
25
29
|
if isinstance(node, IdentNode):
|
|
26
30
|
return self._eval_ident(node)
|
|
31
|
+
|
|
27
32
|
if isinstance(node, CallNode):
|
|
28
33
|
return self._eval_call(node)
|
|
34
|
+
|
|
29
35
|
raise EvaluationError(f'Unknown AST node: {node}')
|
|
30
36
|
|
|
37
|
+
# template
|
|
31
38
|
def _eval_template(self, node: TemplateNode):
|
|
32
39
|
parts = []
|
|
33
40
|
for child in node.children:
|
|
34
41
|
parts.append(self._eval(child))
|
|
35
42
|
out = ''.join(parts)
|
|
36
43
|
|
|
37
|
-
# recursion
|
|
44
|
+
# recursion over full template output
|
|
45
|
+
if self._has_recursive_maps():
|
|
46
|
+
seen = set()
|
|
47
|
+
depth = 0
|
|
48
|
+
max_depth = self._get_max_depth()
|
|
49
|
+
|
|
50
|
+
while depth < max_depth:
|
|
51
|
+
new_out = self._re_resolve(out)
|
|
52
|
+
if new_out == out:
|
|
53
|
+
break
|
|
54
|
+
if new_out in seen:
|
|
55
|
+
raise EvaluationError('Cycle detected in template recursion')
|
|
56
|
+
seen.add(new_out)
|
|
57
|
+
out = new_out
|
|
58
|
+
depth += 1
|
|
59
|
+
|
|
38
60
|
return out
|
|
39
61
|
|
|
62
|
+
# identifier
|
|
40
63
|
def _eval_ident(self, node: IdentNode):
|
|
41
64
|
name = node.name
|
|
42
65
|
|
|
66
|
+
# ignore_delimiters: treat token as literal for this syntax
|
|
67
|
+
if self._syntax_ignores_delimiters(node.syntax):
|
|
68
|
+
return self._literal_from_syntax(name, node.syntax)
|
|
69
|
+
|
|
70
|
+
# cycle detection
|
|
43
71
|
if name in self._seen:
|
|
44
72
|
raise EvaluationError(f"Cycle detected in identifier '{name}'")
|
|
45
73
|
self._seen.add(name)
|
|
46
74
|
|
|
47
75
|
parts = name.split('.')
|
|
48
76
|
|
|
49
|
-
# ctx first
|
|
77
|
+
# 1. ctx first
|
|
50
78
|
val = self._lookup_hierarchical(self.ctx, parts)
|
|
51
79
|
if val is not None:
|
|
52
80
|
self._seen.remove(name)
|
|
53
81
|
return str(val)
|
|
54
82
|
|
|
55
|
-
# syntax-bound maps
|
|
83
|
+
# 2. syntax-bound maps
|
|
56
84
|
for m in self.layerstack.all_maps():
|
|
57
85
|
if getattr(m, '__syntax__', None) not in (None, node.syntax):
|
|
58
86
|
continue
|
|
@@ -66,9 +94,15 @@ class Evaluator:
|
|
|
66
94
|
self._seen.remove(name)
|
|
67
95
|
raise MissingKeyError(name)
|
|
68
96
|
|
|
97
|
+
# call (nested token)
|
|
69
98
|
def _eval_call(self, node: CallNode):
|
|
70
99
|
outer = node.name
|
|
71
100
|
|
|
101
|
+
# ignore_delimiters: treat call token as literal
|
|
102
|
+
if self._syntax_ignores_delimiters(node.syntax):
|
|
103
|
+
arg_text = self._eval(node.arg)
|
|
104
|
+
return self._literal_from_syntax(outer, node.syntax, is_call=True, arg_text=arg_text)
|
|
105
|
+
|
|
72
106
|
if outer in self._seen:
|
|
73
107
|
raise EvaluationError(f"Cycle detected in call '{outer}'")
|
|
74
108
|
self._seen.add(outer)
|
|
@@ -96,6 +130,7 @@ class Evaluator:
|
|
|
96
130
|
self._seen.remove(outer)
|
|
97
131
|
raise MissingKeyError(f'{outer}({arg_value})')
|
|
98
132
|
|
|
133
|
+
# helpers
|
|
99
134
|
def _map_to_dict(self, m):
|
|
100
135
|
if hasattr(m, 'as_map'):
|
|
101
136
|
return m.as_map()
|
|
@@ -113,14 +148,74 @@ class Evaluator:
|
|
|
113
148
|
cur = cur[p]
|
|
114
149
|
return cur
|
|
115
150
|
|
|
116
|
-
# recursion helpers now unused
|
|
117
151
|
def _has_recursive_maps(self):
|
|
152
|
+
for m in self.layerstack.all_maps():
|
|
153
|
+
if getattr(m, '__recursive__', False) is True:
|
|
154
|
+
return True
|
|
118
155
|
return False
|
|
119
156
|
|
|
120
157
|
def _get_max_depth(self):
|
|
121
|
-
|
|
158
|
+
depths = [
|
|
159
|
+
getattr(m, '__max_depth__', 10)
|
|
160
|
+
for m in self.layerstack.all_maps()
|
|
161
|
+
if getattr(m, '__recursive__', False) is True
|
|
162
|
+
]
|
|
163
|
+
return max(depths) if depths else 0
|
|
122
164
|
|
|
123
165
|
def _re_resolve(self, text: str) -> str:
|
|
124
166
|
tokens = Tokenizer(text).tokenize()
|
|
125
167
|
ast = Parser(tokens).parse()
|
|
126
168
|
return self.evaluate(ast)
|
|
169
|
+
|
|
170
|
+
def _syntax_ignores_delimiters(self, syntax_name: str) -> bool:
|
|
171
|
+
for m in self.layerstack.all_maps():
|
|
172
|
+
if getattr(m, '__syntax__', None) == syntax_name:
|
|
173
|
+
if getattr(m, '__ignore_delimiters__', False):
|
|
174
|
+
return True
|
|
175
|
+
return False
|
|
176
|
+
|
|
177
|
+
def _literal_from_syntax(self, name: str, syntax_name: str, is_call: bool = False, arg_text: str | None = None) -> str:
|
|
178
|
+
if not is_call:
|
|
179
|
+
if syntax_name == 'colons':
|
|
180
|
+
return f':{name}:'
|
|
181
|
+
if syntax_name == 'braces':
|
|
182
|
+
return f'{{{{{name}}}}}'
|
|
183
|
+
if syntax_name == 'dollars':
|
|
184
|
+
return f'${{{name}}}'
|
|
185
|
+
if syntax_name == 'angles':
|
|
186
|
+
return f'<{name}>'
|
|
187
|
+
if syntax_name == 'pipes':
|
|
188
|
+
return f'|{name}|'
|
|
189
|
+
if syntax_name == 'percents':
|
|
190
|
+
return f'%{name}%'
|
|
191
|
+
if syntax_name == 'paren_dollars':
|
|
192
|
+
return f'$({name})'
|
|
193
|
+
if syntax_name == 'at_tags':
|
|
194
|
+
return f'@{name}@'
|
|
195
|
+
if syntax_name == 'hash_tags':
|
|
196
|
+
return f'#{name}#'
|
|
197
|
+
return name
|
|
198
|
+
|
|
199
|
+
# call literal: syntax-specific wrapping of name(arg_text)
|
|
200
|
+
arg = arg_text or ''
|
|
201
|
+
inner = f'{name}({arg})'
|
|
202
|
+
|
|
203
|
+
if syntax_name == 'colons':
|
|
204
|
+
return f':{inner}:'
|
|
205
|
+
if syntax_name == 'braces':
|
|
206
|
+
return f'{{{{{inner}}}}}'
|
|
207
|
+
if syntax_name == 'dollars':
|
|
208
|
+
return f'${{{inner}}}'
|
|
209
|
+
if syntax_name == 'angles':
|
|
210
|
+
return f'<{inner}>'
|
|
211
|
+
if syntax_name == 'pipes':
|
|
212
|
+
return f'|{inner}|'
|
|
213
|
+
if syntax_name == 'percents':
|
|
214
|
+
return f'%{inner}%'
|
|
215
|
+
if syntax_name == 'paren_dollars':
|
|
216
|
+
return f'$({inner})'
|
|
217
|
+
if syntax_name == 'at_tags':
|
|
218
|
+
return f'@{inner}@'
|
|
219
|
+
if syntax_name == 'hash_tags':
|
|
220
|
+
return f'#{inner}#'
|
|
221
|
+
return inner
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
mapres/__init__.py,sha256=t23ofM-wzpPByYi1rEQonxyU1yyza4ZzwwHDZUa5QqA,1010
|
|
2
2
|
mapres/ast.py,sha256=6PcMkwtNHYD8xEkdyzDgn_mRTj1ZaWSyuHZK3BtXiuo,427
|
|
3
3
|
mapres/cache.py,sha256=hbvsoc6Qw3RhWAEfOOpKHtC7i9EomoUgWzyd4dnH71w,333
|
|
4
|
-
mapres/datamap.py,sha256=
|
|
5
|
-
mapres/evaluator.py,sha256=
|
|
4
|
+
mapres/datamap.py,sha256=lqz781REOWSt0YwliSrKA1gDxrLeNgN--wwtyyl8Cp8,5244
|
|
5
|
+
mapres/evaluator.py,sha256=glpB4wzugtst4QtkQyAHwUXliHs13B1P4h4VjRAdgp0,6978
|
|
6
6
|
mapres/exceptions.py,sha256=D2XKnrMkZDyUXQt2PmaKbj08FObWU00yXKGHwNpjvww,222
|
|
7
7
|
mapres/layers.py,sha256=sFc1eUOBg2BoB2cBXIleq8RMYcg5GZpUoCnGh-uKN6U,1380
|
|
8
8
|
mapres/parser.py,sha256=8XLCzYosvh81bN-Jddc1AM4w0X7bsHnBzmA8sq5_72w,4018
|
|
@@ -11,8 +11,8 @@ mapres/tokenizer.py,sha256=js0S1s8MkltdQoH5V1cLjdY0fFqZoGqmtP_OhEJEM6M,6539
|
|
|
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.
|
|
15
|
-
mapres-3.
|
|
16
|
-
mapres-3.
|
|
17
|
-
mapres-3.
|
|
18
|
-
mapres-3.
|
|
14
|
+
mapres-3.dev20.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
15
|
+
mapres-3.dev20.dist-info/METADATA,sha256=aOvYC-_w1Kk_sBwqdIpEfEwTamgHFHepWYf0uDGo_4E,1476
|
|
16
|
+
mapres-3.dev20.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
|
|
17
|
+
mapres-3.dev20.dist-info/top_level.txt,sha256=5LMVQqJdtyjxfzv7wJBOZ-360m0Yc79w_-Y7s3n6cTw,7
|
|
18
|
+
mapres-3.dev20.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|