omlish 0.0.0.dev306__py3-none-any.whl → 0.0.0.dev307__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.
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev306'
2
- __revision__ = 'dda70244f84192bfce2428b682f9a5e2349fb05a'
1
+ __version__ = '0.0.0.dev307'
2
+ __revision__ = '7fd729bd16b72af663b113712ea301f150b2449d'
3
3
 
4
4
 
5
5
  #
@@ -1,4 +1,5 @@
1
1
  from .parsing import ( # Noqa
2
+ MetaMaker,
2
3
  parse,
3
4
  parse_list,
4
5
  )
@@ -10,6 +11,6 @@ from .values import ( # noqa
10
11
  Map,
11
12
  Set,
12
13
  Symbol,
13
- TaggedVal,
14
+ Tagged,
14
15
  Vector,
15
16
  )
@@ -0,0 +1,325 @@
1
+ r"""
2
+ https://github.com/edn-format/edn
3
+ https://github.com/antlr/grammars-v4/blob/master/edn/edn.g4
4
+ https://github.com/jorinvo/edn-data/blob/1e5824f63803eb58f35e98839352000053d47115/src/parse.ts
5
+ https://clojure.org/reference/reader#_extensible_data_notation_edn
6
+ """
7
+ import dataclasses as dc
8
+ import io
9
+ import typing as ta
10
+
11
+ from ... import check
12
+ from ...funcs.genmachine import GenMachine
13
+
14
+
15
+ ##
16
+
17
+
18
+ TokenKind: ta.TypeAlias = ta.Literal[
19
+ 'STRING',
20
+ 'CHAR',
21
+ 'WORD',
22
+ 'COMMENT',
23
+
24
+ 'LPAREN',
25
+ 'RPAREN',
26
+ 'LBRACKET',
27
+ 'RBRACKET',
28
+ 'HASH_LBRACE',
29
+ 'LBRACE',
30
+ 'RBRACE',
31
+
32
+ 'HASH_UNDERSCORE',
33
+ 'META',
34
+ 'QUOTE',
35
+ ]
36
+
37
+
38
+ class Position(ta.NamedTuple):
39
+ ofs: int
40
+ line: int
41
+ col: int
42
+
43
+
44
+ class Token(ta.NamedTuple):
45
+ kind: TokenKind
46
+ src: str
47
+
48
+ pos: Position
49
+
50
+ def __iter__(self):
51
+ raise TypeError
52
+
53
+
54
+ ##
55
+
56
+
57
+ SINGLE_TOKENS: ta.Mapping[str, TokenKind] = {
58
+ '(': 'LPAREN',
59
+ ')': 'RPAREN',
60
+ '[': 'LBRACKET',
61
+ ']': 'RBRACKET',
62
+ '{': 'LBRACE',
63
+ '}': 'RBRACE',
64
+
65
+ '^': 'META',
66
+ "'": 'QUOTE',
67
+ }
68
+
69
+
70
+ HASH_TOKENS: ta.Mapping[str, TokenKind] = {
71
+ '{': 'HASH_LBRACE',
72
+ '_': 'HASH_UNDERSCORE',
73
+ }
74
+
75
+
76
+ WORD_FIRST_SPECIAL_CHARS = ':.*+!-_?$%&=<>.'
77
+ WORD_BODY_SPECIAL_CHARS = '/'
78
+
79
+
80
+ ##
81
+
82
+
83
+ @dc.dataclass()
84
+ class StreamLexError(Exception):
85
+ message: str
86
+
87
+ pos: Position
88
+
89
+
90
+ class StreamLexer(GenMachine[str, Token]):
91
+ def __init__(self) -> None:
92
+ self._ofs = 0
93
+ self._line = 1
94
+ self._col = 0
95
+
96
+ self._buf = io.StringIO()
97
+
98
+ super().__init__(self._do_main())
99
+
100
+ @property
101
+ def pos(self) -> Position:
102
+ return Position(
103
+ self._ofs,
104
+ self._line,
105
+ self._col,
106
+ )
107
+
108
+ def _char_in(self, c: str) -> str:
109
+ if not isinstance(c, str):
110
+ raise TypeError(c)
111
+ if c and len(c) != 1:
112
+ raise ValueError(c)
113
+
114
+ self._ofs += 1
115
+
116
+ if c == '\n':
117
+ self._line += 1
118
+ self._col = 0
119
+ else:
120
+ self._col += 1
121
+
122
+ return c
123
+
124
+ def _make_tok(
125
+ self,
126
+ kind: TokenKind,
127
+ src: str,
128
+ pos: Position,
129
+ ) -> ta.Sequence[Token]:
130
+ tok = Token(
131
+ kind,
132
+ src,
133
+ pos,
134
+ )
135
+ return (tok,)
136
+
137
+ def _flip_buf(self) -> str:
138
+ src = self._buf.getvalue()
139
+ self._buf.seek(0)
140
+ self._buf.truncate()
141
+ return src
142
+
143
+ def _raise(self, msg: str, src: Exception | None = None) -> ta.NoReturn:
144
+ raise StreamLexError(msg, self.pos) from src
145
+
146
+ def _do_main(self, p: str | None = None):
147
+ while True:
148
+ if p is not None:
149
+ c = p
150
+ p = None
151
+ else:
152
+ c = self._char_in((yield None)) # noqa
153
+
154
+ if not c:
155
+ return None
156
+
157
+ if c.isspace() or c == ',':
158
+ continue
159
+
160
+ if c in SINGLE_TOKENS:
161
+ yield self._make_tok(SINGLE_TOKENS[c], c, self.pos)
162
+ continue
163
+
164
+ if c == ';':
165
+ return self._do_comment()
166
+
167
+ if c == '"':
168
+ return self._do_string()
169
+
170
+ if c == '\\':
171
+ return self._do_char()
172
+
173
+ if c == '#':
174
+ return self._do_hash()
175
+
176
+ if (
177
+ c.isalnum() or
178
+ c in WORD_FIRST_SPECIAL_CHARS
179
+ ):
180
+ return self._do_word(c)
181
+
182
+ self._raise(f'Unexpected input: {c}')
183
+
184
+ def _do_comment(self):
185
+ check.state(self._buf.tell() == 0)
186
+ self._buf.write(';')
187
+
188
+ pos = self.pos
189
+
190
+ while True:
191
+ try:
192
+ c = self._char_in((yield None)) # noqa
193
+ except GeneratorExit:
194
+ self._raise('Unexpected end of input')
195
+
196
+ if not c or c == '\n':
197
+ break
198
+
199
+ self._buf.write(c)
200
+
201
+ src = self._flip_buf()
202
+ yield self._make_tok('COMMENT', src, pos)
203
+ return self._do_main()
204
+
205
+ def _do_string(self):
206
+ check.state(self._buf.tell() == 0)
207
+ self._buf.write('"')
208
+
209
+ pos = self.pos
210
+
211
+ esc = False
212
+ while True:
213
+ try:
214
+ c = self._char_in((yield None)) # noqa
215
+ except GeneratorExit:
216
+ self._raise('Unexpected end of input')
217
+
218
+ if not c:
219
+ self._raise(f'Unterminated string literal: {self._buf.getvalue()}')
220
+
221
+ self._buf.write(c)
222
+ if esc:
223
+ esc = False
224
+ elif c == '\\':
225
+ esc = True
226
+ elif c == '"':
227
+ break
228
+
229
+ src = self._flip_buf()
230
+ yield self._make_tok('STRING', src, pos)
231
+ return self._do_main()
232
+
233
+ def _do_char(self):
234
+ check.state(self._buf.tell() == 0)
235
+ self._buf.write('\\')
236
+
237
+ pos = self.pos
238
+
239
+ while True:
240
+ try:
241
+ c = self._char_in((yield None)) # noqa
242
+ except GeneratorExit:
243
+ self._raise('Unexpected end of input')
244
+
245
+ if not c or not (
246
+ c.isalnum() or
247
+ c == '\\'
248
+ ):
249
+ break
250
+
251
+ self._buf.write(c)
252
+
253
+ src = self._flip_buf()
254
+ yield self._make_tok('CHAR', src, pos)
255
+ return self._do_main(c)
256
+
257
+ def _do_hash(self):
258
+ check.state(self._buf.tell() == 0)
259
+
260
+ pos = self.pos
261
+
262
+ try:
263
+ c = self._char_in((yield None)) # noqa
264
+ except GeneratorExit:
265
+ self._raise('Unexpected end of input')
266
+
267
+ if (ht := HASH_TOKENS.get(c)) is not None:
268
+ yield self._make_tok(ht, '#' + c, pos)
269
+ return self._do_main()
270
+
271
+ elif (
272
+ c.isalnum() or
273
+ c == '#' or
274
+ c in WORD_FIRST_SPECIAL_CHARS
275
+ ):
276
+ return self._do_word('#' + c, pos=pos)
277
+
278
+ else:
279
+ self._raise(f'Unexpected input: {c}')
280
+
281
+ def _do_word(self, pfx: str, *, pos: Position | None = None):
282
+ check.state(self._buf.tell() == 0)
283
+ self._buf.write(pfx)
284
+
285
+ if pos is None:
286
+ pos = self.pos
287
+
288
+ while True:
289
+ try:
290
+ c = self._char_in((yield None)) # noqa
291
+ except GeneratorExit:
292
+ self._raise('Unexpected end of input')
293
+
294
+ if not c or not (
295
+ c.isalnum() or
296
+ c in WORD_FIRST_SPECIAL_CHARS or
297
+ c in WORD_BODY_SPECIAL_CHARS
298
+ ):
299
+ break
300
+
301
+ self._buf.write(c)
302
+
303
+ src = self._flip_buf()
304
+ yield self._make_tok('WORD', src, pos)
305
+ return self._do_main(c)
306
+
307
+
308
+ ##
309
+
310
+
311
+ def test_lex():
312
+ for s in [
313
+ '"abc"',
314
+ '{"a" "b"}',
315
+ '1',
316
+ '-1',
317
+ '{a :b c 420}',
318
+ '#{a}',
319
+ ]:
320
+ print(s)
321
+ with StreamLexer() as lex:
322
+ for c in [*s, '']:
323
+ for t in lex(c):
324
+ print(t)
325
+ print()
@@ -1,28 +1,88 @@
1
+ # ruff: noqa: PYI055 UP007
1
2
  """
2
3
  TODO:
4
+ - \u0123 in strings
5
+ - https://clojure.org/reference/reader
3
6
  - reader meta - ^:foo
7
+ - read table
4
8
  """
5
- # https://github.com/jorinvo/edn-data/blob/1e5824f63803eb58f35e98839352000053d47115/test/parse.test.ts
9
+ import dataclasses as dc
6
10
  import datetime
7
11
  import enum
12
+ import io
8
13
  import re
9
14
  import typing as ta
10
15
 
11
16
  from ... import check
17
+ from ...funcs.genmachine import GenMachine
18
+ from .lexing import Position
19
+ from .lexing import StreamLexer
20
+ from .lexing import Token
12
21
  from .values import Char
22
+ from .values import Collection
13
23
  from .values import Keyword
14
24
  from .values import List
15
25
  from .values import Map
16
26
  from .values import Set
17
27
  from .values import Symbol
18
- from .values import TaggedVal
28
+ from .values import Tagged
19
29
  from .values import Vector
20
30
 
21
31
 
22
32
  ##
23
33
 
24
34
 
25
- class ListParser:
35
+ WORD_CONST_VALUES: ta.Mapping[str, ta.Any] = {
36
+ 'true': True,
37
+ 'false': False,
38
+ 'nil': None,
39
+
40
+ '##Inf': float('inf'),
41
+ '##-Inf': float('-inf'),
42
+ '##NaN': float('nan'),
43
+ }
44
+
45
+
46
+ STRING_ESCAPE_MAP: ta.Mapping[str, str] = {
47
+ '"': '"',
48
+ '\\': '\\',
49
+ 'b': '\b',
50
+ 'f': '\f',
51
+ 'n': '\n',
52
+ 'r': '\r',
53
+ 't': '\t',
54
+ }
55
+
56
+
57
+ CHAR_ESCAPE_MAP: ta.Mapping[str, str] = {
58
+ 'backspace': '\b',
59
+ 'formfeed': '\f',
60
+ 'newline': '\n',
61
+ 'return': '\r',
62
+ 'space': ' ',
63
+ 'tab': '\t',
64
+ }
65
+
66
+
67
+ ##
68
+
69
+
70
+ @dc.dataclass()
71
+ class StreamParseError(Exception):
72
+ message: str
73
+
74
+ pos: Position | None = None
75
+
76
+
77
+ @dc.dataclass(frozen=True)
78
+ class MetaMaker:
79
+ fn: ta.Callable[..., ta.Any]
80
+
81
+ def __call__(self, *args: ta.Any, meta: ta.Any | None = None) -> ta.Any:
82
+ return self.fn(*args, meta=meta)
83
+
84
+
85
+ class StreamParser(GenMachine[Token, ta.Any]):
26
86
  DEFAULT_TAG_HANDLERS: ta.ClassVar[ta.Mapping[str, ta.Callable[..., ta.Any]]] = {
27
87
  'inst': lambda val: datetime.datetime.fromisoformat(val) if isinstance(val, str) else None,
28
88
  }
@@ -30,19 +90,17 @@ class ListParser:
30
90
  def __init__(
31
91
  self,
32
92
  *,
33
- keyword_maker: ta.Callable[..., ta.Any] = Keyword,
34
- char_maker: ta.Callable[..., ta.Any] = Char,
35
- symbol_maker: ta.Callable[..., ta.Any] = Symbol,
93
+ keyword_maker: ta.Callable[..., ta.Any] = MetaMaker(Keyword),
94
+ char_maker: ta.Callable[..., ta.Any] = MetaMaker(Char),
95
+ symbol_maker: ta.Callable[..., ta.Any] = MetaMaker(Symbol),
36
96
 
37
- list_maker: ta.Callable[..., ta.Any] = List.new,
38
- vector_maker: ta.Callable[..., ta.Any] = Vector.new,
39
- set_maker: ta.Callable[..., ta.Any] = Set.new,
40
- map_maker: ta.Callable[..., ta.Any] = Map.new,
97
+ list_maker: ta.Callable[..., ta.Any] = MetaMaker(List.new),
98
+ vector_maker: ta.Callable[..., ta.Any] = MetaMaker(Vector.new),
99
+ set_maker: ta.Callable[..., ta.Any] = MetaMaker(Set.new),
100
+ map_maker: ta.Callable[..., ta.Any] = MetaMaker(Map.new),
41
101
 
42
102
  tag_handlers: ta.Mapping[str, ta.Callable[..., ta.Any]] | None = None,
43
103
  ) -> None:
44
- super().__init__()
45
-
46
104
  self._keyword_maker = keyword_maker
47
105
  self._char_maker = char_maker
48
106
  self._symbol_maker = symbol_maker
@@ -57,299 +115,260 @@ class ListParser:
57
115
  **(tag_handlers or {}),
58
116
  }
59
117
 
60
- self._stack: list[tuple[ListParser._ParseMode | ListParser._StackItem, ta.Any]] = []
61
- self._mode: ListParser._ParseMode = ListParser._ParseMode.IDLE
62
- self._state = ''
63
- self._result: ta.Any = self._UNDEFINED
118
+ self._stack: list[
119
+ tuple[
120
+ ta.Union[
121
+ type[Collection],
122
+ StreamParser._StackSpecial,
123
+ ],
124
+ list[ta.Any],
125
+ ],
126
+ ] = []
64
127
 
65
- #
128
+ super().__init__(self._do_main())
66
129
 
67
- class _UNDEFINED: # noqa
68
- def __new__(cls, *args, **kwargs): # noqa
69
- raise TypeError
130
+ class _StackSpecial(enum.Enum): # noqa
131
+ DISCARD = enum.auto()
132
+ TAG = enum.auto()
70
133
 
71
- class _ParseMode(enum.Enum):
72
- IDLE = 0
73
- STRING = 1
74
- ESCAPE = 2
75
- COMMENT = 3
134
+ def _emit_value(self, value: ta.Any) -> tuple[ta.Any, ...]:
135
+ while self._stack and self._stack[-1][0] is StreamParser._StackSpecial.TAG:
136
+ cc, cl = self._stack.pop()
137
+ ts = check.non_empty_str(check.single(cl))
138
+ value = Tagged(ts, value)
76
139
 
77
- class _StackItem(enum.Enum):
78
- VECTOR = 0
79
- LIST = 1
80
- MAP = 2
81
- SET = 3
82
- TAG = 4
140
+ if not self._stack:
141
+ return (value,)
83
142
 
84
- #
143
+ cc, cl = self._stack[-1]
85
144
 
86
- def _update_stack(self) -> None:
87
- if not self._stack or self._result is self._UNDEFINED:
88
- return
145
+ if cc is StreamParser._StackSpecial.DISCARD:
146
+ check.empty(cl)
147
+ self._stack.pop()
148
+ return ()
89
149
 
90
- stack_item, prev_state = self._stack[-1]
150
+ elif cc is StreamParser._StackSpecial.TAG:
151
+ ts = check.non_empty_str(check.single(cl))
152
+ self._stack.pop()
153
+ tv = Tagged(ts, value)
154
+ return (tv,)
91
155
 
92
- if stack_item == ListParser._StackItem.VECTOR:
93
- prev_state.append(self._result)
156
+ elif cc is Map:
157
+ if cl and len(cl[-1]) < 2:
158
+ cl[-1] = (*cl[-1], value)
159
+ else:
160
+ cl.append((value,))
161
+ return ()
162
+
163
+ elif isinstance(cc, type) and issubclass(cc, Collection):
164
+ cl.append(value)
165
+ return ()
166
+
167
+ else:
168
+ raise RuntimeError(cc)
169
+
170
+ def _do_main(self):
171
+ while True:
172
+ tok: Token
173
+ try:
174
+ tok = yield None # noqa
175
+ except GeneratorExit:
176
+ if self._stack:
177
+ raise StreamParseError('Expected value') from None
178
+ else:
179
+ raise
94
180
 
95
- elif stack_item == ListParser._StackItem.LIST:
96
- prev_state.append(self._result)
181
+ value: ta.Any
97
182
 
98
- elif stack_item == ListParser._StackItem.SET:
99
- prev_state.append(self._result)
183
+ # scalars
100
184
 
101
- elif stack_item == ListParser._StackItem.MAP:
102
- if len(prev_state[1]) > 0:
103
- prev_state[0].append([prev_state[1].pop(), self._result])
104
- else:
105
- prev_state[1].append(self._result)
185
+ if tok.kind == 'STRING':
186
+ value = self._parse_string(tok)
106
187
 
107
- elif stack_item == ListParser._StackItem.TAG:
108
- self._stack.pop()
188
+ elif tok.kind == 'CHAR':
189
+ value = self._parse_char(tok)
109
190
 
110
- if prev_state == '_':
111
- self._result = self._UNDEFINED
191
+ elif tok.kind == 'WORD':
192
+ if tok.src.startswith('#'):
193
+ # FIXME: more dispatching
194
+ self._stack.append((StreamParser._StackSpecial.TAG, [tok.src[1:]]))
195
+ continue
112
196
 
113
- else:
114
- tag_handler = self._tag_handlers.get(prev_state)
115
- if tag_handler:
116
- self._result = tag_handler(self._result)
117
197
  else:
118
- self._result = TaggedVal(prev_state, self._result)
119
-
120
- self._update_stack()
121
- return
122
-
123
- # TODO: else error
124
- # Reset result after updating stack
125
- self._result = self._UNDEFINED
126
-
127
- #
128
-
129
- _INT_PAT = re.compile(r'^[-+]?(0|[1-9][0-9]*)$')
130
- _BIGINT_PAT = re.compile(r'^[-+]?(0|[1-9][0-9]*)N$')
131
- _FLOAT_PAT = re.compile(r'^[-+]?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?(0|[1-9][0-9]*))?M?$')
132
-
133
- def _match(self) -> None:
134
- if self._state == 'nil':
135
- self._result = None
136
-
137
- elif self._state == 'true':
138
- self._result = True
139
-
140
- elif self._state == 'false':
141
- self._result = False
142
-
143
- elif self._state.startswith(':'):
144
- # Keyword
145
- self._result = self._keyword_maker(self._state[1:])
146
-
147
- elif self._state.startswith('#'):
148
- # Tag
149
- self._stack.append((ListParser._StackItem.TAG, self._state[1:]))
150
- self._result = self._UNDEFINED
151
-
152
- elif self._INT_PAT.match(self._state):
153
- # Int
154
- self._result = int(self._state)
155
-
156
- elif self._FLOAT_PAT.match(self._state):
157
- # Float
158
- self._result = float(self._state)
159
-
160
- elif self._BIGINT_PAT.match(self._state):
161
- # BigInt
162
- self._result = int(self._state[:-1]) # In Python we don't need special handling for bigint
163
-
164
- elif self._state.startswith('\\'):
165
- # Char
166
- check.state(len(self._state) > 1)
167
- if self._state == '\\space':
168
- c = ' '
169
- elif self._state == '\\newline':
170
- c = '\n'
171
- elif self._state == '\\return':
172
- c = '\r'
173
- elif self._state == '\\tab':
174
- c = '\t'
175
- elif self._state == '\\\\':
176
- c = '\\'
177
- elif self._state.startswith('\\u'):
178
- check.state(len(self._state) == 6)
179
- c = chr(int(self._state[2:], 16))
180
- else:
181
- check.state(len(self._state) == 2)
182
- c = self._state[1:]
198
+ value = self._parse_word(tok)
183
199
 
184
- self._result = self._char_maker(c)
200
+ elif tok.kind == 'COMMENT':
201
+ continue
202
+
203
+ # open
204
+
205
+ elif tok.kind == 'LPAREN':
206
+ self._stack.append((List, []))
207
+ continue
185
208
 
186
- elif self._state:
187
- # Symbol
188
- self._result = self._symbol_maker(self._state)
209
+ elif tok.kind == 'LBRACKET':
210
+ self._stack.append((Vector, []))
211
+ continue
189
212
 
190
- self._state = ''
213
+ elif tok.kind == 'HASH_LBRACE':
214
+ self._stack.append((Set, []))
215
+ continue
191
216
 
192
- #
217
+ elif tok.kind == 'LBRACE':
218
+ self._stack.append((Map, []))
219
+ continue
193
220
 
194
- _SPACE_CHARS: ta.ClassVar[ta.AbstractSet[str]] = frozenset([',', ' ', '\t', '\n', '\r'])
221
+ elif tok.kind == 'HASH_UNDERSCORE':
222
+ self._stack.append((StreamParser._StackSpecial.DISCARD, []))
223
+ continue
195
224
 
196
- _STRING_ESCAPE_MAP: ta.ClassVar[ta.Mapping[str, str]] = {
197
- 't': '\t',
198
- 'r': '\r',
199
- 'n': '\n',
200
- '\\': '\\',
201
- '"': '"',
202
- }
225
+ # close
203
226
 
204
- def parse(self, src: str) -> list[ta.Any]:
205
- values = []
227
+ elif tok.kind == 'RPAREN':
228
+ cc, cl = self._stack.pop()
229
+ check.state(cc is List)
230
+ value = self._list_maker(cl)
206
231
 
207
- i = -1
208
- for i in range(len(src)):
209
- if not self._stack and self._result is not self._UNDEFINED:
210
- values.append(self._result)
211
- self._result = self._UNDEFINED
232
+ elif tok.kind == 'RBRACKET':
233
+ cc, cl = self._stack.pop()
234
+ check.state(cc is Vector)
235
+ value = self._vector_maker(cl)
212
236
 
213
- char = src[i]
237
+ elif tok.kind == 'RBRACE':
238
+ cc, cl = self._stack.pop()
214
239
 
215
- if self._mode == ListParser._ParseMode.IDLE:
216
- if char == '"':
217
- self._match()
218
- self._update_stack()
219
- self._mode = ListParser._ParseMode.STRING
220
- self._state = ''
221
- continue
240
+ if cc is Set:
241
+ value = self._set_maker(cl)
222
242
 
223
- if char == ';':
224
- self._mode = ListParser._ParseMode.COMMENT
225
- continue
243
+ elif cc is Map:
244
+ if cl and len(cl[-1]) != 2:
245
+ raise RuntimeError('Mismatched map entries')
246
+ value = self._map_maker(cl)
226
247
 
227
- if char in self._SPACE_CHARS:
228
- self._match()
229
- self._update_stack()
230
- continue
248
+ else:
249
+ raise RuntimeError(cc)
231
250
 
232
- if char == '}':
233
- self._match()
234
- self._update_stack()
251
+ # nyi
235
252
 
236
- if self._stack:
237
- stack_item, prev_state = self._stack.pop()
253
+ elif tok.kind == 'META':
254
+ raise NotImplementedError
238
255
 
239
- if stack_item == ListParser._StackItem.MAP:
240
- check.empty(prev_state[1])
241
- self._result = self._map_maker(prev_state[0])
256
+ elif tok.kind == 'QUOTE':
257
+ raise NotImplementedError
242
258
 
243
- else: # Set
244
- # FIXME:
245
- # check.state(stack_item == ListParser._StackItem.SET)
246
- self._result = self._set_maker(prev_state)
259
+ # failure
247
260
 
248
- self._update_stack()
249
- continue
261
+ else:
262
+ raise ValueError(tok.kind)
263
+
264
+ # emit
265
+
266
+ if (ev := self._emit_value(value)):
267
+ yield ev
268
+
269
+ def _parse_string(self, tok: Token) -> str:
270
+ check.state(tok.kind == 'STRING')
271
+ src = tok.src
272
+ check.state(src[0] == '"')
273
+ check.state(src[-1] == '"')
274
+ check.state(len(src) > 1)
275
+
276
+ p = 1
277
+ end = len(src) - 1
278
+ if src.find('\\', p, end) < 0:
279
+ return src[1:-1]
280
+
281
+ sb = io.StringIO()
282
+ while True:
283
+ if (n := src.find('\\', p, end)) < 0:
284
+ sb.write(src[p:end])
285
+ break
286
+
287
+ sb.write(src[p:n])
288
+ p = n + 1
289
+ check.state(p < end)
290
+ x = src[p]
291
+ p += 1
292
+
293
+ if x == 'u':
294
+ check.state(p < end - 4)
295
+ r = chr(int(src[p:p + 4], 16))
296
+ p += 4
250
297
 
251
- if char == ']':
252
- self._match()
253
- self._update_stack()
254
- stack_item, prev_state = self._stack.pop()
255
- self._result = self._vector_maker(tuple(prev_state))
256
- self._update_stack()
257
- continue
298
+ else:
299
+ r = STRING_ESCAPE_MAP[x]
258
300
 
259
- if char == ')':
260
- self._match()
261
- self._update_stack()
262
- stack_item, prev_state = self._stack.pop()
263
- self._result = self._list_maker(prev_state)
264
- self._update_stack()
265
- continue
301
+ sb.write(r)
266
302
 
267
- if char == '[':
268
- self._match()
269
- self._update_stack()
270
- self._stack.append((ListParser._StackItem.VECTOR, []))
271
- continue
303
+ return sb.getvalue()
272
304
 
273
- if char == '(':
274
- self._match()
275
- self._update_stack()
276
- self._stack.append((ListParser._StackItem.LIST, []))
277
- continue
305
+ def _parse_char(self, tok: Token) -> ta.Any:
306
+ check.state(tok.kind == 'CHAR')
307
+ src = tok.src
308
+ check.state(len(src) > 1)
309
+ check.state(src.startswith('\\'))
278
310
 
279
- state_plus_char = self._state + char
280
- if state_plus_char == '#_':
281
- self._stack.append((ListParser._StackItem.TAG, char))
282
- self._result = self._UNDEFINED
283
- self._state = ''
284
- continue
311
+ if len(src) == 2:
312
+ c = src[1]
285
313
 
286
- if state_plus_char.endswith('#{'):
287
- self._state = self._state[:-1] # Remove the '#'
288
- self._match()
289
- self._update_stack()
290
- self._stack.append((ListParser._StackItem.SET, []))
291
- self._state = ''
292
- continue
314
+ elif src[1] == 'u':
315
+ check.state(len(src) == 6)
316
+ c = chr(int(src[2:], 16))
293
317
 
294
- if char == '{':
295
- self._match()
296
- self._update_stack()
297
- self._stack.append((ListParser._StackItem.MAP, [[], []]))
298
- self._state = ''
299
- continue
318
+ elif src[1] == 'o':
319
+ # \oXXX -> octal
320
+ raise NotImplementedError
300
321
 
301
- self._state += char
302
- continue
322
+ else:
323
+ c = CHAR_ESCAPE_MAP[src[1:]]
303
324
 
304
- elif self._mode == ListParser._ParseMode.STRING: # noqa
305
- if char == '\\':
306
- self._stack.append((self._mode, self._state))
307
- self._mode = ListParser._ParseMode.ESCAPE
308
- self._state = ''
309
- continue
325
+ return self._char_maker(c)
310
326
 
311
- if char == '"':
312
- self._mode = ListParser._ParseMode.IDLE
313
- self._result = self._state
314
- self._update_stack()
315
- self._state = ''
316
- continue
327
+ _INT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)')
328
+ _BIGINT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)N')
329
+ _FLOAT_PAT = re.compile(r'[-+]?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?(0|[1-9][0-9]*))?M?')
317
330
 
318
- self._state += char
331
+ def _parse_word(self, tok: Token) -> ta.Any:
332
+ check.state(tok.kind == 'WORD')
333
+ src = tok.src
334
+ check.non_empty_str(src)
335
+ check.state(not src.startswith('#'))
319
336
 
320
- elif self._mode == ListParser._ParseMode.ESCAPE:
321
- # TODO what should happen when escaping other char
322
- escaped_char = self._STRING_ESCAPE_MAP.get(char, char)
323
- stack_item, prev_state = self._stack.pop()
324
- self._mode = check.isinstance(stack_item, ListParser._ParseMode)
325
- self._state = prev_state + escaped_char
337
+ if src in WORD_CONST_VALUES:
338
+ return WORD_CONST_VALUES[src]
326
339
 
327
- elif self._mode == ListParser._ParseMode.COMMENT:
328
- if char == '\n':
329
- self._mode = ListParser._ParseMode.IDLE
340
+ elif src.startswith(':'):
341
+ return self._keyword_maker(src[1:])
330
342
 
331
- else:
332
- raise RuntimeError(self._mode)
343
+ elif self._INT_PAT.fullmatch(src):
344
+ # FIXME: numbers lol
345
+ # 2r101010, 052, 8r52, 0x2a, 36r16, and 42 are all the same Long.
346
+ # Floating point numbers are read as Doubles; with M suffix they are read as BigDecimals.
347
+ # Ratios are supported, e.g. 22/7.
348
+ return int(src)
333
349
 
334
- if i >= 0:
335
- self._match()
336
- self._update_stack()
350
+ elif self._BIGINT_PAT.fullmatch(src):
351
+ return int(src[:-1])
337
352
 
338
- check.state(not self._stack)
353
+ elif self._FLOAT_PAT.fullmatch(src):
354
+ return float(src)
339
355
 
340
- if self._result is not self._UNDEFINED:
341
- values.append(self._result)
342
- return values
356
+ else:
357
+ return self._symbol_maker(src)
343
358
 
344
359
 
345
- #
360
+ ##
346
361
 
347
362
 
348
363
  def parse_list(src: str, **kwargs: ta.Any) -> list[ta.Any]:
349
- """Parse an edn string and return the corresponding Python object."""
350
-
351
- parser = ListParser(**kwargs)
352
- return parser.parse(src)
364
+ r: list[ta.Any] = []
365
+ with StreamLexer() as l:
366
+ with StreamParser(**kwargs) as p:
367
+ for c in [*src, '']:
368
+ for t in l(c):
369
+ for o in p(t):
370
+ r.append(o) # noqa
371
+ return r
353
372
 
354
373
 
355
374
  def parse(src: str, **kwargs: ta.Any) -> ta.Any | None:
@@ -357,3 +376,25 @@ def parse(src: str, **kwargs: ta.Any) -> ta.Any | None:
357
376
  if not values:
358
377
  return None
359
378
  return check.single(values)
379
+
380
+
381
+ ##
382
+
383
+
384
+ def test_parse():
385
+ for s in [
386
+ '"abc"',
387
+ '"a\\bc"',
388
+ '{"a" "b"}',
389
+ '1',
390
+ '-1',
391
+ '{a :b c 420}',
392
+ '#{a}',
393
+ '(1 #_ 2 3)',
394
+ '"foo\u1234bar"',
395
+ '\\x',
396
+ '\\u1234',
397
+ ]:
398
+ print(s)
399
+ print(parse(s))
400
+ print()
@@ -15,7 +15,7 @@ _DEBUG = __debug__
15
15
 
16
16
  @dc.dataclass(frozen=True)
17
17
  class Value(lang.Abstract, lang.Sealed):
18
- pass
18
+ meta: ta.Any | None = dc.field(default=None, kw_only=True)
19
19
 
20
20
 
21
21
  #
@@ -87,8 +87,8 @@ class List(Collection, lang.Final):
87
87
  check.isinstance(self.items, tuple)
88
88
 
89
89
  @classmethod
90
- def new(cls, items: ta.Iterable[ta.Any]) -> 'List':
91
- return cls(tuple(items))
90
+ def new(cls, items: ta.Iterable[ta.Any], *, meta: ta.Any | None = None) -> 'List':
91
+ return cls(tuple(items), meta=meta)
92
92
 
93
93
 
94
94
  @dataclass_cache_hash()
@@ -104,8 +104,8 @@ class Vector(Collection, lang.Final):
104
104
  check.isinstance(self.items, tuple)
105
105
 
106
106
  @classmethod
107
- def new(cls, items: ta.Iterable[ta.Any]) -> 'Vector':
108
- return cls(tuple(items))
107
+ def new(cls, items: ta.Iterable[ta.Any], *, meta: ta.Any | None = None) -> 'Vector':
108
+ return cls(tuple(items), meta=meta)
109
109
 
110
110
 
111
111
  @dataclass_cache_hash()
@@ -121,8 +121,8 @@ class Set(Collection, lang.Final):
121
121
  check.isinstance(self.items, tuple)
122
122
 
123
123
  @classmethod
124
- def new(cls, items: ta.Iterable[ta.Any]) -> 'Set':
125
- return cls(tuple(items))
124
+ def new(cls, items: ta.Iterable[ta.Any], *, meta: ta.Any | None = None) -> 'Set':
125
+ return cls(tuple(items), meta=meta)
126
126
 
127
127
 
128
128
  @dataclass_cache_hash()
@@ -141,8 +141,8 @@ class Map(Collection, lang.Final):
141
141
  check.equal(len(t), 2)
142
142
 
143
143
  @classmethod
144
- def new(cls, items: ta.Iterable[ta.Iterable[ta.Any]]) -> 'Map':
145
- return cls(tuple((k, v) for k, v in items))
144
+ def new(cls, items: ta.Iterable[ta.Iterable[ta.Any]], *, meta: ta.Any | None = None) -> 'Map':
145
+ return cls(tuple((k, v) for k, v in items), meta=meta)
146
146
 
147
147
 
148
148
  #
@@ -150,7 +150,7 @@ class Map(Collection, lang.Final):
150
150
 
151
151
  @dataclass_cache_hash()
152
152
  @dc.dataclass(frozen=True)
153
- class TaggedVal(Value, lang.Final):
153
+ class Tagged(Value, lang.Final):
154
154
  t: str
155
155
  v: ta.Any
156
156
 
@@ -35,11 +35,11 @@ from .json import ( # noqa
35
35
  )
36
36
 
37
37
  if _ta.TYPE_CHECKING:
38
- from .render import ( # noqa
38
+ from .rendering import ( # noqa
39
39
  JsonRenderer,
40
40
  )
41
41
  else:
42
- _lang.proxy_init(globals(), '.render', [
42
+ _lang.proxy_init(globals(), '.rendering', [
43
43
  'JsonRenderer',
44
44
  ])
45
45
 
@@ -1,13 +1,13 @@
1
1
  import typing as ta
2
2
 
3
- from .lex import SCALAR_VALUE_TYPES
4
- from .parse import BeginArray
5
- from .parse import BeginObject
6
- from .parse import EndArray
7
- from .parse import EndObject
8
- from .parse import JsonStreamObject
9
- from .parse import JsonStreamParserEvent
10
- from .parse import Key
3
+ from .lexing import SCALAR_VALUE_TYPES
4
+ from .parsing import BeginArray
5
+ from .parsing import BeginObject
6
+ from .parsing import EndArray
7
+ from .parsing import EndObject
8
+ from .parsing import JsonStreamObject
9
+ from .parsing import JsonStreamParserEvent
10
+ from .parsing import Key
11
11
 
12
12
 
13
13
  ##
@@ -4,11 +4,11 @@ import typing as ta
4
4
  from .... import lang
5
5
  from ....funcs.genmachine import GenMachine
6
6
  from .errors import JsonStreamError
7
- from .lex import SCALAR_VALUE_TYPES
8
- from .lex import VALUE_TOKEN_KINDS
9
- from .lex import Position
10
- from .lex import ScalarValue
11
- from .lex import Token
7
+ from .lexing import SCALAR_VALUE_TYPES
8
+ from .lexing import VALUE_TOKEN_KINDS
9
+ from .lexing import Position
10
+ from .lexing import ScalarValue
11
+ from .lexing import Token
12
12
 
13
13
 
14
14
  ##
@@ -1,14 +1,14 @@
1
1
  import io
2
2
  import typing as ta
3
3
 
4
- from ..render import AbstractJsonRenderer
4
+ from ..rendering import AbstractJsonRenderer
5
5
  from ..types import SCALAR_TYPES
6
- from .parse import BeginArray
7
- from .parse import BeginObject
8
- from .parse import EndArray
9
- from .parse import EndObject
10
- from .parse import JsonStreamParserEvent
11
- from .parse import Key
6
+ from .parsing import BeginArray
7
+ from .parsing import BeginObject
8
+ from .parsing import EndArray
9
+ from .parsing import EndObject
10
+ from .parsing import JsonStreamParserEvent
11
+ from .parsing import Key
12
12
 
13
13
 
14
14
  ##
@@ -2,9 +2,9 @@ import dataclasses as dc
2
2
  import typing as ta
3
3
 
4
4
  from .... import lang
5
- from .build import JsonObjectBuilder
6
- from .lex import JsonStreamLexer
7
- from .parse import JsonStreamParser
5
+ from .building import JsonObjectBuilder
6
+ from .lexing import JsonStreamLexer
7
+ from .parsing import JsonStreamParser
8
8
 
9
9
 
10
10
  ##
@@ -34,7 +34,10 @@ class GenMachine(ta.Generic[I, O]):
34
34
  if initial is None:
35
35
  raise TypeError('No initial state')
36
36
 
37
- self._advance(initial)
37
+ self._gen = initial
38
+
39
+ if (n := next(self._gen)) is not None: # noqa
40
+ raise GenMachine.NotStartedError
38
41
 
39
42
  def _initial_state(self) -> MachineGen | None:
40
43
  return None
@@ -74,33 +77,34 @@ class GenMachine(ta.Generic[I, O]):
74
77
  class Error(Exception):
75
78
  pass
76
79
 
77
- class ClosedError(Exception):
80
+ class NotStartedError(Error):
78
81
  pass
79
82
 
80
- class StateError(Exception):
83
+ class ClosedError(Error):
81
84
  pass
82
85
 
83
- #
84
-
85
- def _advance(self, gen: MachineGen) -> None:
86
- self._gen = gen
86
+ class StateError(Error):
87
+ pass
87
88
 
88
- if (n := next(self._gen)) is not None: # noqa
89
- raise GenMachine.ClosedError
89
+ #
90
90
 
91
91
  def __call__(self, i: I) -> ta.Iterable[O]:
92
92
  if self._gen is None:
93
93
  raise GenMachine.ClosedError
94
94
 
95
95
  gi: I | None = i
96
- try:
97
- while (o := self._gen.send(gi)) is not None:
98
- gi = None
99
- yield from o
96
+ while True:
97
+ try:
98
+ while (o := self._gen.send(gi)) is not None: # type: ignore[union-attr]
99
+ gi = None
100
+ yield from o
100
101
 
101
- except StopIteration as s:
102
- if s.value is None:
103
- self._gen = None
104
- return None
102
+ break
105
103
 
106
- self._advance(s.value)
104
+ except StopIteration as s:
105
+ if (sv := s.value) is None:
106
+ self._gen = None
107
+ return None
108
+
109
+ self._gen = sv
110
+ gi = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish
3
- Version: 0.0.0.dev306
3
+ Version: 0.0.0.dev307
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License: BSD-3-Clause
@@ -1,5 +1,5 @@
1
1
  omlish/.manifests.json,sha256=orgsRvtpHu8tdhaCvlP9v3P495OJopYYiHKjK68WtWg,8587
2
- omlish/__about__.py,sha256=GhIrDhTYihc0evLBX7furXS9SiYupCmrYNe-MrlOL0w,3478
2
+ omlish/__about__.py,sha256=YdK-L7JBeMEmm3Dl7hWjoFj3BouojWQPvOl_zSLUgMA,3478
3
3
  omlish/__init__.py,sha256=SsyiITTuK0v74XpKV8dqNaCmjOlan1JZKrHQv5rWKPA,253
4
4
  omlish/c3.py,sha256=rer-TPOFDU6fYq_AWio_AmA-ckZ8JDY5shIzQ_yXfzA,8414
5
5
  omlish/cached.py,sha256=MLap_p0rdGoDIMVhXVHm1tsbcWobJF0OanoodV03Ju8,542
@@ -319,21 +319,21 @@ omlish/formats/props.py,sha256=auCv-Jx79KGlWfyG1-Qo0ou-Ex0W_mF3r_lDFdsVkWI,18920
319
319
  omlish/formats/repr.py,sha256=kYrNs4o-ji8nOdp6u_L3aMgBMWN1ZAZJSAWgQQfStSQ,414
320
320
  omlish/formats/xml.py,sha256=VJfqHR60dhAtjeG8WXFMozFqesTBSGvv264d67eDFXc,3514
321
321
  omlish/formats/yaml.py,sha256=jGPQlTE0vSV-p0O7TJRNlf6o1uq4gx8PrHZe1ApJ_o8,7386
322
- omlish/formats/edn/LICENSE,sha256=EUHM_e21AO5QuuMYXhDE81wmeBxbxeEk_i8oToZhiJo,1078
323
- omlish/formats/edn/__init__.py,sha256=JXHN8RlPAl-l2OnOQOQgYO8-f2zrYy-bGS2uaHiD9x0,183
322
+ omlish/formats/edn/__init__.py,sha256=H3q5B-dibXvQV8pmuWizTo6Xk75M7M0M7VPCLt86rpo,195
324
323
  omlish/formats/edn/codec.py,sha256=k6-Ra3P3Rlv6JA69-jPLI4nCe5XVes_QJbcsj5DYzMM,454
325
- omlish/formats/edn/parsing.py,sha256=EtpFdeU1DPAh9pvPjEQSBwZJ9OESeonRkUFQdjmIh2Y,11212
326
- omlish/formats/edn/values.py,sha256=m2mDkX0BP63-e81cBbZnB-S5zPzc-J-tYrFzycqL7AI,3488
324
+ omlish/formats/edn/lexing.py,sha256=plwbFwHLOmrr5_QhmzvMTmTK55Ot0DxRgKY6ASSl_-Y,6966
325
+ omlish/formats/edn/parsing.py,sha256=rtoOnDo8TuTo_GAhrNoVHu7Ys7CboALdgOF2PevAJYk,10152
326
+ omlish/formats/edn/values.py,sha256=jf0g88KJIMALxcuH51SoaMWg1HqTUqc1ugldmyyXWoc,3707
327
327
  omlish/formats/ini/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
328
328
  omlish/formats/ini/codec.py,sha256=omuFg0kiDksv8rRlWd_v32ebzEcKlgmiPgGID3bRi2M,631
329
329
  omlish/formats/ini/sections.py,sha256=7wYyZdVTQbMPFpjQEACKJfAEPzUBrogINsrvFgxJoZ0,1015
330
- omlish/formats/json/__init__.py,sha256=1lEtsA5v3F34nlhnWCn2CtoC3cgDIubSsxGz5J9wkZs,780
330
+ omlish/formats/json/__init__.py,sha256=HXFv4VDTBhx0k5U4JYWZqxziBwzJoPX51QK3xIboT8U,786
331
331
  omlish/formats/json/codecs.py,sha256=E5KErfqsgGZq763ixXLT3qysbk5MIsypT92xG5aSaIs,796
332
332
  omlish/formats/json/consts.py,sha256=A0cTAGGLyjo-gcYIQrL4JIaardI0yPMhQoNmh42BaRg,387
333
333
  omlish/formats/json/encoding.py,sha256=O4iIWle7W_-RwpOvJNlqOfkbnDyiQHexV5Za4hlrFzw,497
334
334
  omlish/formats/json/json.py,sha256=Mdqv2vdMi7gp96eV0BIYH5UdWpjWfsh-tSMZeywG-08,331
335
335
  omlish/formats/json/literals.py,sha256=6ptwZyfTXodEtAjDnUhsx6XU3KRZWWYWKYtZ8T7rzsQ,5625
336
- omlish/formats/json/render.py,sha256=r6J5YKSzcxPg_RiG5idGqJ-AIZozJQ_Awj6W_oSGRjk,4555
336
+ omlish/formats/json/rendering.py,sha256=r6J5YKSzcxPg_RiG5idGqJ-AIZozJQ_Awj6W_oSGRjk,4555
337
337
  omlish/formats/json/types.py,sha256=ueO9-uOU2eVWowJf0LH1fHFLjZ6fTIZyq9qybcLQaiQ,147
338
338
  omlish/formats/json/backends/__init__.py,sha256=gnaNDCxy_KmmPUPDnjxO5_WjuWxLGbI9FYWx8ZJuQUU,97
339
339
  omlish/formats/json/backends/base.py,sha256=WqtyoM82pyM0NyqpPwndrebr1bUVU1QlpmVQNrcAO8c,1114
@@ -343,12 +343,12 @@ omlish/formats/json/backends/orjson.py,sha256=wR8pMGFtkhZGHcNVk7vNYUnv8lUapdK89p
343
343
  omlish/formats/json/backends/std.py,sha256=PM00Kh9ZR2XzollHMEvdo35Eml1N-zFfRW-LOCV5ftM,3085
344
344
  omlish/formats/json/backends/ujson.py,sha256=BNJCU4kluGHdqTUKLJEuHhE2m2TmqR7HEN289S0Eokg,2278
345
345
  omlish/formats/json/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
346
- omlish/formats/json/stream/build.py,sha256=EveVyYjc3lbVQRs8Y5uMGEKp0E_gJAUR5bd2ToSVnSU,2533
346
+ omlish/formats/json/stream/building.py,sha256=SGbExmaerqOEiNSom2AERlpyXTj4dpM0QbMW-2WWM2o,2550
347
347
  omlish/formats/json/stream/errors.py,sha256=c8M8UAYmIZ-vWZLeKD2jMj4EDCJbr9QR8Jq_DyHjujQ,43
348
- omlish/formats/json/stream/lex.py,sha256=ItsWvtl5SZH-HwQtPy8Cpf4nszqDzvUTdIOEmSRiZ-E,6807
349
- omlish/formats/json/stream/parse.py,sha256=JuYmXwtTHmQJTFKoJNoEHUpCPxXdl_gvKPykVXgED34,6208
350
- omlish/formats/json/stream/render.py,sha256=NtmDsN92xZi5dkgSSuMeMXMAiJblmjz1arB4Ft7vBhc,3715
351
- omlish/formats/json/stream/utils.py,sha256=QASlxxQGscktsHrORlt8m9V3VWLDakP01QnsSPHLDQ8,1189
348
+ omlish/formats/json/stream/lexing.py,sha256=ItsWvtl5SZH-HwQtPy8Cpf4nszqDzvUTdIOEmSRiZ-E,6807
349
+ omlish/formats/json/stream/parsing.py,sha256=yvH5Llql3Ri_1xDhi1s9CKL6XLJVSQ8vYJ_dz3KAX-4,6223
350
+ omlish/formats/json/stream/rendering.py,sha256=uuJc__MR0G5kypYMAAudBNjBfiIzA_GGli-DWT90428,3730
351
+ omlish/formats/json/stream/utils.py,sha256=UhBRuWbb25wrdQWl8Ttq7xGRLoa329TvNdecGCZxgzg,1197
352
352
  omlish/formats/json5/Json5.g4,sha256=ZUmgJPvj8lSMUD_v3wijp10ZQExYB5mu5Q089dYEJSU,2389
353
353
  omlish/formats/json5/__init__.py,sha256=BsjPz5zJDji3GjQ8x8hWvcl1GYPV_ZIHnE3c2Sr8aTU,102
354
354
  omlish/formats/json5/codec.py,sha256=ldnxCRo0JP1fkGLt0mMxJlLvNxqIF_1KUCcSp1HtI-M,452
@@ -365,7 +365,7 @@ omlish/formats/toml/codec.py,sha256=5HFGWEPd9IFxPlRMRheX8FEDlRIzLe1moHEOj2_PFKU,
365
365
  omlish/formats/toml/parser.py,sha256=c6Hrf6OfVQVtgsYUXL5P5PQQqF-v7r8nkUmprxhV-lI,30536
366
366
  omlish/formats/toml/writer.py,sha256=HIp6XvriXaPTLqyLe-fkIiEf1Pyhsp0TcOg5rFBpO3g,3226
367
367
  omlish/funcs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
368
- omlish/funcs/genmachine.py,sha256=jvctDOJd3X-S2_8C83sgWYz2llYlyhHN7P19-WsanOs,2506
368
+ omlish/funcs/genmachine.py,sha256=8K5fNvFwxctZcVyulOqPLhZ_6Nwh8RAN6yawiE4wFio,2606
369
369
  omlish/funcs/match.py,sha256=gMLZn1enNiFvQaWrQubY300M1BrmdKWzeePihBS7Ywc,6153
370
370
  omlish/funcs/pairs.py,sha256=VCkZjDmJGtR76BsejsHNfb4TcpHCtkkmak-zWDFchAo,3904
371
371
  omlish/funcs/pipes.py,sha256=E7Sz8Aj8ke_vCs5AMNwg1I36kRdHVGTnzxVQaDyn43U,2490
@@ -852,9 +852,9 @@ omlish/typedvalues/holder.py,sha256=ZTnHiw-K38ciOBLEdwgrltr7Xp8jjEs_0Lp69DH-G-o,
852
852
  omlish/typedvalues/marshal.py,sha256=hWHRLcrGav7lvXJDtb9bNI0ickl4SKPQ6F4BbTpqw3A,4219
853
853
  omlish/typedvalues/reflect.py,sha256=Ih1YgU-srUjsvBn_P7C66f73_VCvcwqE3ffeBnZBgt4,674
854
854
  omlish/typedvalues/values.py,sha256=ym46I-q2QJ_6l4UlERqv3yj87R-kp8nCKMRph0xQ3UA,1307
855
- omlish-0.0.0.dev306.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
856
- omlish-0.0.0.dev306.dist-info/METADATA,sha256=GvMQ6j9pDWhBIlo-FffGTPzRwn6QSfgLdBRQ62RIshY,4416
857
- omlish-0.0.0.dev306.dist-info/WHEEL,sha256=wXxTzcEDnjrTwFYjLPcsW_7_XihufBwmpiBeiXNBGEA,91
858
- omlish-0.0.0.dev306.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
859
- omlish-0.0.0.dev306.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
860
- omlish-0.0.0.dev306.dist-info/RECORD,,
855
+ omlish-0.0.0.dev307.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
856
+ omlish-0.0.0.dev307.dist-info/METADATA,sha256=UgKZG7ukJ09NYqteFnD8DCdWblMH5cosVoyewHJxBTY,4416
857
+ omlish-0.0.0.dev307.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
858
+ omlish-0.0.0.dev307.dist-info/entry_points.txt,sha256=Lt84WvRZJskWCAS7xnQGZIeVWksprtUHj0llrvVmod8,35
859
+ omlish-0.0.0.dev307.dist-info/top_level.txt,sha256=pePsKdLu7DvtUiecdYXJ78iO80uDNmBlqe-8hOzOmfs,7
860
+ omlish-0.0.0.dev307.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.1.0)
2
+ Generator: setuptools (80.3.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,16 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2020 Jorin Vogel
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
6
- documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
7
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
8
- persons to whom the Software is furnished to do so, subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
11
- Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
14
- WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16
- OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
File without changes
File without changes