omextra 0.0.0.dev472__py3-none-any.whl → 0.0.0.dev485__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.
@@ -0,0 +1,998 @@
1
+ # ruff: noqa: UP006 UP007 UP043 UP045
2
+ # @omlish-lite
3
+ import copy
4
+ import dataclasses as dc
5
+ import datetime
6
+ import enum
7
+ import functools
8
+ import typing as ta
9
+
10
+ from omlish.lite.dataclasses import dataclass_field_required
11
+
12
+ from .errors import YamlError
13
+ from .errors import YamlErrorOr
14
+ from .errors import yaml_error
15
+
16
+
17
+ ##
18
+
19
+
20
+ class YamlChars:
21
+ # SEQUENCE_ENTRY character for sequence entry
22
+ SEQUENCE_ENTRY = '-'
23
+ # MAPPING_KEY character for mapping key
24
+ MAPPING_KEY = '?'
25
+ # MAPPING_VALUE character for mapping value
26
+ MAPPING_VALUE = ':'
27
+ # COLLECT_ENTRY character for collect entry
28
+ COLLECT_ENTRY = ','
29
+ # SEQUENCE_START character for sequence start
30
+ SEQUENCE_START = '['
31
+ # SEQUENCE_END character for sequence end
32
+ SEQUENCE_END = ']'
33
+ # MAPPING_START character for mapping start
34
+ MAPPING_START = '{'
35
+ # MAPPING_END character for mapping end
36
+ MAPPING_END = '}'
37
+ # COMMENT character for comment
38
+ COMMENT = '#'
39
+ # ANCHOR character for anchor
40
+ ANCHOR = '&'
41
+ # ALIAS character for alias
42
+ ALIAS = '*'
43
+ # TAG character for tag
44
+ TAG = '!'
45
+ # LITERAL character for literal
46
+ LITERAL = '|'
47
+ # FOLDED character for folded
48
+ FOLDED = '>'
49
+ # SINGLE_QUOTE character for single quote
50
+ SINGLE_QUOTE = '\''
51
+ # DOUBLE_QUOTE character for double quote
52
+ DOUBLE_QUOTE = '"'
53
+ # DIRECTIVE character for directive
54
+ DIRECTIVE = '%'
55
+ # SPACE character for space
56
+ SPACE = ' '
57
+ # LINE_BREAK character for line break
58
+ LINE_BREAK = '\n'
59
+
60
+
61
+ class YamlTokenType(enum.Enum):
62
+ # UNKNOWN reserve for invalid type
63
+ UNKNOWN = enum.auto()
64
+ # DOCUMENT_HEADER type for DocumentHeader token
65
+ DOCUMENT_HEADER = enum.auto()
66
+ # DOCUMENT_END type for DocumentEnd token
67
+ DOCUMENT_END = enum.auto()
68
+ # SEQUENCE_ENTRY type for SequenceEntry token
69
+ SEQUENCE_ENTRY = enum.auto()
70
+ # MAPPING_KEY type for MappingKey token
71
+ MAPPING_KEY = enum.auto()
72
+ # MAPPING_VALUE type for MappingValue token
73
+ MAPPING_VALUE = enum.auto()
74
+ # MERGE_KEY type for MergeKey token
75
+ MERGE_KEY = enum.auto()
76
+ # COLLECT_ENTRY type for CollectEntry token
77
+ COLLECT_ENTRY = enum.auto()
78
+ # SEQUENCE_START type for SequenceStart token
79
+ SEQUENCE_START = enum.auto()
80
+ # SEQUENCE_END type for SequenceEnd token
81
+ SEQUENCE_END = enum.auto()
82
+ # MAPPING_START type for MappingStart token
83
+ MAPPING_START = enum.auto()
84
+ # MAPPING_END type for MappingEnd token
85
+ MAPPING_END = enum.auto()
86
+ # COMMENT type for Comment token
87
+ COMMENT = enum.auto()
88
+ # ANCHOR type for Anchor token
89
+ ANCHOR = enum.auto()
90
+ # ALIAS type for Alias token
91
+ ALIAS = enum.auto()
92
+ # TAG type for Tag token
93
+ TAG = enum.auto()
94
+ # LITERAL type for Literal token
95
+ LITERAL = enum.auto()
96
+ # FOLDED type for Folded token
97
+ FOLDED = enum.auto()
98
+ # SINGLE_QUOTE type for SingleQuote token
99
+ SINGLE_QUOTE = enum.auto()
100
+ # DOUBLE_QUOTE type for DoubleQuote token
101
+ DOUBLE_QUOTE = enum.auto()
102
+ # DIRECTIVE type for Directive token
103
+ DIRECTIVE = enum.auto()
104
+ # SPACE type for Space token
105
+ SPACE = enum.auto()
106
+ # NULL type for Null token
107
+ NULL = enum.auto()
108
+ # IMPLICIT_NULL type for implicit Null token.
109
+ # This is used when explicit keywords such as null or ~ are not specified. It is distinguished during encoding and
110
+ # output as an empty string.
111
+ IMPLICIT_NULL = enum.auto()
112
+ # INFINITY type for Infinity token
113
+ INFINITY = enum.auto()
114
+ # NAN type for Nan token
115
+ NAN = enum.auto()
116
+ # INTEGER type for Integer token
117
+ INTEGER = enum.auto()
118
+ # BINARY_INTEGER type for BinaryInteger token
119
+ BINARY_INTEGER = enum.auto()
120
+ # OCTET_INTEGER type for OctetInteger token
121
+ OCTET_INTEGER = enum.auto()
122
+ # HEX_INTEGER type for HexInteger token
123
+ HEX_INTEGER = enum.auto()
124
+ # FLOAT type for Float token
125
+ FLOAT = enum.auto()
126
+ # STRING type for String token
127
+ STRING = enum.auto()
128
+ # BOOL type for Bool token
129
+ BOOL = enum.auto()
130
+ # INVALID type for invalid token
131
+ INVALID = enum.auto()
132
+
133
+
134
+ class YamlCharType(enum.Enum):
135
+ # INDICATOR type of indicator character
136
+ INDICATOR = enum.auto()
137
+ # WHITE-SPACE type of white space character
138
+ WHITESPACE = enum.auto()
139
+ # MISCELLANEOUS type of miscellaneous character
140
+ MISCELLANEOUS = enum.auto()
141
+ # ESCAPED type of escaped character
142
+ ESCAPED = enum.auto()
143
+ # INVALID type for an invalid token.
144
+ INVALID = enum.auto()
145
+
146
+
147
+ class YamlIndicator(enum.Enum):
148
+ # NOT not an indicator
149
+ NOT = enum.auto()
150
+ # BLOCK_STRUCTURE indicator for block structure ( '-', '?', ':' )
151
+ BLOCK_STRUCTURE = enum.auto()
152
+ # FLOW_COLLECTION indicator for flow collection ( '[', ']', '{', '}', ',' )
153
+ FLOW_COLLECTION = enum.auto()
154
+ # COMMENT indicator for comment ( '#' )
155
+ COMMENT = enum.auto()
156
+ # NODE_PROPERTY indicator for node property ( '!', '&', '*' )
157
+ NODE_PROPERTY = enum.auto()
158
+ # BLOCK_SCALAR indicator for block scalar ( '|', '>' )
159
+ BLOCK_SCALAR = enum.auto()
160
+ # QUOTED_SCALAR indicator for quoted scalar ( ''', '"' )
161
+ QUOTED_SCALAR = enum.auto()
162
+ # DIRECTIVE indicator for directive ( '%' )
163
+ DIRECTIVE = enum.auto()
164
+ # INVALID_USE_OF_RESERVED indicator for invalid use of reserved keyword ( '@', '`' )
165
+ INVALID_USE_OF_RESERVED = enum.auto()
166
+
167
+
168
+ ##
169
+
170
+
171
+ RESERVED_NULL_KEYWORDS = (
172
+ 'null',
173
+ 'Null',
174
+ 'NULL',
175
+ '~',
176
+ )
177
+
178
+
179
+ RESERVED_BOOL_KEYWORDS = (
180
+ 'true',
181
+ 'True',
182
+ 'TRUE',
183
+ 'false',
184
+ 'False',
185
+ 'FALSE',
186
+ )
187
+
188
+
189
+ # For compatibility with other YAML 1.1 parsers.
190
+ # Note that we use these solely for encoding the bool value with quotes. go-yaml should not treat these as reserved
191
+ # keywords at parsing time. as go-yaml is supposed to be compliant only with YAML 1.2.
192
+ RESERVED_LEGACY_BOOL_KEYWORDS = (
193
+ 'y',
194
+ 'Y',
195
+ 'yes',
196
+ 'Yes',
197
+ 'YES',
198
+ 'n',
199
+ 'N',
200
+ 'no',
201
+ 'No',
202
+ 'NO',
203
+ 'on',
204
+ 'On',
205
+ 'ON',
206
+ 'off',
207
+ 'Off',
208
+ 'OFF',
209
+ )
210
+
211
+
212
+ RESERVED_INF_KEYWORDS = (
213
+ '.inf',
214
+ '.Inf',
215
+ '.INF',
216
+ '-.inf',
217
+ '-.Inf',
218
+ '-.INF',
219
+ )
220
+
221
+
222
+ RESERVED_NAN_KEYWORDS = (
223
+ '.nan',
224
+ '.NaN',
225
+ '.NAN',
226
+ )
227
+
228
+
229
+ def reserved_keyword_token(typ: YamlTokenType, value: str, org: str, pos: 'YamlPosition') -> 'YamlToken':
230
+ return YamlToken(
231
+ type=typ,
232
+ char_type=YamlCharType.MISCELLANEOUS,
233
+ indicator=YamlIndicator.NOT,
234
+ value=value,
235
+ origin=org,
236
+ position=pos,
237
+ )
238
+
239
+
240
+ RESERVED_KEYWORD_MAP: ta.Mapping[str, ta.Callable[[str, str, 'YamlPosition'], 'YamlToken']] = {
241
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.NULL) for keyword in RESERVED_NULL_KEYWORDS},
242
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.BOOL) for keyword in RESERVED_BOOL_KEYWORDS},
243
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.INFINITY) for keyword in RESERVED_INF_KEYWORDS},
244
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.NAN) for keyword in RESERVED_NAN_KEYWORDS},
245
+ }
246
+
247
+
248
+ # RESERVED_ENC_KEYWORD_MAP contains is the keyword map used at encoding time.
249
+ # This is supposed to be a superset of RESERVED_KEYWORD_MAP, and used to quote legacy keywords present in YAML 1.1 or
250
+ # lesser for compatibility reasons, even though this library is supposed to be YAML 1.2-compliant.
251
+ RESERVED_ENC_KEYWORD_MAP: ta.Mapping[str, ta.Callable[[str, str, 'YamlPosition'], 'YamlToken']] = {
252
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.NULL) for keyword in RESERVED_NULL_KEYWORDS},
253
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.BOOL) for keyword in RESERVED_BOOL_KEYWORDS},
254
+ **{keyword: functools.partial(reserved_keyword_token, YamlTokenType.BOOL) for keyword in RESERVED_LEGACY_BOOL_KEYWORDS}, # noqa
255
+ }
256
+
257
+
258
+ ##
259
+
260
+
261
+ YamlReservedTagKeyword = str # ta.TypeAlias # omlish-amalg-typing-no-move
262
+
263
+
264
+ class YamlReservedTagKeywords:
265
+ # INTEGER `!!int` tag
266
+ INTEGER = '!!int'
267
+ # FLOAT `!!float` tag
268
+ FLOAT = '!!float'
269
+ # NULL `!!null` tag
270
+ NULL = '!!null'
271
+ # SEQUENCE `!!seq` tag
272
+ SEQUENCE = '!!seq'
273
+ # MAPPING `!!map` tag
274
+ MAPPING = '!!map'
275
+ # STRING `!!str` tag
276
+ STRING = '!!str'
277
+ # BINARY `!!binary` tag
278
+ BINARY = '!!binary'
279
+ # ORDERED_MAP `!!omap` tag
280
+ ORDERED_MAP = '!!omap'
281
+ # SET `!!set` tag
282
+ SET = '!!set'
283
+ # TIMESTAMP `!!timestamp` tag
284
+ TIMESTAMP = '!!timestamp'
285
+ # BOOLEAN `!!bool` tag
286
+ BOOLEAN = '!!bool'
287
+ # MERGE `!!merge` tag
288
+ MERGE = '!!merge'
289
+
290
+
291
+ # RESERVED_TAG_KEYWORD_MAP map for reserved tag keywords
292
+ YAML_RESERVED_TAG_KEYWORD_MAP: ta.Mapping[YamlReservedTagKeyword, ta.Callable[[str, str, 'YamlPosition'], 'YamlToken']] = { # noqa
293
+ YamlReservedTagKeywords.INTEGER: lambda value, org, pos: YamlToken(
294
+ type=YamlTokenType.TAG,
295
+ char_type=YamlCharType.INDICATOR,
296
+ indicator=YamlIndicator.NODE_PROPERTY,
297
+ value=value,
298
+ origin=org,
299
+ position=pos,
300
+ ),
301
+ YamlReservedTagKeywords.FLOAT: lambda value, org, pos: YamlToken(
302
+ type=YamlTokenType.TAG,
303
+ char_type=YamlCharType.INDICATOR,
304
+ indicator=YamlIndicator.NODE_PROPERTY,
305
+ value=value,
306
+ origin=org,
307
+ position=pos,
308
+ ),
309
+ YamlReservedTagKeywords.NULL: lambda value, org, pos: YamlToken(
310
+ type=YamlTokenType.TAG,
311
+ char_type=YamlCharType.INDICATOR,
312
+ indicator=YamlIndicator.NODE_PROPERTY,
313
+ value=value,
314
+ origin=org,
315
+ position=pos,
316
+ ),
317
+ YamlReservedTagKeywords.SEQUENCE: lambda value, org, pos: YamlToken(
318
+ type=YamlTokenType.TAG,
319
+ char_type=YamlCharType.INDICATOR,
320
+ indicator=YamlIndicator.NODE_PROPERTY,
321
+ value=value,
322
+ origin=org,
323
+ position=pos,
324
+ ),
325
+ YamlReservedTagKeywords.MAPPING: lambda value, org, pos: YamlToken(
326
+ type=YamlTokenType.TAG,
327
+ char_type=YamlCharType.INDICATOR,
328
+ indicator=YamlIndicator.NODE_PROPERTY,
329
+ value=value,
330
+ origin=org,
331
+ position=pos,
332
+ ),
333
+ YamlReservedTagKeywords.STRING: lambda value, org, pos: YamlToken(
334
+ type=YamlTokenType.TAG,
335
+ char_type=YamlCharType.INDICATOR,
336
+ indicator=YamlIndicator.NODE_PROPERTY,
337
+ value=value,
338
+ origin=org,
339
+ position=pos,
340
+ ),
341
+ YamlReservedTagKeywords.BINARY: lambda value, org, pos: YamlToken(
342
+ type=YamlTokenType.TAG,
343
+ char_type=YamlCharType.INDICATOR,
344
+ indicator=YamlIndicator.NODE_PROPERTY,
345
+ value=value,
346
+ origin=org,
347
+ position=pos,
348
+ ),
349
+ YamlReservedTagKeywords.ORDERED_MAP: lambda value, org, pos: YamlToken(
350
+ type=YamlTokenType.TAG,
351
+ char_type=YamlCharType.INDICATOR,
352
+ indicator=YamlIndicator.NODE_PROPERTY,
353
+ value=value,
354
+ origin=org,
355
+ position=pos,
356
+ ),
357
+ YamlReservedTagKeywords.SET: lambda value, org, pos: YamlToken(
358
+ type=YamlTokenType.TAG,
359
+ char_type=YamlCharType.INDICATOR,
360
+ indicator=YamlIndicator.NODE_PROPERTY,
361
+ value=value,
362
+ origin=org,
363
+ position=pos,
364
+ ),
365
+ YamlReservedTagKeywords.TIMESTAMP: lambda value, org, pos: YamlToken(
366
+ type=YamlTokenType.TAG,
367
+ char_type=YamlCharType.INDICATOR,
368
+ indicator=YamlIndicator.NODE_PROPERTY,
369
+ value=value,
370
+ origin=org,
371
+ position=pos,
372
+ ),
373
+ YamlReservedTagKeywords.BOOLEAN: lambda value, org, pos: YamlToken(
374
+ type=YamlTokenType.TAG,
375
+ char_type=YamlCharType.INDICATOR,
376
+ indicator=YamlIndicator.NODE_PROPERTY,
377
+ value=value,
378
+ origin=org,
379
+ position=pos,
380
+ ),
381
+ YamlReservedTagKeywords.MERGE: lambda value, org, pos: YamlToken(
382
+ type=YamlTokenType.TAG,
383
+ char_type=YamlCharType.INDICATOR,
384
+ indicator=YamlIndicator.NODE_PROPERTY,
385
+ value=value,
386
+ origin=org,
387
+ position=pos,
388
+ ),
389
+ }
390
+
391
+
392
+ ##
393
+
394
+
395
+ class YamlNumberType(enum.Enum):
396
+ DECIMAL = 'decimal'
397
+ BINARY = 'binary'
398
+ OCTET = 'octet'
399
+ HEX = 'hex'
400
+ FLOAT = 'float'
401
+
402
+
403
+ @dc.dataclass()
404
+ class YamlNumberValue:
405
+ type: YamlNumberType
406
+ value: ta.Any
407
+ text: str
408
+
409
+
410
+ def to_number(value: str) -> ta.Optional[YamlNumberValue]:
411
+ num = _to_number(value)
412
+ if isinstance(num, YamlError):
413
+ return None
414
+
415
+ return num
416
+
417
+
418
+ def _is_number(value: str) -> bool:
419
+ num = _to_number(value)
420
+ if isinstance(num, YamlError):
421
+ # var numErr *strconv.NumError
422
+ # if errors.As(err, &numErr) && errors.Is(numErr.Err, strconv.ErrRange) {
423
+ # return true
424
+
425
+ return False
426
+
427
+ return num is not None
428
+
429
+
430
+ def _to_number(value: str) -> YamlErrorOr[ta.Optional[YamlNumberValue]]:
431
+ if not value:
432
+ return None
433
+
434
+ if value.startswith('_'):
435
+ return None
436
+
437
+ dot_count = value.count('.')
438
+ if dot_count > 1:
439
+ return None
440
+
441
+ is_negative = value.startswith('-')
442
+ normalized = value.lstrip('+').lstrip('-').replace('_', '')
443
+
444
+ typ: YamlNumberType
445
+ base = 0
446
+
447
+ if normalized.startswith('0x'):
448
+ normalized = normalized.lstrip('0x')
449
+ base = 16
450
+ typ = YamlNumberType.HEX
451
+ elif normalized.startswith('0o'):
452
+ normalized = normalized.lstrip('0o')
453
+ base = 8
454
+ typ = YamlNumberType.OCTET
455
+ elif normalized.startswith('0b'):
456
+ normalized = normalized.lstrip('0b')
457
+ base = 2
458
+ typ = YamlNumberType.BINARY
459
+ elif normalized.startswith('0') and len(normalized) > 1 and dot_count == 0:
460
+ base = 8
461
+ typ = YamlNumberType.OCTET
462
+ elif dot_count == 1:
463
+ typ = YamlNumberType.FLOAT
464
+ else:
465
+ typ = YamlNumberType.DECIMAL
466
+ base = 10
467
+
468
+ text = normalized
469
+ if is_negative:
470
+ text = '-' + text
471
+
472
+ v: ta.Any
473
+ if typ == YamlNumberType.FLOAT:
474
+ try:
475
+ v = float(text)
476
+ except ValueError as e:
477
+ return yaml_error(e)
478
+ else:
479
+ try:
480
+ v = int(text, base)
481
+ except ValueError as e:
482
+ return yaml_error(e)
483
+
484
+ return YamlNumberValue(
485
+ type=typ,
486
+ value=v,
487
+ text=text,
488
+ )
489
+
490
+
491
+ ##
492
+
493
+
494
+ # This is a subset of the formats permitted by the regular expression defined at http:#yaml.org/type/timestamp.html.
495
+ # Note that time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" from the examples.
496
+ YAML_TIMESTAMP_FORMATS = (
497
+ '%Y-%m-%dT%H:%M:%S.%fZ', # RFC3339Nano
498
+ '%Y-%m-%dt%H:%M:%S.%fZ', # RFC3339Nano with lower-case "t"
499
+ '%Y-%m-%d %H:%M:%S', # DateTime
500
+ '%Y-%m-%d', # DateOnly
501
+
502
+ # Not in examples, but to preserve backward compatibility by quoting time values
503
+ '%H:%M',
504
+ )
505
+
506
+
507
+ def _is_timestamp(value: str) -> bool:
508
+ for format_str in YAML_TIMESTAMP_FORMATS:
509
+ try:
510
+ datetime.datetime.strptime(value, format_str) # noqa
511
+ return True
512
+ except ValueError:
513
+ continue
514
+ return False
515
+
516
+
517
+ ##
518
+
519
+
520
+ # is_need_quoted checks whether the value needs quote for passed string or not
521
+ def is_need_quoted(value: str) -> bool:
522
+ if not value:
523
+ return True
524
+
525
+ if value in RESERVED_ENC_KEYWORD_MAP:
526
+ return True
527
+
528
+ if _is_number(value):
529
+ return True
530
+
531
+ if value == '-':
532
+ return True
533
+
534
+ if value[0] in ('*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ', '`'):
535
+ return True
536
+
537
+ if value[-1] in (':', ' '):
538
+ return True
539
+
540
+ if _is_timestamp(value):
541
+ return True
542
+
543
+ for i, c in enumerate(value):
544
+ if c in ('#', '\\'):
545
+ return True
546
+ elif c in (':', '-'):
547
+ if i + 1 < len(value) and value[i + 1] == ' ':
548
+ return True
549
+
550
+ return False
551
+
552
+
553
+ # literal_block_header detect literal block scalar header
554
+ def literal_block_header(value: str) -> str:
555
+ lbc = detect_line_break_char(value)
556
+
557
+ if lbc not in value:
558
+ return ''
559
+ elif value.endswith(lbc + lbc):
560
+ return '|+'
561
+ elif value.endswith(lbc):
562
+ return '|'
563
+ else:
564
+ return '|-'
565
+
566
+
567
+ ##
568
+
569
+
570
+ # new create reserved keyword token or number token and other string token.
571
+ def new_yaml_token(value: str, org: str, pos: 'YamlPosition') -> 'YamlToken':
572
+ fn = RESERVED_KEYWORD_MAP.get(value)
573
+ if fn is not None:
574
+ return fn(value, org, pos)
575
+
576
+ if (num := to_number(value)) is not None:
577
+ tk = YamlToken(
578
+ type=YamlTokenType.INTEGER,
579
+ char_type=YamlCharType.MISCELLANEOUS,
580
+ indicator=YamlIndicator.NOT,
581
+ value=value,
582
+ origin=org,
583
+ position=pos,
584
+ )
585
+ if num.type == YamlNumberType.FLOAT:
586
+ tk.type = YamlTokenType.FLOAT
587
+ elif num.type == YamlNumberType.BINARY:
588
+ tk.type = YamlTokenType.BINARY_INTEGER
589
+ elif num.type == YamlNumberType.OCTET:
590
+ tk.type = YamlTokenType.OCTET_INTEGER
591
+ elif num.type == YamlNumberType.HEX:
592
+ tk.type = YamlTokenType.HEX_INTEGER
593
+ return tk
594
+
595
+ return YamlTokenMakers.new_string(value, org, pos)
596
+
597
+
598
+ # Position type for position in YAML document
599
+ @dc.dataclass()
600
+ class YamlPosition:
601
+ line: int
602
+ column: int
603
+ offset: int
604
+ indent_num: int
605
+ indent_level: int
606
+
607
+ # String position to text
608
+ def __str__(self) -> str:
609
+ return f'[level:{self.indent_level:d},line:{self.line:d},column:{self.column:d},offset:{self.offset:d}]'
610
+
611
+
612
+ # Token type for token
613
+ @dc.dataclass()
614
+ @ta.final
615
+ class YamlToken:
616
+ # Type is a token type.
617
+ type: YamlTokenType
618
+ # CharType is a character type.
619
+ char_type: YamlCharType
620
+ # Indicator is an indicator type.
621
+ indicator: YamlIndicator
622
+ # Value is a string extracted with only meaningful characters, with spaces and such removed.
623
+ value: str
624
+ # Origin is a string that stores the original text as-is.
625
+ origin: str
626
+ # Error keeps error message for InvalidToken.
627
+ error: ta.Optional[YamlError] = None
628
+ # Position is a token position.
629
+ position: YamlPosition = dc.field(default_factory=dataclass_field_required('position'))
630
+ # Next is a next token reference.
631
+ next: ta.Optional['YamlToken'] = dc.field(default=None, repr=False)
632
+ # Prev is a previous token reference.
633
+ prev: ta.Optional['YamlToken'] = dc.field(default=None, repr=False)
634
+
635
+ # previous_type previous token type
636
+ def previous_type(self) -> YamlTokenType:
637
+ if self.prev is not None:
638
+ return self.prev.type
639
+
640
+ return YamlTokenType.UNKNOWN
641
+
642
+ # next_type next token type
643
+ def next_type(self) -> YamlTokenType:
644
+ if self.next is not None:
645
+ return self.next.type
646
+
647
+ return YamlTokenType.UNKNOWN
648
+
649
+ # add_column append column number to current position of column
650
+ @classmethod
651
+ def add_column(cls, t: ta.Optional['YamlToken'], col: int) -> None:
652
+ if t is None:
653
+ return
654
+
655
+ t.position.column += col
656
+
657
+ # clone copy token ( preserve Prev/Next reference )
658
+ @classmethod
659
+ def clone(cls, t: ta.Optional['YamlToken']) -> ta.Optional['YamlToken']:
660
+ if t is None:
661
+ return None
662
+
663
+ copied = copy.copy(t)
664
+ if t.position is not None:
665
+ pos = copy.copy(t.position)
666
+ copied.position = pos
667
+
668
+ return copied
669
+
670
+
671
+ ##
672
+
673
+
674
+ # Tokens type of token collection
675
+ class YamlTokens(ta.List[YamlToken]):
676
+ def invalid_token(self) -> ta.Optional[YamlToken]:
677
+ for tt in self:
678
+ if tt.type == YamlTokenType.INVALID:
679
+ return tt
680
+ return None
681
+
682
+ def _add(self, tk: YamlToken) -> None:
683
+ if not self:
684
+ self.append(tk)
685
+ else:
686
+ last = self[-1]
687
+ last.next = tk
688
+ tk.prev = last
689
+ self.append(tk)
690
+
691
+ # add append new some tokens
692
+ def add(self, *tks: YamlToken) -> None:
693
+ for tk in tks:
694
+ self._add(tk)
695
+
696
+
697
+ ##
698
+
699
+
700
+ class YamlTokenMakers: # noqa
701
+ def __new__(cls, *args, **kwargs): # noqa
702
+ raise TypeError
703
+
704
+ # new_string create token for String
705
+ @staticmethod
706
+ def new_string(value: str, org: str, pos: YamlPosition) -> YamlToken:
707
+ return YamlToken(
708
+ type=YamlTokenType.STRING,
709
+ char_type=YamlCharType.MISCELLANEOUS,
710
+ indicator=YamlIndicator.NOT,
711
+ value=value,
712
+ origin=org,
713
+ position=pos,
714
+ )
715
+
716
+ # new_sequence_entry create token for SequenceEntry
717
+ @staticmethod
718
+ def new_sequence_entry(org: str, pos: YamlPosition) -> YamlToken:
719
+ return YamlToken(
720
+ type=YamlTokenType.SEQUENCE_ENTRY,
721
+ char_type=YamlCharType.INDICATOR,
722
+ indicator=YamlIndicator.BLOCK_STRUCTURE,
723
+ value=YamlChars.SEQUENCE_ENTRY,
724
+ origin=org,
725
+ position=pos,
726
+ )
727
+
728
+ # new_mapping_key create token for MappingKey
729
+ @staticmethod
730
+ def new_mapping_key(pos: YamlPosition) -> YamlToken:
731
+ return YamlToken(
732
+ type=YamlTokenType.MAPPING_KEY,
733
+ char_type=YamlCharType.INDICATOR,
734
+ indicator=YamlIndicator.BLOCK_STRUCTURE,
735
+ value=YamlChars.MAPPING_KEY,
736
+ origin=YamlChars.MAPPING_KEY,
737
+ position=pos,
738
+ )
739
+
740
+ # new_mapping_value create token for MappingValue
741
+ @staticmethod
742
+ def new_mapping_value(pos: YamlPosition) -> YamlToken:
743
+ return YamlToken(
744
+ type=YamlTokenType.MAPPING_VALUE,
745
+ char_type=YamlCharType.INDICATOR,
746
+ indicator=YamlIndicator.BLOCK_STRUCTURE,
747
+ value=YamlChars.MAPPING_VALUE,
748
+ origin=YamlChars.MAPPING_VALUE,
749
+ position=pos,
750
+ )
751
+
752
+ # new_collect_entry create token for CollectEntry
753
+ @staticmethod
754
+ def new_collect_entry(org: str, pos: YamlPosition) -> YamlToken:
755
+ return YamlToken(
756
+ type=YamlTokenType.COLLECT_ENTRY,
757
+ char_type=YamlCharType.INDICATOR,
758
+ indicator=YamlIndicator.FLOW_COLLECTION,
759
+ value=YamlChars.COLLECT_ENTRY,
760
+ origin=org,
761
+ position=pos,
762
+ )
763
+
764
+ # new_sequence_start create token for SequenceStart
765
+ @staticmethod
766
+ def new_sequence_start(org: str, pos: YamlPosition) -> YamlToken:
767
+ return YamlToken(
768
+ type=YamlTokenType.SEQUENCE_START,
769
+ char_type=YamlCharType.INDICATOR,
770
+ indicator=YamlIndicator.FLOW_COLLECTION,
771
+ value=YamlChars.SEQUENCE_START,
772
+ origin=org,
773
+ position=pos,
774
+ )
775
+
776
+ # new_sequence_end create token for SequenceEnd
777
+ @staticmethod
778
+ def new_sequence_end(org: str, pos: YamlPosition) -> YamlToken:
779
+ return YamlToken(
780
+ type=YamlTokenType.SEQUENCE_END,
781
+ char_type=YamlCharType.INDICATOR,
782
+ indicator=YamlIndicator.FLOW_COLLECTION,
783
+ value=YamlChars.SEQUENCE_END,
784
+ origin=org,
785
+ position=pos,
786
+ )
787
+
788
+ # new_mapping_start create token for MappingStart
789
+ @staticmethod
790
+ def new_mapping_start(org: str, pos: YamlPosition) -> YamlToken:
791
+ return YamlToken(
792
+ type=YamlTokenType.MAPPING_START,
793
+ char_type=YamlCharType.INDICATOR,
794
+ indicator=YamlIndicator.FLOW_COLLECTION,
795
+ value=YamlChars.MAPPING_START,
796
+ origin=org,
797
+ position=pos,
798
+ )
799
+
800
+ # new_mapping_end create token for MappingEnd
801
+ @staticmethod
802
+ def new_mapping_end(org: str, pos: YamlPosition) -> YamlToken:
803
+ return YamlToken(
804
+ type=YamlTokenType.MAPPING_END,
805
+ char_type=YamlCharType.INDICATOR,
806
+ indicator=YamlIndicator.FLOW_COLLECTION,
807
+ value=YamlChars.MAPPING_END,
808
+ origin=org,
809
+ position=pos,
810
+ )
811
+
812
+ # new_comment create token for Comment
813
+ @staticmethod
814
+ def new_comment(value: str, org: str, pos: YamlPosition) -> YamlToken:
815
+ return YamlToken(
816
+ type=YamlTokenType.COMMENT,
817
+ char_type=YamlCharType.INDICATOR,
818
+ indicator=YamlIndicator.COMMENT,
819
+ value=value,
820
+ origin=org,
821
+ position=pos,
822
+ )
823
+
824
+ # new_anchor create token for Anchor
825
+ @staticmethod
826
+ def new_anchor(org: str, pos: YamlPosition) -> YamlToken:
827
+ return YamlToken(
828
+ type=YamlTokenType.ANCHOR,
829
+ char_type=YamlCharType.INDICATOR,
830
+ indicator=YamlIndicator.NODE_PROPERTY,
831
+ value=YamlChars.ANCHOR,
832
+ origin=org,
833
+ position=pos,
834
+ )
835
+
836
+ # new_alias create token for Alias
837
+ @staticmethod
838
+ def new_alias(org: str, pos: YamlPosition) -> YamlToken:
839
+ return YamlToken(
840
+ type=YamlTokenType.ALIAS,
841
+ char_type=YamlCharType.INDICATOR,
842
+ indicator=YamlIndicator.NODE_PROPERTY,
843
+ value=YamlChars.ALIAS,
844
+ origin=org,
845
+ position=pos,
846
+ )
847
+
848
+ # new_tag create token for Tag
849
+ @staticmethod
850
+ def new_tag(value: str, org: str, pos: YamlPosition) -> YamlToken:
851
+ fn = YAML_RESERVED_TAG_KEYWORD_MAP.get(value)
852
+ if fn is not None:
853
+ return fn(value, org, pos)
854
+
855
+ return YamlToken(
856
+ type=YamlTokenType.TAG,
857
+ char_type=YamlCharType.INDICATOR,
858
+ indicator=YamlIndicator.NODE_PROPERTY,
859
+ value=value,
860
+ origin=org,
861
+ position=pos,
862
+ )
863
+
864
+ # new_literal create token for Literal
865
+ @staticmethod
866
+ def new_literal(value: str, org: str, pos: YamlPosition) -> YamlToken:
867
+ return YamlToken(
868
+ type=YamlTokenType.LITERAL,
869
+ char_type=YamlCharType.INDICATOR,
870
+ indicator=YamlIndicator.BLOCK_SCALAR,
871
+ value=value,
872
+ origin=org,
873
+ position=pos,
874
+ )
875
+
876
+ # new_folded create token for Folded
877
+ @staticmethod
878
+ def new_folded(value: str, org: str, pos: YamlPosition) -> YamlToken:
879
+ return YamlToken(
880
+ type=YamlTokenType.FOLDED,
881
+ char_type=YamlCharType.INDICATOR,
882
+ indicator=YamlIndicator.BLOCK_SCALAR,
883
+ value=value,
884
+ origin=org,
885
+ position=pos,
886
+ )
887
+
888
+ # new_single_quote create token for SingleQuote
889
+ @staticmethod
890
+ def new_single_quote(value: str, org: str, pos: YamlPosition) -> YamlToken:
891
+ return YamlToken(
892
+ type=YamlTokenType.SINGLE_QUOTE,
893
+ char_type=YamlCharType.INDICATOR,
894
+ indicator=YamlIndicator.QUOTED_SCALAR,
895
+ value=value,
896
+ origin=org,
897
+ position=pos,
898
+ )
899
+
900
+ # new_double_quote create token for DoubleQuote
901
+ @staticmethod
902
+ def new_double_quote(value: str, org: str, pos: YamlPosition) -> YamlToken:
903
+ return YamlToken(
904
+ type=YamlTokenType.DOUBLE_QUOTE,
905
+ char_type=YamlCharType.INDICATOR,
906
+ indicator=YamlIndicator.QUOTED_SCALAR,
907
+ value=value,
908
+ origin=org,
909
+ position=pos,
910
+ )
911
+
912
+ # new_directive create token for Directive
913
+ @staticmethod
914
+ def new_directive(org: str, pos: YamlPosition) -> YamlToken:
915
+ return YamlToken(
916
+ type=YamlTokenType.DIRECTIVE,
917
+ char_type=YamlCharType.INDICATOR,
918
+ indicator=YamlIndicator.DIRECTIVE,
919
+ value=YamlChars.DIRECTIVE,
920
+ origin=org,
921
+ position=pos,
922
+ )
923
+
924
+ # new_space create token for Space
925
+ @staticmethod
926
+ def new_space(pos: YamlPosition) -> YamlToken:
927
+ return YamlToken(
928
+ type=YamlTokenType.SPACE,
929
+ char_type=YamlCharType.WHITESPACE,
930
+ indicator=YamlIndicator.NOT,
931
+ value=YamlChars.SPACE,
932
+ origin=YamlChars.SPACE,
933
+ position=pos,
934
+ )
935
+
936
+ # new_merge_key create token for MergeKey
937
+ @staticmethod
938
+ def new_merge_key(org: str, pos: YamlPosition) -> YamlToken:
939
+ return YamlToken(
940
+ type=YamlTokenType.MERGE_KEY,
941
+ char_type=YamlCharType.MISCELLANEOUS,
942
+ indicator=YamlIndicator.NOT,
943
+ value='<<',
944
+ origin=org,
945
+ position=pos,
946
+ )
947
+
948
+ # new_document_header create token for DocumentHeader
949
+ @staticmethod
950
+ def new_document_header(org: str, pos: YamlPosition) -> YamlToken:
951
+ return YamlToken(
952
+ type=YamlTokenType.DOCUMENT_HEADER,
953
+ char_type=YamlCharType.MISCELLANEOUS,
954
+ indicator=YamlIndicator.NOT,
955
+ value='---',
956
+ origin=org,
957
+ position=pos,
958
+ )
959
+
960
+ # new_document_end create token for DocumentEnd
961
+ @staticmethod
962
+ def new_document_end(org: str, pos: YamlPosition) -> YamlToken:
963
+ return YamlToken(
964
+ type=YamlTokenType.DOCUMENT_END,
965
+ char_type=YamlCharType.MISCELLANEOUS,
966
+ indicator=YamlIndicator.NOT,
967
+ value='...',
968
+ origin=org,
969
+ position=pos,
970
+ )
971
+
972
+ @staticmethod
973
+ def new_invalid(err: YamlError, org: str, pos: YamlPosition) -> YamlToken:
974
+ return YamlToken(
975
+ type=YamlTokenType.INVALID,
976
+ char_type=YamlCharType.INVALID,
977
+ indicator=YamlIndicator.NOT,
978
+ value=org,
979
+ origin=org,
980
+ error=err,
981
+ position=pos,
982
+ )
983
+
984
+
985
+ ##
986
+
987
+
988
+ # detect_line_break_char detect line break character in only one inside scalar content scope.
989
+ def detect_line_break_char(src: str) -> str:
990
+ nc = src.count('\n')
991
+ rc = src.count('\r')
992
+ rnc = src.count('\r\n')
993
+ if nc == rnc and rc == rnc:
994
+ return '\r\n'
995
+ elif rc > nc:
996
+ return '\r'
997
+ else:
998
+ return '\n'