mapres 3.dev15__tar.gz → 3.dev17__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.dev15/src/mapres.egg-info → mapres-3.dev17}/PKG-INFO +1 -1
- {mapres-3.dev15 → mapres-3.dev17}/pyproject.toml +1 -1
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/datamap.py +14 -16
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/evaluator.py +64 -2
- {mapres-3.dev15 → mapres-3.dev17/src/mapres.egg-info}/PKG-INFO +1 -1
- {mapres-3.dev15 → mapres-3.dev17}/LICENSE +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/README.md +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/setup.cfg +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/__init__.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/ast.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/cache.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/exceptions.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/layers.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/maps/__init__.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/maps/color.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/maps/time.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/parser.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/resolver.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres/tokenizer.py +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/SOURCES.txt +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/dependency_links.txt +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/requires.txt +0 -0
- {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/top_level.txt +0 -0
|
@@ -38,6 +38,7 @@ class DataMap:
|
|
|
38
38
|
__mode__: str | None = None # "dynamic", "config", "callable", or None
|
|
39
39
|
__recursive__: bool = False
|
|
40
40
|
__missing_key__: str = 'error' # 'error', 'silent', 'placeholder'
|
|
41
|
+
__ignore_delimiters__: bool = False
|
|
41
42
|
|
|
42
43
|
def as_map(self):
|
|
43
44
|
'''
|
|
@@ -51,7 +52,7 @@ class DataMap:
|
|
|
51
52
|
|
|
52
53
|
# dataclass fields
|
|
53
54
|
for f in fields(self):
|
|
54
|
-
if not f.init or f.name
|
|
55
|
+
if not f.init or f.name.startswith('__'):
|
|
55
56
|
continue
|
|
56
57
|
|
|
57
58
|
val = getattr(self, f.name)
|
|
@@ -78,15 +79,13 @@ class DataMap:
|
|
|
78
79
|
def _class_to_dict(self, cls):
|
|
79
80
|
out = {}
|
|
80
81
|
|
|
81
|
-
# dataclass fields on nested class
|
|
82
82
|
if is_dataclass(cls):
|
|
83
|
-
dummy = cls()
|
|
83
|
+
dummy = cls()
|
|
84
84
|
for f in fields(dummy):
|
|
85
85
|
if not f.init:
|
|
86
86
|
continue
|
|
87
87
|
out[f.name] = getattr(dummy, f.name)
|
|
88
88
|
|
|
89
|
-
# nested classes inside nested class
|
|
90
89
|
for name, obj in cls.__dict__.items():
|
|
91
90
|
if name.startswith('__'):
|
|
92
91
|
continue
|
|
@@ -96,23 +95,30 @@ class DataMap:
|
|
|
96
95
|
return out
|
|
97
96
|
|
|
98
97
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
def datamap(_cls=None, *, syntax=None, mode=None, recursive=None,
|
|
99
|
+
missing_key=None, ignore_delimiters=None):
|
|
100
|
+
|
|
102
101
|
def wrap(cls):
|
|
103
102
|
cls.__syntax__ = syntax
|
|
104
103
|
cls.__mode__ = mode
|
|
105
104
|
cls.__recursive__ = recursive
|
|
106
105
|
cls.__missing_key__ = missing_key or 'error'
|
|
106
|
+
cls.__ignore_delimiters__ = ignore_delimiters or False
|
|
107
|
+
|
|
107
108
|
namespace = dict(cls.__dict__)
|
|
108
109
|
namespace['__dict__'] = {}
|
|
109
110
|
rules_obj = namespace.get('rules', None)
|
|
111
|
+
|
|
110
112
|
cls = type(cls.__name__, (DataMap,), namespace)
|
|
113
|
+
|
|
111
114
|
if rules_obj is not None:
|
|
112
115
|
setattr(cls, 'rules', rules_obj)
|
|
116
|
+
|
|
113
117
|
return dataclass(frozen=False)(cls)
|
|
114
|
-
|
|
118
|
+
|
|
119
|
+
if _cls is not None and isinstance(_cls, type):
|
|
115
120
|
return wrap(_cls)
|
|
121
|
+
|
|
116
122
|
return wrap
|
|
117
123
|
|
|
118
124
|
|
|
@@ -130,30 +136,23 @@ class _SyntaxFactory:
|
|
|
130
136
|
self._syntax = syntax_name
|
|
131
137
|
|
|
132
138
|
def __call__(self, _cls=None, **kwargs):
|
|
133
|
-
# Case 1: bare decorator: @datamap.angles
|
|
134
139
|
if _cls is not None and isinstance(_cls, type):
|
|
135
140
|
return datamap(_cls, syntax=self._syntax)
|
|
136
|
-
|
|
137
|
-
# Case 2: decorator with args: @datamap.angles(mode='config')
|
|
138
141
|
return datamap(syntax=self._syntax, **kwargs)
|
|
139
142
|
|
|
140
143
|
@property
|
|
141
144
|
def config(self):
|
|
142
|
-
# Case 3: @datamap.angles.config
|
|
143
145
|
return datamap(syntax=self._syntax, mode='config')
|
|
144
146
|
|
|
145
147
|
@property
|
|
146
148
|
def dynamic(self):
|
|
147
|
-
# @datamap.angles.dynamic
|
|
148
149
|
return datamap(syntax=self._syntax, mode='dynamic')
|
|
149
150
|
|
|
150
151
|
@property
|
|
151
152
|
def callable(self):
|
|
152
|
-
# @datamap.angles.callable
|
|
153
153
|
return datamap(syntax=self._syntax, mode='callable')
|
|
154
154
|
|
|
155
155
|
|
|
156
|
-
# build syntax shortcuts
|
|
157
156
|
datamap.braces = _SyntaxFactory(syntax.braces)
|
|
158
157
|
datamap.dollars = _SyntaxFactory(syntax.dollars)
|
|
159
158
|
datamap.angles = _SyntaxFactory(syntax.angles)
|
|
@@ -164,7 +163,6 @@ datamap.at_tags = _SyntaxFactory(syntax.at_tags)
|
|
|
164
163
|
datamap.hash_tags = _SyntaxFactory(syntax.hash_tags)
|
|
165
164
|
datamap.paren_dollars = _SyntaxFactory(syntax.paren_dollars)
|
|
166
165
|
|
|
167
|
-
# global mode shortcuts
|
|
168
166
|
datamap.config = datamap(mode='config')
|
|
169
167
|
datamap.dynamic = datamap(mode='dynamic')
|
|
170
168
|
datamap.callable = datamap(mode='callable')
|
|
@@ -63,6 +63,10 @@ class Evaluator:
|
|
|
63
63
|
def _eval_ident(self, node: IdentNode):
|
|
64
64
|
name = node.name
|
|
65
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
|
+
|
|
66
70
|
# cycle detection
|
|
67
71
|
if name in self._seen:
|
|
68
72
|
raise EvaluationError(f"Cycle detected in identifier '{name}'")
|
|
@@ -94,6 +98,11 @@ class Evaluator:
|
|
|
94
98
|
def _eval_call(self, node: CallNode):
|
|
95
99
|
outer = node.name
|
|
96
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
|
+
|
|
97
106
|
if outer in self._seen:
|
|
98
107
|
raise EvaluationError(f"Cycle detected in call '{outer}'")
|
|
99
108
|
self._seen.add(outer)
|
|
@@ -141,7 +150,7 @@ class Evaluator:
|
|
|
141
150
|
|
|
142
151
|
def _has_recursive_maps(self):
|
|
143
152
|
for m in self.layerstack.all_maps():
|
|
144
|
-
if getattr(m, '__recursive__', False):
|
|
153
|
+
if getattr(m, '__recursive__', False) is True:
|
|
145
154
|
return True
|
|
146
155
|
return False
|
|
147
156
|
|
|
@@ -149,7 +158,7 @@ class Evaluator:
|
|
|
149
158
|
depths = [
|
|
150
159
|
getattr(m, '__max_depth__', 10)
|
|
151
160
|
for m in self.layerstack.all_maps()
|
|
152
|
-
if getattr(m, '__recursive__', False)
|
|
161
|
+
if getattr(m, '__recursive__', False) is True
|
|
153
162
|
]
|
|
154
163
|
return max(depths) if depths else 0
|
|
155
164
|
|
|
@@ -157,3 +166,56 @@ class Evaluator:
|
|
|
157
166
|
tokens = Tokenizer(text).tokenize()
|
|
158
167
|
ast = Parser(tokens).parse()
|
|
159
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
|
|
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
|