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.
Files changed (102) hide show
  1. omlish/__about__.py +2 -2
  2. omlish/dataclasses/impl/api/classes/make.py +1 -1
  3. omlish/dataclasses/tools/static.py +1 -1
  4. omlish/formats/json/stream/__init__.py +5 -3
  5. omlish/formats/json/stream/building.py +2 -2
  6. omlish/formats/json/stream/lexing.py +187 -42
  7. omlish/formats/json/stream/parsing.py +31 -9
  8. omlish/formats/json/stream/rendering.py +6 -6
  9. omlish/formats/json/stream/utils.py +106 -33
  10. omlish/formats/json5/literals.py +7 -4
  11. omlish/formats/json5/parsing.py +33 -79
  12. omlish/formats/json5/stream.py +45 -50
  13. omlish/http/all.py +59 -53
  14. omlish/inject/__init__.py +1 -0
  15. omlish/iterators/__init__.py +2 -0
  16. omlish/iterators/transforms.py +204 -0
  17. omlish/lang/classes/bindable.py +1 -1
  18. omlish/lang/classes/restrict.py +8 -0
  19. omlish/lite/inject.py +1 -0
  20. omlish/lite/marshal.py +1 -0
  21. omlish/reflect/types.py +2 -2
  22. omlish/sql/queries/_marshal.py +1 -1
  23. {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/METADATA +2 -2
  24. {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/RECORD +28 -101
  25. omlish/formats/json5/Json5.g4 +0 -168
  26. omlish/formats/json5/_antlr/Json5Lexer.py +0 -354
  27. omlish/formats/json5/_antlr/Json5Listener.py +0 -79
  28. omlish/formats/json5/_antlr/Json5Parser.py +0 -617
  29. omlish/formats/json5/_antlr/Json5Visitor.py +0 -52
  30. omlish/formats/json5/_antlr/__init__.py +0 -0
  31. omlish/text/antlr/__init__.py +0 -3
  32. omlish/text/antlr/_runtime/BufferedTokenStream.py +0 -305
  33. omlish/text/antlr/_runtime/CommonTokenFactory.py +0 -64
  34. omlish/text/antlr/_runtime/CommonTokenStream.py +0 -90
  35. omlish/text/antlr/_runtime/FileStream.py +0 -30
  36. omlish/text/antlr/_runtime/InputStream.py +0 -90
  37. omlish/text/antlr/_runtime/IntervalSet.py +0 -183
  38. omlish/text/antlr/_runtime/LICENSE.txt +0 -28
  39. omlish/text/antlr/_runtime/LL1Analyzer.py +0 -176
  40. omlish/text/antlr/_runtime/Lexer.py +0 -332
  41. omlish/text/antlr/_runtime/ListTokenSource.py +0 -147
  42. omlish/text/antlr/_runtime/Parser.py +0 -583
  43. omlish/text/antlr/_runtime/ParserInterpreter.py +0 -173
  44. omlish/text/antlr/_runtime/ParserRuleContext.py +0 -189
  45. omlish/text/antlr/_runtime/PredictionContext.py +0 -632
  46. omlish/text/antlr/_runtime/Recognizer.py +0 -150
  47. omlish/text/antlr/_runtime/RuleContext.py +0 -230
  48. omlish/text/antlr/_runtime/StdinStream.py +0 -14
  49. omlish/text/antlr/_runtime/Token.py +0 -158
  50. omlish/text/antlr/_runtime/TokenStreamRewriter.py +0 -258
  51. omlish/text/antlr/_runtime/Utils.py +0 -36
  52. omlish/text/antlr/_runtime/__init__.py +0 -2
  53. omlish/text/antlr/_runtime/_all.py +0 -24
  54. omlish/text/antlr/_runtime/_pygrun.py +0 -174
  55. omlish/text/antlr/_runtime/atn/ATN.py +0 -135
  56. omlish/text/antlr/_runtime/atn/ATNConfig.py +0 -162
  57. omlish/text/antlr/_runtime/atn/ATNConfigSet.py +0 -215
  58. omlish/text/antlr/_runtime/atn/ATNDeserializationOptions.py +0 -27
  59. omlish/text/antlr/_runtime/atn/ATNDeserializer.py +0 -449
  60. omlish/text/antlr/_runtime/atn/ATNSimulator.py +0 -50
  61. omlish/text/antlr/_runtime/atn/ATNState.py +0 -267
  62. omlish/text/antlr/_runtime/atn/ATNType.py +0 -20
  63. omlish/text/antlr/_runtime/atn/LexerATNSimulator.py +0 -573
  64. omlish/text/antlr/_runtime/atn/LexerAction.py +0 -301
  65. omlish/text/antlr/_runtime/atn/LexerActionExecutor.py +0 -146
  66. omlish/text/antlr/_runtime/atn/ParserATNSimulator.py +0 -1664
  67. omlish/text/antlr/_runtime/atn/PredictionMode.py +0 -502
  68. omlish/text/antlr/_runtime/atn/SemanticContext.py +0 -333
  69. omlish/text/antlr/_runtime/atn/Transition.py +0 -271
  70. omlish/text/antlr/_runtime/atn/__init__.py +0 -4
  71. omlish/text/antlr/_runtime/dfa/DFA.py +0 -136
  72. omlish/text/antlr/_runtime/dfa/DFASerializer.py +0 -76
  73. omlish/text/antlr/_runtime/dfa/DFAState.py +0 -129
  74. omlish/text/antlr/_runtime/dfa/__init__.py +0 -4
  75. omlish/text/antlr/_runtime/error/DiagnosticErrorListener.py +0 -111
  76. omlish/text/antlr/_runtime/error/ErrorListener.py +0 -75
  77. omlish/text/antlr/_runtime/error/ErrorStrategy.py +0 -712
  78. omlish/text/antlr/_runtime/error/Errors.py +0 -176
  79. omlish/text/antlr/_runtime/error/__init__.py +0 -4
  80. omlish/text/antlr/_runtime/tree/Chunk.py +0 -33
  81. omlish/text/antlr/_runtime/tree/ParseTreeMatch.py +0 -121
  82. omlish/text/antlr/_runtime/tree/ParseTreePattern.py +0 -75
  83. omlish/text/antlr/_runtime/tree/ParseTreePatternMatcher.py +0 -377
  84. omlish/text/antlr/_runtime/tree/RuleTagToken.py +0 -53
  85. omlish/text/antlr/_runtime/tree/TokenTagToken.py +0 -50
  86. omlish/text/antlr/_runtime/tree/Tree.py +0 -194
  87. omlish/text/antlr/_runtime/tree/Trees.py +0 -114
  88. omlish/text/antlr/_runtime/tree/__init__.py +0 -2
  89. omlish/text/antlr/_runtime/xpath/XPath.py +0 -278
  90. omlish/text/antlr/_runtime/xpath/XPathLexer.py +0 -98
  91. omlish/text/antlr/_runtime/xpath/__init__.py +0 -4
  92. omlish/text/antlr/delimit.py +0 -109
  93. omlish/text/antlr/dot.py +0 -41
  94. omlish/text/antlr/errors.py +0 -14
  95. omlish/text/antlr/input.py +0 -96
  96. omlish/text/antlr/parsing.py +0 -54
  97. omlish/text/antlr/runtime.py +0 -102
  98. omlish/text/antlr/utils.py +0 -38
  99. {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/WHEEL +0 -0
  100. {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/entry_points.txt +0 -0
  101. {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/licenses/LICENSE +0 -0
  102. {omlish-0.0.0.dev437.dist-info → omlish-0.0.0.dev439.dist-info}/top_level.txt +0 -0
omlish/__about__.py CHANGED
@@ -1,5 +1,5 @@
1
- __version__ = '0.0.0.dev437'
2
- __revision__ = '6e7eba22d4b3eb0dc36e24d1612b485ffa1e3c00'
1
+ __version__ = '0.0.0.dev439'
2
+ __revision__ = '3f7c136d6cdb4f4c2e749cf404632d93585559e3'
3
3
 
4
4
 
5
5
  #
@@ -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__' # type: ignore # noqa
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__ # type: ignore[attr-defined]
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
- CONST_TOKENS,
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
- JsonStreamParserEvent,
39
- JsonStreamParserEvents,
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: JsonStreamParserEvent) -> ta.Iterable[ta.Any]:
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
- CONST_TOKENS: ta.Mapping[str, tuple[TokenKind, str | float | None]] = {
97
- 'NaN': ('SPECIAL_NUMBER', float('nan')),
98
- 'Infinity': ('SPECIAL_NUMBER', float('inf')),
99
- '-Infinity': ('SPECIAL_NUMBER', float('-inf')),
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': ('BOOLEAN', True),
102
- 'false': ('BOOLEAN', False),
103
- 'null': ('NULL', None),
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 not NUMBER_PAT.fullmatch(raw):
281
- # Can only be -Infinity
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
- if not c:
284
- self._raise('Unexpected end of input')
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
- raw += c
287
- try:
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
- if raw != '-Infinity':
294
- self._raise(f'Invalid number format: {raw}')
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
- tk, tv = CONST_TOKENS[raw]
297
- yield self._make_tok(tk, tv, raw, pos)
362
+ if raw[0] == '+':
363
+ raw = raw[1:]
298
364
 
299
- return self._do_main()
365
+ yield self._make_tok('IDENT', raw, raw, pos)
366
+
367
+ return self._do_main()
300
368
 
301
369
  #
302
370
 
303
- if '.' in raw or 'e' in raw or 'E' in raw:
304
- nv = float(raw)
371
+ nv: ta.Any
372
+
373
+ if (np := self._number_literal_parser) is not None:
374
+ nv = np(raw)
375
+
305
376
  else:
306
- nv = int(raw)
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 CONST_TOKENS:
403
+ if raw in CONST_IDENT_VALUES:
326
404
  break
327
405
 
328
- if len(raw) > 8: # None of the keywords are longer than 8 characters
406
+ if len(raw) > MAX_CONST_IDENT_LEN:
329
407
  self._raise(f'Invalid literal: {raw}')
330
408
 
331
- tk, tv = CONST_TOKENS[raw]
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
- JsonStreamParserEvent: ta.TypeAlias = ta.Union[ # noqa
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 JsonStreamParserEvents(lang.Namespace):
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.Generator[JsonStreamParserEvent]:
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, JsonStreamParserEvent]):
98
- def __init__(self) -> None:
99
- super().__init__(self._do_value())
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=True)
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=True)
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 JsonStreamParserEvent
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[JsonStreamParserEvent]]):
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.Generator[str]:
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: JsonStreamParserEvent) -> ta.Generator[str]:
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[JsonStreamParserEvent]) -> ta.Generator[str]:
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[JsonStreamParserEvent], /, **kwargs: ta.Any) -> str:
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)