omlish 0.0.0.dev437__py3-none-any.whl → 0.0.0.dev439__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 +2 -2
- omlish/dataclasses/impl/api/classes/make.py +1 -1
- omlish/dataclasses/tools/static.py +1 -1
- omlish/formats/json/stream/__init__.py +5 -3
- omlish/formats/json/stream/building.py +2 -2
- omlish/formats/json/stream/lexing.py +187 -42
- omlish/formats/json/stream/parsing.py +31 -9
- omlish/formats/json/stream/rendering.py +6 -6
- omlish/formats/json/stream/utils.py +106 -33
- omlish/formats/json5/literals.py +7 -4
- omlish/formats/json5/parsing.py +33 -79
- omlish/formats/json5/stream.py +45 -50
- omlish/http/all.py +59 -53
- omlish/inject/__init__.py +1 -0
- omlish/iterators/__init__.py +2 -0
- omlish/iterators/transforms.py +204 -0
- omlish/lang/classes/bindable.py +1 -1
- omlish/lang/classes/restrict.py +8 -0
- omlish/lite/inject.py +1 -0
- omlish/lite/marshal.py +1 -0
- omlish/reflect/types.py +2 -2
- omlish/sql/queries/_marshal.py +1 -1
- {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/METADATA +2 -2
- {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/RECORD +28 -101
- omlish/formats/json5/Json5.g4 +0 -168
- omlish/formats/json5/_antlr/Json5Lexer.py +0 -354
- omlish/formats/json5/_antlr/Json5Listener.py +0 -79
- omlish/formats/json5/_antlr/Json5Parser.py +0 -617
- omlish/formats/json5/_antlr/Json5Visitor.py +0 -52
- omlish/formats/json5/_antlr/__init__.py +0 -0
- omlish/text/antlr/__init__.py +0 -3
- omlish/text/antlr/_runtime/BufferedTokenStream.py +0 -305
- omlish/text/antlr/_runtime/CommonTokenFactory.py +0 -64
- omlish/text/antlr/_runtime/CommonTokenStream.py +0 -90
- omlish/text/antlr/_runtime/FileStream.py +0 -30
- omlish/text/antlr/_runtime/InputStream.py +0 -90
- omlish/text/antlr/_runtime/IntervalSet.py +0 -183
- omlish/text/antlr/_runtime/LICENSE.txt +0 -28
- omlish/text/antlr/_runtime/LL1Analyzer.py +0 -176
- omlish/text/antlr/_runtime/Lexer.py +0 -332
- omlish/text/antlr/_runtime/ListTokenSource.py +0 -147
- omlish/text/antlr/_runtime/Parser.py +0 -583
- omlish/text/antlr/_runtime/ParserInterpreter.py +0 -173
- omlish/text/antlr/_runtime/ParserRuleContext.py +0 -189
- omlish/text/antlr/_runtime/PredictionContext.py +0 -632
- omlish/text/antlr/_runtime/Recognizer.py +0 -150
- omlish/text/antlr/_runtime/RuleContext.py +0 -230
- omlish/text/antlr/_runtime/StdinStream.py +0 -14
- omlish/text/antlr/_runtime/Token.py +0 -158
- omlish/text/antlr/_runtime/TokenStreamRewriter.py +0 -258
- omlish/text/antlr/_runtime/Utils.py +0 -36
- omlish/text/antlr/_runtime/__init__.py +0 -2
- omlish/text/antlr/_runtime/_all.py +0 -24
- omlish/text/antlr/_runtime/_pygrun.py +0 -174
- omlish/text/antlr/_runtime/atn/ATN.py +0 -135
- omlish/text/antlr/_runtime/atn/ATNConfig.py +0 -162
- omlish/text/antlr/_runtime/atn/ATNConfigSet.py +0 -215
- omlish/text/antlr/_runtime/atn/ATNDeserializationOptions.py +0 -27
- omlish/text/antlr/_runtime/atn/ATNDeserializer.py +0 -449
- omlish/text/antlr/_runtime/atn/ATNSimulator.py +0 -50
- omlish/text/antlr/_runtime/atn/ATNState.py +0 -267
- omlish/text/antlr/_runtime/atn/ATNType.py +0 -20
- omlish/text/antlr/_runtime/atn/LexerATNSimulator.py +0 -573
- omlish/text/antlr/_runtime/atn/LexerAction.py +0 -301
- omlish/text/antlr/_runtime/atn/LexerActionExecutor.py +0 -146
- omlish/text/antlr/_runtime/atn/ParserATNSimulator.py +0 -1664
- omlish/text/antlr/_runtime/atn/PredictionMode.py +0 -502
- omlish/text/antlr/_runtime/atn/SemanticContext.py +0 -333
- omlish/text/antlr/_runtime/atn/Transition.py +0 -271
- omlish/text/antlr/_runtime/atn/__init__.py +0 -4
- omlish/text/antlr/_runtime/dfa/DFA.py +0 -136
- omlish/text/antlr/_runtime/dfa/DFASerializer.py +0 -76
- omlish/text/antlr/_runtime/dfa/DFAState.py +0 -129
- omlish/text/antlr/_runtime/dfa/__init__.py +0 -4
- omlish/text/antlr/_runtime/error/DiagnosticErrorListener.py +0 -111
- omlish/text/antlr/_runtime/error/ErrorListener.py +0 -75
- omlish/text/antlr/_runtime/error/ErrorStrategy.py +0 -712
- omlish/text/antlr/_runtime/error/Errors.py +0 -176
- omlish/text/antlr/_runtime/error/__init__.py +0 -4
- omlish/text/antlr/_runtime/tree/Chunk.py +0 -33
- omlish/text/antlr/_runtime/tree/ParseTreeMatch.py +0 -121
- omlish/text/antlr/_runtime/tree/ParseTreePattern.py +0 -75
- omlish/text/antlr/_runtime/tree/ParseTreePatternMatcher.py +0 -377
- omlish/text/antlr/_runtime/tree/RuleTagToken.py +0 -53
- omlish/text/antlr/_runtime/tree/TokenTagToken.py +0 -50
- omlish/text/antlr/_runtime/tree/Tree.py +0 -194
- omlish/text/antlr/_runtime/tree/Trees.py +0 -114
- omlish/text/antlr/_runtime/tree/__init__.py +0 -2
- omlish/text/antlr/_runtime/xpath/XPath.py +0 -278
- omlish/text/antlr/_runtime/xpath/XPathLexer.py +0 -98
- omlish/text/antlr/_runtime/xpath/__init__.py +0 -4
- omlish/text/antlr/delimit.py +0 -109
- omlish/text/antlr/dot.py +0 -41
- omlish/text/antlr/errors.py +0 -14
- omlish/text/antlr/input.py +0 -96
- omlish/text/antlr/parsing.py +0 -54
- omlish/text/antlr/runtime.py +0 -102
- omlish/text/antlr/utils.py +0 -38
- {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/top_level.txt +0 -0
omlish/__about__.py
CHANGED
@@ -142,7 +142,7 @@ def make_dataclass( # noqa
|
|
142
142
|
|
143
143
|
if module is None:
|
144
144
|
try:
|
145
|
-
module = sys._getframemodulename(_frame_offset) or '__main__' #
|
145
|
+
module = sys._getframemodulename(_frame_offset) or '__main__' # noqa
|
146
146
|
except AttributeError:
|
147
147
|
with contextlib.suppress(AttributeError, ValueError):
|
148
148
|
module = sys._getframe(_frame_offset).f_globals.get('__name__', '__main__') # noqa
|
@@ -142,7 +142,7 @@ class Static(lang.Abstract):
|
|
142
142
|
n_md[_ExtraFieldParamsMetadata] = {
|
143
143
|
fs_f.name: getattr(x_fs, fs_f.name)
|
144
144
|
for fs_f in dc.fields(FieldSpec) # noqa
|
145
|
-
if fs_f not in dc.Field.__slots__
|
145
|
+
if fs_f not in dc.Field.__slots__
|
146
146
|
and fs_f.name not in ('default', 'default_factory')
|
147
147
|
}
|
148
148
|
new_fld.metadata = n_md # type: ignore[assignment]
|
@@ -8,6 +8,7 @@ from .errors import ( # noqa
|
|
8
8
|
|
9
9
|
|
10
10
|
from .lexing import ( # noqa
|
11
|
+
IdentTokenKind,
|
11
12
|
ValueTokenKind,
|
12
13
|
VALUE_TOKEN_KINDS,
|
13
14
|
ControlTokenKind,
|
@@ -22,7 +23,7 @@ from .lexing import ( # noqa
|
|
22
23
|
Token,
|
23
24
|
|
24
25
|
CONTROL_TOKENS,
|
25
|
-
|
26
|
+
CONST_IDENT_VALUES,
|
26
27
|
|
27
28
|
JsonStreamLexError,
|
28
29
|
JsonStreamLexer,
|
@@ -35,8 +36,8 @@ from .parsing import ( # noqa
|
|
35
36
|
BeginArray,
|
36
37
|
EndArray,
|
37
38
|
|
38
|
-
|
39
|
-
|
39
|
+
Event,
|
40
|
+
Events,
|
40
41
|
|
41
42
|
yield_parser_events,
|
42
43
|
|
@@ -54,4 +55,5 @@ from .utils import ( # noqa
|
|
54
55
|
|
55
56
|
stream_parse_values,
|
56
57
|
stream_parse_one_value,
|
58
|
+
stream_parse_exactly_one_value,
|
57
59
|
)
|
@@ -5,8 +5,8 @@ from .parsing import BeginArray
|
|
5
5
|
from .parsing import BeginObject
|
6
6
|
from .parsing import EndArray
|
7
7
|
from .parsing import EndObject
|
8
|
+
from .parsing import Event
|
8
9
|
from .parsing import JsonStreamObject
|
9
|
-
from .parsing import JsonStreamParserEvent
|
10
10
|
from .parsing import Key
|
11
11
|
|
12
12
|
|
@@ -62,7 +62,7 @@ class JsonValueBuilder:
|
|
62
62
|
else:
|
63
63
|
raise self.StateError
|
64
64
|
|
65
|
-
def __call__(self, e:
|
65
|
+
def __call__(self, e: Event) -> ta.Iterable[ta.Any]:
|
66
66
|
stk = self._stack
|
67
67
|
|
68
68
|
#
|
@@ -13,20 +13,23 @@ import re
|
|
13
13
|
import typing as ta
|
14
14
|
|
15
15
|
from .... import check
|
16
|
+
from .... import lang
|
16
17
|
from ....funcs.genmachine import GenMachine
|
17
18
|
from .errors import JsonStreamError
|
18
19
|
|
19
20
|
|
21
|
+
with lang.auto_proxy_import(globals()):
|
22
|
+
import unicodedata
|
23
|
+
|
24
|
+
|
20
25
|
##
|
21
26
|
|
22
27
|
|
28
|
+
IdentTokenKind: ta.TypeAlias = ta.Literal['IDENT']
|
29
|
+
|
23
30
|
ValueTokenKind: ta.TypeAlias = ta.Literal[
|
24
31
|
'STRING',
|
25
32
|
'NUMBER',
|
26
|
-
|
27
|
-
'SPECIAL_NUMBER',
|
28
|
-
'BOOLEAN',
|
29
|
-
'NULL',
|
30
33
|
]
|
31
34
|
|
32
35
|
VALUE_TOKEN_KINDS = frozenset(check.isinstance(a, str) for a in ta.get_args(ValueTokenKind))
|
@@ -45,6 +48,7 @@ SpaceTokenKind: ta.TypeAlias = ta.Literal['SPACE']
|
|
45
48
|
CommentTokenKind: ta.TypeAlias = ta.Literal['COMMENT']
|
46
49
|
|
47
50
|
TokenKind: ta.TypeAlias = ta.Union[ # noqa
|
51
|
+
IdentTokenKind,
|
48
52
|
ValueTokenKind,
|
49
53
|
ControlTokenKind,
|
50
54
|
SpaceTokenKind,
|
@@ -93,16 +97,19 @@ CONTROL_TOKENS: ta.Mapping[str, TokenKind] = {
|
|
93
97
|
':': 'COLON',
|
94
98
|
}
|
95
99
|
|
96
|
-
|
97
|
-
'NaN':
|
98
|
-
'
|
99
|
-
'
|
100
|
+
CONST_IDENT_VALUES: ta.Mapping[str, str | float | None] = {
|
101
|
+
'NaN': float('nan'),
|
102
|
+
'-NaN': float('-nan'), # distinguished in parsing even if indistinguishable in value
|
103
|
+
'Infinity': float('inf'),
|
104
|
+
'-Infinity': float('-inf'),
|
100
105
|
|
101
|
-
'true':
|
102
|
-
'false':
|
103
|
-
'null':
|
106
|
+
'true': True,
|
107
|
+
'false': False,
|
108
|
+
'null': None,
|
104
109
|
}
|
105
110
|
|
111
|
+
MAX_CONST_IDENT_LEN = max(map(len, CONST_IDENT_VALUES))
|
112
|
+
|
106
113
|
|
107
114
|
##
|
108
115
|
|
@@ -119,21 +126,39 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
119
126
|
self,
|
120
127
|
*,
|
121
128
|
include_raw: bool = False,
|
129
|
+
|
130
|
+
allow_extended_space: bool = False,
|
122
131
|
include_space: bool = False,
|
132
|
+
|
123
133
|
allow_comments: bool = False,
|
124
134
|
include_comments: bool = False,
|
135
|
+
|
125
136
|
allow_single_quotes: bool = False,
|
126
137
|
string_literal_parser: ta.Callable[[str], str] | None = None,
|
138
|
+
|
139
|
+
allow_extended_number_literals: bool = False,
|
140
|
+
number_literal_parser: ta.Callable[[str], ta.Any] | None = None,
|
141
|
+
|
142
|
+
allow_extended_idents: bool = False,
|
127
143
|
) -> None:
|
128
144
|
self._include_raw = include_raw
|
145
|
+
|
146
|
+
self._allow_extended_space = allow_extended_space
|
129
147
|
self._include_space = include_space
|
148
|
+
|
130
149
|
self._allow_comments = allow_comments
|
131
150
|
self._include_comments = include_comments
|
151
|
+
|
132
152
|
self._allow_single_quotes = allow_single_quotes
|
133
153
|
if string_literal_parser is None:
|
134
|
-
string_literal_parser = json.loads
|
154
|
+
string_literal_parser = json.loads # noqa
|
135
155
|
self._string_literal_parser = string_literal_parser
|
136
156
|
|
157
|
+
self._allow_extended_number_literals = allow_extended_number_literals
|
158
|
+
self._number_literal_parser = number_literal_parser
|
159
|
+
|
160
|
+
self._allow_extended_idents = allow_extended_idents
|
161
|
+
|
137
162
|
self._ofs = 0
|
138
163
|
self._line = 1
|
139
164
|
self._col = 0
|
@@ -199,7 +224,33 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
199
224
|
if not c:
|
200
225
|
return None
|
201
226
|
|
202
|
-
if c.isspace()
|
227
|
+
if c.isspace() or (self._allow_extended_space and c in (
|
228
|
+
'\u0009'
|
229
|
+
'\u000A'
|
230
|
+
'\u000B'
|
231
|
+
'\u000C'
|
232
|
+
'\u000D'
|
233
|
+
'\u0020'
|
234
|
+
'\u00A0'
|
235
|
+
'\u2028'
|
236
|
+
'\u2029'
|
237
|
+
'\uFEFF'
|
238
|
+
'\u1680'
|
239
|
+
'\u2000'
|
240
|
+
'\u2001'
|
241
|
+
'\u2002'
|
242
|
+
'\u2003'
|
243
|
+
'\u2004'
|
244
|
+
'\u2005'
|
245
|
+
'\u2006'
|
246
|
+
'\u2007'
|
247
|
+
'\u2008'
|
248
|
+
'\u2009'
|
249
|
+
'\u200A'
|
250
|
+
'\u202F'
|
251
|
+
'\u205F'
|
252
|
+
'\u3000'
|
253
|
+
)):
|
203
254
|
if self._include_space:
|
204
255
|
yield self._make_tok('SPACE', c, c, self.pos)
|
205
256
|
continue
|
@@ -211,16 +262,18 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
211
262
|
if c == '"' or (self._allow_single_quotes and c == "'"):
|
212
263
|
return self._do_string(c)
|
213
264
|
|
214
|
-
if c.isdigit() or c == '-':
|
265
|
+
if c.isdigit() or c == '-' or (self._allow_extended_number_literals and c in '.+'):
|
215
266
|
return self._do_number(c)
|
216
267
|
|
268
|
+
if self._allow_comments and c == '/':
|
269
|
+
return self._do_comment()
|
270
|
+
|
271
|
+
if self._allow_extended_idents:
|
272
|
+
return self._do_extended_ident(c)
|
273
|
+
|
217
274
|
if c in 'tfnIN':
|
218
275
|
return self._do_const(c)
|
219
276
|
|
220
|
-
if self._allow_comments and c == '/':
|
221
|
-
yield from self._do_comment()
|
222
|
-
continue
|
223
|
-
|
224
277
|
self._raise(f'Unexpected character: {c}')
|
225
278
|
|
226
279
|
def _do_string(self, q: str):
|
@@ -269,7 +322,7 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
269
322
|
if not c:
|
270
323
|
break
|
271
324
|
|
272
|
-
if not (c.isdigit() or c in '.eE+-'):
|
325
|
+
if not (c.isdigit() or c in '.eE+-' or (self._allow_extended_number_literals and c in 'xXabcdefABCDEF')):
|
273
326
|
break
|
274
327
|
self._buf.write(c)
|
275
328
|
|
@@ -277,33 +330,58 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
277
330
|
|
278
331
|
#
|
279
332
|
|
280
|
-
if
|
281
|
-
|
333
|
+
if self._allow_extended_number_literals:
|
334
|
+
p = 1 if raw[0] in '+-' else 0
|
335
|
+
if (len(raw) - p) > 1 and raw[p] == '0' and raw[p + 1].isdigit():
|
336
|
+
self._raise('Invalid number literal')
|
282
337
|
|
283
|
-
|
284
|
-
|
338
|
+
if raw == '-' or (self._allow_extended_number_literals and raw == '+'):
|
339
|
+
for svs in [
|
340
|
+
'Infinity',
|
341
|
+
*(['NaN'] if self._allow_extended_number_literals else []),
|
342
|
+
]:
|
343
|
+
if c != svs[0]:
|
344
|
+
continue
|
285
345
|
|
286
|
-
|
287
|
-
|
288
|
-
for _ in range(7):
|
289
|
-
raw += self._char_in((yield None)) # noqa
|
290
|
-
except GeneratorExit:
|
291
|
-
self._raise('Unexpected end of input')
|
346
|
+
if not c:
|
347
|
+
self._raise('Unexpected end of input')
|
292
348
|
|
293
|
-
|
294
|
-
|
349
|
+
raw += c
|
350
|
+
try:
|
351
|
+
for _ in range(len(svs) - 1):
|
352
|
+
c = self._char_in((yield None)) # noqa
|
353
|
+
if not c:
|
354
|
+
break
|
355
|
+
raw += c
|
356
|
+
except GeneratorExit:
|
357
|
+
self._raise('Unexpected end of input')
|
358
|
+
|
359
|
+
if raw[1:] != svs:
|
360
|
+
self._raise(f'Invalid number format: {raw}')
|
295
361
|
|
296
|
-
|
297
|
-
|
362
|
+
if raw[0] == '+':
|
363
|
+
raw = raw[1:]
|
298
364
|
|
299
|
-
|
365
|
+
yield self._make_tok('IDENT', raw, raw, pos)
|
366
|
+
|
367
|
+
return self._do_main()
|
300
368
|
|
301
369
|
#
|
302
370
|
|
303
|
-
|
304
|
-
|
371
|
+
nv: ta.Any
|
372
|
+
|
373
|
+
if (np := self._number_literal_parser) is not None:
|
374
|
+
nv = np(raw)
|
375
|
+
|
305
376
|
else:
|
306
|
-
|
377
|
+
if not NUMBER_PAT.fullmatch(raw):
|
378
|
+
self._raise(f'Invalid number format: {raw}')
|
379
|
+
|
380
|
+
if '.' in raw or 'e' in raw or 'E' in raw:
|
381
|
+
nv = float(raw)
|
382
|
+
else:
|
383
|
+
nv = int(raw)
|
384
|
+
|
307
385
|
yield self._make_tok('NUMBER', nv, raw, pos)
|
308
386
|
|
309
387
|
#
|
@@ -322,17 +400,79 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
322
400
|
except GeneratorExit:
|
323
401
|
self._raise('Unexpected end of input')
|
324
402
|
|
325
|
-
if raw in
|
403
|
+
if raw in CONST_IDENT_VALUES:
|
326
404
|
break
|
327
405
|
|
328
|
-
if len(raw) >
|
406
|
+
if len(raw) > MAX_CONST_IDENT_LEN:
|
329
407
|
self._raise(f'Invalid literal: {raw}')
|
330
408
|
|
331
|
-
|
332
|
-
yield self._make_tok(tk, tv, raw, pos)
|
409
|
+
yield self._make_tok('IDENT', raw, raw, pos)
|
333
410
|
|
334
411
|
return self._do_main()
|
335
412
|
|
413
|
+
def _do_unicode_escape(self):
|
414
|
+
try:
|
415
|
+
c = self._char_in((yield None)) # noqa
|
416
|
+
except GeneratorExit:
|
417
|
+
self._raise('Unexpected end of input')
|
418
|
+
|
419
|
+
if c != 'u':
|
420
|
+
self._raise('Illegal identifier escape')
|
421
|
+
|
422
|
+
ux = []
|
423
|
+
for _ in range(4):
|
424
|
+
try:
|
425
|
+
c = self._char_in((yield None)) # noqa
|
426
|
+
except GeneratorExit:
|
427
|
+
self._raise('Unexpected end of input')
|
428
|
+
|
429
|
+
if c not in '0123456789abcdefABCDEF':
|
430
|
+
self._raise('Illegal identifier escape')
|
431
|
+
|
432
|
+
ux.append(c)
|
433
|
+
|
434
|
+
return chr(int(''.join(ux), 16))
|
435
|
+
|
436
|
+
def _do_extended_ident(self, c: str):
|
437
|
+
check.state(self._buf.tell() == 0)
|
438
|
+
|
439
|
+
if c == '\\':
|
440
|
+
c = yield from self._do_unicode_escape()
|
441
|
+
|
442
|
+
elif not (c in '$_' or unicodedata.category(c).startswith('L')):
|
443
|
+
self._raise('Illegal identifier start')
|
444
|
+
|
445
|
+
self._buf.write(c)
|
446
|
+
|
447
|
+
pos = self.pos
|
448
|
+
|
449
|
+
while True:
|
450
|
+
try:
|
451
|
+
c = self._char_in((yield None)) # noqa
|
452
|
+
except GeneratorExit:
|
453
|
+
self._raise('Unexpected end of input')
|
454
|
+
|
455
|
+
if c == '\\':
|
456
|
+
c = yield from self._do_unicode_escape()
|
457
|
+
self._buf.write(c)
|
458
|
+
continue
|
459
|
+
|
460
|
+
if not c:
|
461
|
+
break
|
462
|
+
|
463
|
+
if c not in '$_\u200C\u200D':
|
464
|
+
uc = unicodedata.category(c)
|
465
|
+
if not (uc.startswith(('L', 'M', 'N')) or uc == 'Pc'):
|
466
|
+
break
|
467
|
+
|
468
|
+
self._buf.write(c)
|
469
|
+
|
470
|
+
raw = self._flip_buf()
|
471
|
+
|
472
|
+
yield self._make_tok('IDENT', raw, raw, pos)
|
473
|
+
|
474
|
+
return self._do_main(c)
|
475
|
+
|
336
476
|
def _do_comment(self):
|
337
477
|
check.state(self._buf.tell() == 0)
|
338
478
|
|
@@ -349,7 +489,7 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
349
489
|
except GeneratorExit:
|
350
490
|
self._raise('Unexpected end of input')
|
351
491
|
|
352
|
-
if ic == '\n':
|
492
|
+
if not ic or ic == '\n':
|
353
493
|
break
|
354
494
|
|
355
495
|
if self._include_comments:
|
@@ -360,6 +500,9 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
360
500
|
raw = f'//{cmt}\n' if self._include_raw else None
|
361
501
|
yield self._make_tok('COMMENT', cmt, raw, pos)
|
362
502
|
|
503
|
+
if not ic:
|
504
|
+
return
|
505
|
+
|
363
506
|
elif oc == '*':
|
364
507
|
lc: str | None = None
|
365
508
|
while True:
|
@@ -382,3 +525,5 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
382
525
|
|
383
526
|
else:
|
384
527
|
self._raise(f'Unexpected character after comment start: {oc}')
|
528
|
+
|
529
|
+
return self._do_main()
|
@@ -4,6 +4,7 @@ import typing as ta
|
|
4
4
|
from .... import lang
|
5
5
|
from ....funcs.genmachine import GenMachine
|
6
6
|
from .errors import JsonStreamError
|
7
|
+
from .lexing import CONST_IDENT_VALUES
|
7
8
|
from .lexing import SCALAR_VALUE_TYPES
|
8
9
|
from .lexing import VALUE_TOKEN_KINDS
|
9
10
|
from .lexing import Position
|
@@ -34,7 +35,7 @@ class EndArray(lang.Marker):
|
|
34
35
|
pass
|
35
36
|
|
36
37
|
|
37
|
-
|
38
|
+
Event: ta.TypeAlias = ta.Union[ # noqa
|
38
39
|
type[BeginObject],
|
39
40
|
Key,
|
40
41
|
type[EndObject],
|
@@ -46,7 +47,7 @@ JsonStreamParserEvent: ta.TypeAlias = ta.Union[ # noqa
|
|
46
47
|
]
|
47
48
|
|
48
49
|
|
49
|
-
class
|
50
|
+
class Events(lang.Namespace):
|
50
51
|
BeginObject = BeginObject
|
51
52
|
Key = Key
|
52
53
|
EndObject = EndObject
|
@@ -58,7 +59,7 @@ class JsonStreamParserEvents(lang.Namespace):
|
|
58
59
|
##
|
59
60
|
|
60
61
|
|
61
|
-
def yield_parser_events(obj: ta.Any) -> ta.
|
62
|
+
def yield_parser_events(obj: ta.Any) -> ta.Iterator[Event]:
|
62
63
|
if isinstance(obj, SCALAR_VALUE_TYPES):
|
63
64
|
yield obj # type: ignore
|
64
65
|
|
@@ -94,12 +95,22 @@ class JsonStreamObject(list):
|
|
94
95
|
return f'{self.__class__.__name__}({super().__repr__()})'
|
95
96
|
|
96
97
|
|
97
|
-
class JsonStreamParser(GenMachine[Token,
|
98
|
-
def __init__(
|
99
|
-
|
98
|
+
class JsonStreamParser(GenMachine[Token, Event]):
|
99
|
+
def __init__(
|
100
|
+
self,
|
101
|
+
*,
|
102
|
+
allow_trailing_commas: bool = False,
|
103
|
+
|
104
|
+
allow_extended_idents: bool = False,
|
105
|
+
) -> None:
|
106
|
+
self._allow_trailing_commas = allow_trailing_commas
|
107
|
+
|
108
|
+
self._allow_extended_idents = allow_extended_idents
|
100
109
|
|
101
110
|
self._stack: list[ta.Literal['OBJECT', 'KEY', 'ARRAY']] = []
|
102
111
|
|
112
|
+
super().__init__(self._do_value())
|
113
|
+
|
103
114
|
#
|
104
115
|
|
105
116
|
def _next_tok(self):
|
@@ -144,12 +155,23 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
|
|
144
155
|
raise JsonStreamParseError('Expected value') from None
|
145
156
|
else:
|
146
157
|
raise
|
158
|
+
# except Exception as e:
|
159
|
+
# raise
|
147
160
|
|
148
161
|
if tok.kind in VALUE_TOKEN_KINDS:
|
149
162
|
y, r = self._emit_event(tok.value)
|
150
163
|
yield y
|
151
164
|
return r
|
152
165
|
|
166
|
+
elif tok.kind == 'IDENT':
|
167
|
+
try:
|
168
|
+
cv = CONST_IDENT_VALUES[tok.value]
|
169
|
+
except KeyError:
|
170
|
+
raise JsonStreamParseError('Expected value', tok.pos) from None
|
171
|
+
y, r = self._emit_event(cv)
|
172
|
+
yield y
|
173
|
+
return r
|
174
|
+
|
153
175
|
elif tok.kind == 'LBRACE':
|
154
176
|
y, r = self._emit_begin_object()
|
155
177
|
yield y
|
@@ -193,7 +215,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
|
|
193
215
|
except GeneratorExit:
|
194
216
|
raise JsonStreamParseError('Expected object body') from None
|
195
217
|
|
196
|
-
if tok.kind == 'STRING':
|
218
|
+
if tok.kind == 'STRING' or (self._allow_trailing_commas and tok.kind == 'IDENT'):
|
197
219
|
k = tok.value
|
198
220
|
|
199
221
|
try:
|
@@ -225,7 +247,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
|
|
225
247
|
raise JsonStreamParseError('Expected continuation') from None
|
226
248
|
|
227
249
|
if tok.kind == 'COMMA':
|
228
|
-
return self._do_object_body(must_be_present=
|
250
|
+
return self._do_object_body(must_be_present=not self._allow_trailing_commas)
|
229
251
|
|
230
252
|
elif tok.kind == 'RBRACE':
|
231
253
|
y, r = self._emit_end_object()
|
@@ -258,7 +280,7 @@ class JsonStreamParser(GenMachine[Token, JsonStreamParserEvent]):
|
|
258
280
|
raise JsonStreamParseError('Expected continuation') from None
|
259
281
|
|
260
282
|
if tok.kind == 'COMMA':
|
261
|
-
return self._do_value(must_be_present=
|
283
|
+
return self._do_value(must_be_present=not self._allow_trailing_commas)
|
262
284
|
|
263
285
|
elif tok.kind == 'RBRACKET':
|
264
286
|
y, r = self._emit_end_array()
|
@@ -7,14 +7,14 @@ from .parsing import BeginArray
|
|
7
7
|
from .parsing import BeginObject
|
8
8
|
from .parsing import EndArray
|
9
9
|
from .parsing import EndObject
|
10
|
-
from .parsing import
|
10
|
+
from .parsing import Event
|
11
11
|
from .parsing import Key
|
12
12
|
|
13
13
|
|
14
14
|
##
|
15
15
|
|
16
16
|
|
17
|
-
class StreamJsonRenderer(AbstractJsonRenderer[ta.Iterable[
|
17
|
+
class StreamJsonRenderer(AbstractJsonRenderer[ta.Iterable[Event]]):
|
18
18
|
def __init__(
|
19
19
|
self,
|
20
20
|
*,
|
@@ -36,7 +36,7 @@ class StreamJsonRenderer(AbstractJsonRenderer[ta.Iterable[JsonStreamParserEvent]
|
|
36
36
|
self,
|
37
37
|
o: ta.Any,
|
38
38
|
state: AbstractJsonRenderer.State = AbstractJsonRenderer.State.VALUE,
|
39
|
-
) -> ta.
|
39
|
+
) -> ta.Iterator[str]:
|
40
40
|
if self._style is not None:
|
41
41
|
pre, post = self._style(o, state)
|
42
42
|
yield pre
|
@@ -52,7 +52,7 @@ class StreamJsonRenderer(AbstractJsonRenderer[ta.Iterable[JsonStreamParserEvent]
|
|
52
52
|
if post:
|
53
53
|
yield post
|
54
54
|
|
55
|
-
def _render(self, e:
|
55
|
+
def _render(self, e: Event) -> ta.Iterator[str]:
|
56
56
|
if self._need_delimit:
|
57
57
|
yield self._delimiter
|
58
58
|
self._need_delimit = False
|
@@ -124,12 +124,12 @@ class StreamJsonRenderer(AbstractJsonRenderer[ta.Iterable[JsonStreamParserEvent]
|
|
124
124
|
else:
|
125
125
|
raise TypeError(e)
|
126
126
|
|
127
|
-
def render(self, events: ta.Iterable[
|
127
|
+
def render(self, events: ta.Iterable[Event]) -> ta.Iterator[str]:
|
128
128
|
for e in events:
|
129
129
|
yield from self._render(e)
|
130
130
|
|
131
131
|
@classmethod
|
132
|
-
def render_str(cls, i: ta.Iterable[
|
132
|
+
def render_str(cls, i: ta.Iterable[Event], /, **kwargs: ta.Any) -> str:
|
133
133
|
out = io.StringIO()
|
134
134
|
for s in cls(**kwargs).render(i):
|
135
135
|
out.write(s)
|