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.
Files changed (23) hide show
  1. {mapres-3.dev15/src/mapres.egg-info → mapres-3.dev17}/PKG-INFO +1 -1
  2. {mapres-3.dev15 → mapres-3.dev17}/pyproject.toml +1 -1
  3. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/datamap.py +14 -16
  4. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/evaluator.py +64 -2
  5. {mapres-3.dev15 → mapres-3.dev17/src/mapres.egg-info}/PKG-INFO +1 -1
  6. {mapres-3.dev15 → mapres-3.dev17}/LICENSE +0 -0
  7. {mapres-3.dev15 → mapres-3.dev17}/README.md +0 -0
  8. {mapres-3.dev15 → mapres-3.dev17}/setup.cfg +0 -0
  9. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/__init__.py +0 -0
  10. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/ast.py +0 -0
  11. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/cache.py +0 -0
  12. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/exceptions.py +0 -0
  13. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/layers.py +0 -0
  14. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/maps/__init__.py +0 -0
  15. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/maps/color.py +0 -0
  16. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/maps/time.py +0 -0
  17. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/parser.py +0 -0
  18. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/resolver.py +0 -0
  19. {mapres-3.dev15 → mapres-3.dev17}/src/mapres/tokenizer.py +0 -0
  20. {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/SOURCES.txt +0 -0
  21. {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/dependency_links.txt +0 -0
  22. {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/requires.txt +0 -0
  23. {mapres-3.dev15 → mapres-3.dev17}/src/mapres.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapres
3
- Version: 3.dev15
3
+ Version: 3.dev17
4
4
  Summary: A powerfull mapping utility
5
5
  Author: iFamished
6
6
  License: MIT
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
 
8
8
  [project]
9
9
  name = "mapres"
10
- version = "3-dev.15"
10
+ version = "3-dev.17"
11
11
  description = "A powerfull mapping utility"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.9"
@@ -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 in ('__syntax__', '__mode__', '__recursive__', '__missing_key__'):
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() # assumes no required __init__ args
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
- # main decorator factory
100
- def datamap(_cls=None, *, syntax=None, mode=None, recursive=None, missing_key=None):
101
- '''@datamap decorator'''
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
- if _cls is not None:
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mapres
3
- Version: 3.dev15
3
+ Version: 3.dev17
4
4
  Summary: A powerfull mapping utility
5
5
  Author: iFamished
6
6
  License: MIT
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes