omextra 0.0.0.dev471__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.
- omextra/__about__.py +2 -0
- omextra/collections/__init__.py +0 -0
- omextra/collections/hamt/LICENSE +35 -0
- omextra/collections/hamt/__init__.py +0 -0
- omextra/collections/hamt/_hamt.c +3621 -0
- omextra/formats/goyaml/LICENSE +16 -0
- omextra/formats/goyaml/__init__.py +29 -0
- omextra/formats/goyaml/ast.py +2217 -0
- omextra/formats/goyaml/errors.py +49 -0
- omextra/formats/goyaml/parsing.py +2332 -0
- omextra/formats/goyaml/scanning.py +1888 -0
- omextra/formats/goyaml/tokens.py +998 -0
- omextra/text/abnf/LICENSE +16 -0
- omextra/text/abnf/__init__.py +79 -0
- omextra/text/abnf/base.py +313 -0
- omextra/text/abnf/core.py +141 -0
- omextra/text/abnf/errors.py +10 -0
- omextra/text/abnf/meta.py +583 -0
- omextra/text/abnf/parsers.py +343 -0
- omextra/text/abnf/utils.py +76 -0
- omextra/text/abnf/visitors.py +55 -0
- {omextra-0.0.0.dev471.dist-info → omextra-0.0.0.dev485.dist-info}/METADATA +2 -2
- {omextra-0.0.0.dev471.dist-info → omextra-0.0.0.dev485.dist-info}/RECORD +27 -7
- {omextra-0.0.0.dev471.dist-info → omextra-0.0.0.dev485.dist-info}/WHEEL +0 -0
- {omextra-0.0.0.dev471.dist-info → omextra-0.0.0.dev485.dist-info}/entry_points.txt +0 -0
- {omextra-0.0.0.dev471.dist-info → omextra-0.0.0.dev485.dist-info}/licenses/LICENSE +0 -0
- {omextra-0.0.0.dev471.dist-info → omextra-0.0.0.dev485.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,2332 @@
|
|
|
1
|
+
# ruff: noqa: UP006 UP007 UP043 UP045
|
|
2
|
+
# @omlish-lite
|
|
3
|
+
import copy
|
|
4
|
+
import dataclasses as dc
|
|
5
|
+
import enum
|
|
6
|
+
import typing as ta
|
|
7
|
+
|
|
8
|
+
from omlish.lite.check import check
|
|
9
|
+
from omlish.lite.dataclasses import dataclass_field_required
|
|
10
|
+
|
|
11
|
+
from . import ast
|
|
12
|
+
from .errors import YamlError
|
|
13
|
+
from .errors import YamlErrorOr
|
|
14
|
+
from .errors import yaml_error
|
|
15
|
+
from .scanning import yaml_tokenize
|
|
16
|
+
from .tokens import YamlReservedTagKeywords
|
|
17
|
+
from .tokens import YamlToken
|
|
18
|
+
from .tokens import YamlTokens
|
|
19
|
+
from .tokens import YamlTokenType
|
|
20
|
+
from .tokens import new_yaml_token
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# context context at parsing
|
|
27
|
+
@dc.dataclass()
|
|
28
|
+
class YamlParsingContext:
|
|
29
|
+
token_ref: ta.Optional['YamlParseTokenRef'] = None
|
|
30
|
+
path: str = dc.field(default_factory=dataclass_field_required('path'))
|
|
31
|
+
is_flow: bool = False
|
|
32
|
+
|
|
33
|
+
def current_token(self) -> ta.Optional['YamlParseToken']:
|
|
34
|
+
ref = check.not_none(self.token_ref)
|
|
35
|
+
|
|
36
|
+
if ref.idx >= ref.size:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
return ref.tokens[ref.idx]
|
|
40
|
+
|
|
41
|
+
def is_comment(self) -> bool:
|
|
42
|
+
return YamlParseToken.type(self.current_token()) == YamlTokenType.COMMENT
|
|
43
|
+
|
|
44
|
+
def next_token(self) -> ta.Optional['YamlParseToken']:
|
|
45
|
+
ref = check.not_none(self.token_ref)
|
|
46
|
+
|
|
47
|
+
if ref.idx + 1 >= ref.size:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
return ref.tokens[ref.idx + 1]
|
|
51
|
+
|
|
52
|
+
def next_not_comment_token(self) -> ta.Optional['YamlParseToken']:
|
|
53
|
+
ref = check.not_none(self.token_ref)
|
|
54
|
+
|
|
55
|
+
for i in range(ref.idx + 1, ref.size):
|
|
56
|
+
tk = ref.tokens[i]
|
|
57
|
+
if tk.type() == YamlTokenType.COMMENT:
|
|
58
|
+
continue
|
|
59
|
+
return tk
|
|
60
|
+
|
|
61
|
+
return None
|
|
62
|
+
|
|
63
|
+
def is_token_not_found(self) -> bool:
|
|
64
|
+
return self.current_token() is None
|
|
65
|
+
|
|
66
|
+
def with_group(self, g: 'YamlParseTokenGroup') -> 'YamlParsingContext':
|
|
67
|
+
ctx = copy.copy(self)
|
|
68
|
+
ctx.token_ref = YamlParseTokenRef(
|
|
69
|
+
tokens=g.tokens,
|
|
70
|
+
size=len(g.tokens),
|
|
71
|
+
)
|
|
72
|
+
return ctx
|
|
73
|
+
|
|
74
|
+
def with_child(self, path: str) -> 'YamlParsingContext':
|
|
75
|
+
ctx = copy.copy(self)
|
|
76
|
+
ctx.path = self.path + '.' + normalize_path(path)
|
|
77
|
+
return ctx
|
|
78
|
+
|
|
79
|
+
def with_index(self, idx: int) -> 'YamlParsingContext':
|
|
80
|
+
ctx = copy.copy(self)
|
|
81
|
+
ctx.path = self.path + '[' + str(idx) + ']'
|
|
82
|
+
return ctx
|
|
83
|
+
|
|
84
|
+
def with_flow(self, is_flow: bool) -> 'YamlParsingContext':
|
|
85
|
+
ctx = copy.copy(self)
|
|
86
|
+
ctx.is_flow = is_flow
|
|
87
|
+
return ctx
|
|
88
|
+
|
|
89
|
+
@staticmethod
|
|
90
|
+
def new() -> 'YamlParsingContext':
|
|
91
|
+
return YamlParsingContext(
|
|
92
|
+
path='$',
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
def go_next(self) -> None:
|
|
96
|
+
ref = check.not_none(self.token_ref)
|
|
97
|
+
if ref.size <= ref.idx + 1:
|
|
98
|
+
ref.idx = ref.size
|
|
99
|
+
else:
|
|
100
|
+
ref.idx += 1
|
|
101
|
+
|
|
102
|
+
def next(self) -> bool:
|
|
103
|
+
return check.not_none(self.token_ref).idx < check.not_none(self.token_ref).size
|
|
104
|
+
|
|
105
|
+
def insert_null_token(self, tk: 'YamlParseToken') -> 'YamlParseToken':
|
|
106
|
+
null_token = self.create_implicit_null_token(tk)
|
|
107
|
+
self.insert_token(null_token)
|
|
108
|
+
self.go_next()
|
|
109
|
+
|
|
110
|
+
return null_token
|
|
111
|
+
|
|
112
|
+
def add_null_value_token(self, tk: 'YamlParseToken') -> 'YamlParseToken':
|
|
113
|
+
null_token = self.create_implicit_null_token(tk)
|
|
114
|
+
raw_tk = null_token.raw_token()
|
|
115
|
+
|
|
116
|
+
# add space for map or sequence value.
|
|
117
|
+
check.not_none(raw_tk).position.column += 1
|
|
118
|
+
|
|
119
|
+
self.add_token(null_token)
|
|
120
|
+
self.go_next()
|
|
121
|
+
|
|
122
|
+
return null_token
|
|
123
|
+
|
|
124
|
+
def create_implicit_null_token(self, base: 'YamlParseToken') -> 'YamlParseToken':
|
|
125
|
+
pos = copy.copy(check.not_none(base.raw_token()).position)
|
|
126
|
+
pos.column += 1
|
|
127
|
+
tk = new_yaml_token('null', ' null', pos)
|
|
128
|
+
tk.type = YamlTokenType.IMPLICIT_NULL
|
|
129
|
+
return YamlParseToken(token=tk)
|
|
130
|
+
|
|
131
|
+
def insert_token(self, tk: 'YamlParseToken') -> None:
|
|
132
|
+
ref = check.not_none(self.token_ref)
|
|
133
|
+
idx = ref.idx
|
|
134
|
+
if ref.size < idx:
|
|
135
|
+
return
|
|
136
|
+
|
|
137
|
+
if ref.size == idx:
|
|
138
|
+
cur_token = ref.tokens[ref.size - 1]
|
|
139
|
+
check.not_none(tk.raw_token()).next = cur_token.raw_token()
|
|
140
|
+
check.not_none(cur_token.raw_token()).prev = tk.raw_token()
|
|
141
|
+
|
|
142
|
+
ref.tokens.append(tk)
|
|
143
|
+
ref.size = len(ref.tokens)
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
cur_token = ref.tokens[idx]
|
|
147
|
+
check.not_none(tk.raw_token()).next = cur_token.raw_token()
|
|
148
|
+
check.not_none(cur_token.raw_token()).prev = tk.raw_token()
|
|
149
|
+
|
|
150
|
+
ref.tokens = [*ref.tokens[:idx + 1], *ref.tokens[idx:]]
|
|
151
|
+
ref.tokens[idx] = tk
|
|
152
|
+
ref.size = len(ref.tokens)
|
|
153
|
+
|
|
154
|
+
def add_token(self, tk: 'YamlParseToken') -> None:
|
|
155
|
+
ref = check.not_none(self.token_ref)
|
|
156
|
+
last_tk = check.not_none(ref.tokens[ref.size - 1])
|
|
157
|
+
if last_tk.group is not None:
|
|
158
|
+
last_tk = check.not_none(last_tk.group.last())
|
|
159
|
+
|
|
160
|
+
check.not_none(last_tk.raw_token()).next = tk.raw_token()
|
|
161
|
+
check.not_none(tk.raw_token()).prev = last_tk.raw_token()
|
|
162
|
+
|
|
163
|
+
ref.tokens.append(tk)
|
|
164
|
+
ref.size = len(ref.tokens)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
@dc.dataclass()
|
|
168
|
+
class YamlParseTokenRef:
|
|
169
|
+
tokens: ta.List['YamlParseToken']
|
|
170
|
+
size: int
|
|
171
|
+
idx: int = 0
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
##
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
PATH_SPECIAL_CHARS = (
|
|
178
|
+
'$',
|
|
179
|
+
'*',
|
|
180
|
+
'.',
|
|
181
|
+
'[',
|
|
182
|
+
']',
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def contains_path_special_char(path: str) -> bool:
|
|
187
|
+
return any(char in path for char in PATH_SPECIAL_CHARS)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def normalize_path(path: str) -> str:
|
|
191
|
+
if contains_path_special_char(path):
|
|
192
|
+
return f"'{path}'"
|
|
193
|
+
|
|
194
|
+
return path
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
##
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
# Option represents parser's option.
|
|
201
|
+
Option = ta.Callable[['YamlParser'], None] # ta.TypeAlias # omlish-amalg-typing-no-move
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# AllowDuplicateMapKey allow the use of keys with the same name in the same map, but by default, this is not permitted.
|
|
205
|
+
def allow_duplicate_map_key() -> Option:
|
|
206
|
+
def fn(p: 'YamlParser') -> None:
|
|
207
|
+
p.allow_duplicate_map_key = True
|
|
208
|
+
|
|
209
|
+
return fn
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
##
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class YamlParseTokenGroupType(enum.Enum):
|
|
216
|
+
NONE = enum.auto()
|
|
217
|
+
DIRECTIVE = enum.auto()
|
|
218
|
+
DIRECTIVE_NAME = enum.auto()
|
|
219
|
+
DOCUMENT = enum.auto()
|
|
220
|
+
DOCUMENT_BODY = enum.auto()
|
|
221
|
+
ANCHOR = enum.auto()
|
|
222
|
+
ANCHOR_NAME = enum.auto()
|
|
223
|
+
ALIAS = enum.auto()
|
|
224
|
+
LITERAL = enum.auto()
|
|
225
|
+
FOLDED = enum.auto()
|
|
226
|
+
SCALAR_TAG = enum.auto()
|
|
227
|
+
MAP_KEY = enum.auto()
|
|
228
|
+
MAP_KEY_VALUE = enum.auto()
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@dc.dataclass()
|
|
232
|
+
class YamlParseToken:
|
|
233
|
+
token: ta.Optional[YamlToken] = None
|
|
234
|
+
group: ta.Optional['YamlParseTokenGroup'] = None
|
|
235
|
+
line_comment: ta.Optional[YamlToken] = None
|
|
236
|
+
|
|
237
|
+
def raw_token(self: ta.Optional['YamlParseToken']) -> ta.Optional[YamlToken]:
|
|
238
|
+
if self is None:
|
|
239
|
+
return None
|
|
240
|
+
if self.token is not None:
|
|
241
|
+
return self.token
|
|
242
|
+
return check.not_none(self.group).raw_token()
|
|
243
|
+
|
|
244
|
+
def type(self: ta.Optional['YamlParseToken']) -> YamlTokenType:
|
|
245
|
+
if self is None:
|
|
246
|
+
return YamlTokenType.UNKNOWN
|
|
247
|
+
if self.token is not None:
|
|
248
|
+
return self.token.type
|
|
249
|
+
return check.not_none(self.group).token_type()
|
|
250
|
+
|
|
251
|
+
def group_type(self: ta.Optional['YamlParseToken']) -> YamlParseTokenGroupType:
|
|
252
|
+
if self is None:
|
|
253
|
+
return YamlParseTokenGroupType.NONE
|
|
254
|
+
if self.token is not None:
|
|
255
|
+
return YamlParseTokenGroupType.NONE
|
|
256
|
+
return check.not_none(self.group).type
|
|
257
|
+
|
|
258
|
+
def line(self: ta.Optional['YamlParseToken']) -> int:
|
|
259
|
+
if self is None:
|
|
260
|
+
return 0
|
|
261
|
+
if self.token is not None:
|
|
262
|
+
return self.token.position.line
|
|
263
|
+
return check.not_none(self.group).line()
|
|
264
|
+
|
|
265
|
+
def column(self: ta.Optional['YamlParseToken']) -> int:
|
|
266
|
+
if self is None:
|
|
267
|
+
return 0
|
|
268
|
+
if self.token is not None:
|
|
269
|
+
return self.token.position.column
|
|
270
|
+
return check.not_none(self.group).column()
|
|
271
|
+
|
|
272
|
+
def set_group_type(self, typ: YamlParseTokenGroupType) -> None:
|
|
273
|
+
if self.group is None:
|
|
274
|
+
return
|
|
275
|
+
self.group.type = typ
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
##
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
@dc.dataclass()
|
|
282
|
+
class YamlParseTokenGroup:
|
|
283
|
+
type: YamlParseTokenGroupType = YamlParseTokenGroupType.NONE
|
|
284
|
+
tokens: ta.List[YamlParseToken] = dc.field(default_factory=dataclass_field_required('tokens'))
|
|
285
|
+
|
|
286
|
+
def first(self) -> ta.Optional[YamlParseToken]:
|
|
287
|
+
if len(self.tokens) == 0:
|
|
288
|
+
return None
|
|
289
|
+
return self.tokens[0]
|
|
290
|
+
|
|
291
|
+
def last(self) -> ta.Optional[YamlParseToken]:
|
|
292
|
+
if len(self.tokens) == 0:
|
|
293
|
+
return None
|
|
294
|
+
return self.tokens[len(self.tokens) - 1]
|
|
295
|
+
|
|
296
|
+
def raw_token(self) -> ta.Optional[YamlToken]:
|
|
297
|
+
if len(self.tokens) == 0:
|
|
298
|
+
return None
|
|
299
|
+
return self.tokens[0].raw_token()
|
|
300
|
+
|
|
301
|
+
def line(self) -> int:
|
|
302
|
+
if len(self.tokens) == 0:
|
|
303
|
+
return 0
|
|
304
|
+
return self.tokens[0].line()
|
|
305
|
+
|
|
306
|
+
def column(self) -> int:
|
|
307
|
+
if len(self.tokens) == 0:
|
|
308
|
+
return 0
|
|
309
|
+
return self.tokens[0].column()
|
|
310
|
+
|
|
311
|
+
def token_type(self) -> YamlTokenType:
|
|
312
|
+
if len(self.tokens) == 0:
|
|
313
|
+
return YamlTokenType.UNKNOWN
|
|
314
|
+
return self.tokens[0].type()
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def create_grouped_tokens(tokens: YamlTokens) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
318
|
+
tks = new_tokens(tokens)
|
|
319
|
+
|
|
320
|
+
tks = create_line_comment_token_groups(tks)
|
|
321
|
+
|
|
322
|
+
tks_ = create_literal_and_folded_token_groups(tks)
|
|
323
|
+
if isinstance(tks_, YamlError):
|
|
324
|
+
return tks_
|
|
325
|
+
tks = tks_
|
|
326
|
+
|
|
327
|
+
tks_ = create_anchor_and_alias_token_groups(tks)
|
|
328
|
+
if isinstance(tks_, YamlError):
|
|
329
|
+
return tks_
|
|
330
|
+
tks = tks_
|
|
331
|
+
|
|
332
|
+
tks_ = create_scalar_tag_token_groups(tks)
|
|
333
|
+
if isinstance(tks_, YamlError):
|
|
334
|
+
return tks_
|
|
335
|
+
tks = tks_
|
|
336
|
+
|
|
337
|
+
tks_ = create_anchor_with_scalar_tag_token_groups(tks)
|
|
338
|
+
if isinstance(tks_, YamlError):
|
|
339
|
+
return tks_
|
|
340
|
+
tks = tks_
|
|
341
|
+
|
|
342
|
+
tks_ = create_map_key_token_groups(tks)
|
|
343
|
+
if isinstance(tks_, YamlError):
|
|
344
|
+
return tks_
|
|
345
|
+
tks = tks_
|
|
346
|
+
|
|
347
|
+
tks = create_map_key_value_token_groups(tks)
|
|
348
|
+
|
|
349
|
+
tks_ = create_directive_token_groups(tks)
|
|
350
|
+
if isinstance(tks_, YamlError):
|
|
351
|
+
return tks_
|
|
352
|
+
tks = tks_
|
|
353
|
+
|
|
354
|
+
tks_ = create_document_tokens(tks)
|
|
355
|
+
if isinstance(tks_, YamlError):
|
|
356
|
+
return tks_
|
|
357
|
+
tks = tks_
|
|
358
|
+
|
|
359
|
+
return tks
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def new_tokens(tks: YamlTokens) -> ta.List[YamlParseToken]:
|
|
363
|
+
ret: ta.List[YamlParseToken] = []
|
|
364
|
+
for tk in tks:
|
|
365
|
+
ret.append(YamlParseToken(token=tk))
|
|
366
|
+
return ret
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def create_line_comment_token_groups(tokens: ta.List[YamlParseToken]) -> ta.List[YamlParseToken]:
|
|
370
|
+
ret: ta.List[YamlParseToken] = []
|
|
371
|
+
for i in range(len(tokens)):
|
|
372
|
+
tk = tokens[i]
|
|
373
|
+
if tk.type() == YamlTokenType.COMMENT:
|
|
374
|
+
if i > 0 and tokens[i - 1].line() == tk.line():
|
|
375
|
+
tokens[i - 1].line_comment = tk.raw_token()
|
|
376
|
+
else:
|
|
377
|
+
ret.append(tk)
|
|
378
|
+
else:
|
|
379
|
+
ret.append(tk)
|
|
380
|
+
return ret
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
def create_literal_and_folded_token_groups(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
384
|
+
ret: ta.List[YamlParseToken] = []
|
|
385
|
+
i = -1
|
|
386
|
+
while True:
|
|
387
|
+
i += 1
|
|
388
|
+
if not (i < len(tokens)):
|
|
389
|
+
break
|
|
390
|
+
tk = tokens[i]
|
|
391
|
+
if tk.type() == YamlTokenType.LITERAL:
|
|
392
|
+
tks: ta.List[YamlParseToken] = [tk]
|
|
393
|
+
if i + 1 < len(tokens):
|
|
394
|
+
tks.append(tokens[i + 1])
|
|
395
|
+
ret.append(YamlParseToken(
|
|
396
|
+
group=YamlParseTokenGroup(
|
|
397
|
+
type=YamlParseTokenGroupType.LITERAL,
|
|
398
|
+
tokens=tks,
|
|
399
|
+
),
|
|
400
|
+
))
|
|
401
|
+
i += 1
|
|
402
|
+
elif tk.type() == YamlTokenType.FOLDED:
|
|
403
|
+
tks = [tk]
|
|
404
|
+
if i + 1 < len(tokens):
|
|
405
|
+
tks.append(tokens[i + 1])
|
|
406
|
+
ret.append(YamlParseToken(
|
|
407
|
+
group=YamlParseTokenGroup(
|
|
408
|
+
type=YamlParseTokenGroupType.FOLDED,
|
|
409
|
+
tokens=tks,
|
|
410
|
+
),
|
|
411
|
+
))
|
|
412
|
+
i += 1
|
|
413
|
+
else:
|
|
414
|
+
ret.append(tk)
|
|
415
|
+
return ret
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
def err_syntax(msg: str, tk: ta.Optional[YamlToken]) -> YamlError:
|
|
419
|
+
return yaml_error(f'Syntax error: {msg}, {tk}')
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def create_anchor_and_alias_token_groups(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
423
|
+
ret: ta.List[YamlParseToken] = []
|
|
424
|
+
i = -1
|
|
425
|
+
while True:
|
|
426
|
+
i += 1
|
|
427
|
+
if not (i < len(tokens)):
|
|
428
|
+
break
|
|
429
|
+
tk = tokens[i]
|
|
430
|
+
if tk.type() == YamlTokenType.ANCHOR:
|
|
431
|
+
if i + 1 >= len(tokens):
|
|
432
|
+
return err_syntax('undefined anchor name', tk.raw_token())
|
|
433
|
+
if i + 2 >= len(tokens):
|
|
434
|
+
return err_syntax('undefined anchor value', tk.raw_token())
|
|
435
|
+
anchor_name = YamlParseToken(
|
|
436
|
+
group=YamlParseTokenGroup(
|
|
437
|
+
type=YamlParseTokenGroupType.ANCHOR_NAME,
|
|
438
|
+
tokens=[tk, tokens[i + 1]],
|
|
439
|
+
),
|
|
440
|
+
)
|
|
441
|
+
value_tk = tokens[i + 2]
|
|
442
|
+
if tk.line() == value_tk.line() and value_tk.type() == YamlTokenType.SEQUENCE_ENTRY:
|
|
443
|
+
return err_syntax(
|
|
444
|
+
'sequence entries are not allowed after anchor on the same line',
|
|
445
|
+
value_tk.raw_token(),
|
|
446
|
+
)
|
|
447
|
+
if tk.line() == value_tk.line() and is_scalar_type(value_tk):
|
|
448
|
+
ret.append(YamlParseToken(
|
|
449
|
+
group=YamlParseTokenGroup(
|
|
450
|
+
type=YamlParseTokenGroupType.ANCHOR,
|
|
451
|
+
tokens=[anchor_name, value_tk],
|
|
452
|
+
),
|
|
453
|
+
))
|
|
454
|
+
i += 1
|
|
455
|
+
else:
|
|
456
|
+
ret.append(anchor_name)
|
|
457
|
+
i += 1
|
|
458
|
+
elif tk.type() == YamlTokenType.ALIAS:
|
|
459
|
+
if i + 1 == len(tokens):
|
|
460
|
+
return err_syntax('undefined alias name', tk.raw_token())
|
|
461
|
+
ret.append(YamlParseToken(
|
|
462
|
+
group=YamlParseTokenGroup(
|
|
463
|
+
type=YamlParseTokenGroupType.ALIAS,
|
|
464
|
+
tokens=[tk, tokens[i + 1]],
|
|
465
|
+
),
|
|
466
|
+
))
|
|
467
|
+
i += 1
|
|
468
|
+
else:
|
|
469
|
+
ret.append(tk)
|
|
470
|
+
return ret
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def create_scalar_tag_token_groups(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
474
|
+
ret: ta.List[YamlParseToken] = []
|
|
475
|
+
i = -1
|
|
476
|
+
while True:
|
|
477
|
+
i += 1
|
|
478
|
+
if not (i < len(tokens)):
|
|
479
|
+
break
|
|
480
|
+
tk = tokens[i]
|
|
481
|
+
if tk.type() != YamlTokenType.TAG:
|
|
482
|
+
ret.append(tk)
|
|
483
|
+
continue
|
|
484
|
+
tag = check.not_none(tk.raw_token())
|
|
485
|
+
if tag.value.startswith('!!'):
|
|
486
|
+
# secondary tag.
|
|
487
|
+
if tag.value in (
|
|
488
|
+
YamlReservedTagKeywords.INTEGER,
|
|
489
|
+
YamlReservedTagKeywords.FLOAT,
|
|
490
|
+
YamlReservedTagKeywords.STRING,
|
|
491
|
+
YamlReservedTagKeywords.BINARY,
|
|
492
|
+
YamlReservedTagKeywords.TIMESTAMP,
|
|
493
|
+
YamlReservedTagKeywords.BOOLEAN,
|
|
494
|
+
YamlReservedTagKeywords.NULL,
|
|
495
|
+
):
|
|
496
|
+
if len(tokens) <= i + 1:
|
|
497
|
+
ret.append(tk)
|
|
498
|
+
continue
|
|
499
|
+
if tk.line() != tokens[i + 1].line():
|
|
500
|
+
ret.append(tk)
|
|
501
|
+
continue
|
|
502
|
+
if tokens[i + 1].group_type() == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
503
|
+
ret.append(tk)
|
|
504
|
+
continue
|
|
505
|
+
if is_scalar_type(tokens[i + 1]):
|
|
506
|
+
ret.append(YamlParseToken(
|
|
507
|
+
group=YamlParseTokenGroup(
|
|
508
|
+
type=YamlParseTokenGroupType.SCALAR_TAG,
|
|
509
|
+
tokens=[tk, tokens[i + 1]],
|
|
510
|
+
),
|
|
511
|
+
))
|
|
512
|
+
i += 1
|
|
513
|
+
else:
|
|
514
|
+
ret.append(tk)
|
|
515
|
+
elif tag.value == YamlReservedTagKeywords.MERGE:
|
|
516
|
+
if len(tokens) <= i + 1:
|
|
517
|
+
ret.append(tk)
|
|
518
|
+
continue
|
|
519
|
+
if tk.line() != tokens[i + 1].line():
|
|
520
|
+
ret.append(tk)
|
|
521
|
+
continue
|
|
522
|
+
if tokens[i + 1].group_type() == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
523
|
+
ret.append(tk)
|
|
524
|
+
continue
|
|
525
|
+
if tokens[i + 1].type() != YamlTokenType.MERGE_KEY:
|
|
526
|
+
return err_syntax('could not find merge key', tokens[i + 1].raw_token())
|
|
527
|
+
ret.append(YamlParseToken(
|
|
528
|
+
group=YamlParseTokenGroup(
|
|
529
|
+
type=YamlParseTokenGroupType.SCALAR_TAG,
|
|
530
|
+
tokens=[tk, tokens[i + 1]],
|
|
531
|
+
),
|
|
532
|
+
))
|
|
533
|
+
i += 1
|
|
534
|
+
else:
|
|
535
|
+
ret.append(tk)
|
|
536
|
+
else:
|
|
537
|
+
if len(tokens) <= i + 1:
|
|
538
|
+
ret.append(tk)
|
|
539
|
+
continue
|
|
540
|
+
if tk.line() != tokens[i + 1].line():
|
|
541
|
+
ret.append(tk)
|
|
542
|
+
continue
|
|
543
|
+
if tokens[i + 1].group_type() == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
544
|
+
ret.append(tk)
|
|
545
|
+
continue
|
|
546
|
+
if is_flow_type(tokens[i + 1]):
|
|
547
|
+
ret.append(tk)
|
|
548
|
+
continue
|
|
549
|
+
ret.append(YamlParseToken(
|
|
550
|
+
group=YamlParseTokenGroup(
|
|
551
|
+
type=YamlParseTokenGroupType.SCALAR_TAG,
|
|
552
|
+
tokens=[tk, tokens[i + 1]],
|
|
553
|
+
),
|
|
554
|
+
))
|
|
555
|
+
i += 1
|
|
556
|
+
return ret
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def create_anchor_with_scalar_tag_token_groups(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
560
|
+
ret: ta.List[YamlParseToken] = []
|
|
561
|
+
i = -1
|
|
562
|
+
while True:
|
|
563
|
+
i += 1
|
|
564
|
+
if not (i < len(tokens)):
|
|
565
|
+
break
|
|
566
|
+
tk = tokens[i]
|
|
567
|
+
if tk.group_type() == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
568
|
+
if i + 1 >= len(tokens):
|
|
569
|
+
return err_syntax('undefined anchor value', tk.raw_token())
|
|
570
|
+
value_tk = tokens[i + 1]
|
|
571
|
+
if tk.line() == value_tk.line() and value_tk.group_type() == YamlParseTokenGroupType.SCALAR_TAG:
|
|
572
|
+
ret.append(YamlParseToken(
|
|
573
|
+
group=YamlParseTokenGroup(
|
|
574
|
+
type=YamlParseTokenGroupType.ANCHOR,
|
|
575
|
+
tokens=[tk, tokens[i + 1]],
|
|
576
|
+
),
|
|
577
|
+
))
|
|
578
|
+
i += 1
|
|
579
|
+
else:
|
|
580
|
+
ret.append(tk)
|
|
581
|
+
else:
|
|
582
|
+
ret.append(tk)
|
|
583
|
+
return ret
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def create_map_key_token_groups(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
587
|
+
tks = create_map_key_by_mapping_key(tokens)
|
|
588
|
+
if isinstance(tks, YamlError):
|
|
589
|
+
return tks
|
|
590
|
+
return create_map_key_by_mapping_value(tks)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
def create_map_key_by_mapping_key(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
594
|
+
ret: ta.List[YamlParseToken] = []
|
|
595
|
+
i = -1
|
|
596
|
+
while True:
|
|
597
|
+
i += 1
|
|
598
|
+
if not (i < len(tokens)):
|
|
599
|
+
break
|
|
600
|
+
tk = tokens[i]
|
|
601
|
+
if tk.type() == YamlTokenType.MAPPING_KEY:
|
|
602
|
+
if i + 1 >= len(tokens):
|
|
603
|
+
return err_syntax('undefined map key', tk.raw_token())
|
|
604
|
+
ret.append(YamlParseToken(
|
|
605
|
+
group=YamlParseTokenGroup(
|
|
606
|
+
type=YamlParseTokenGroupType.MAP_KEY,
|
|
607
|
+
tokens=[tk, tokens[i + 1]],
|
|
608
|
+
),
|
|
609
|
+
))
|
|
610
|
+
i += 1
|
|
611
|
+
else:
|
|
612
|
+
ret.append(tk)
|
|
613
|
+
return ret
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def create_map_key_by_mapping_value(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
617
|
+
ret: ta.List[YamlParseToken] = []
|
|
618
|
+
i = -1
|
|
619
|
+
while True:
|
|
620
|
+
i += 1
|
|
621
|
+
if not (i < len(tokens)):
|
|
622
|
+
break
|
|
623
|
+
tk = tokens[i]
|
|
624
|
+
if tk.type() == YamlTokenType.MAPPING_VALUE:
|
|
625
|
+
if i == 0:
|
|
626
|
+
return err_syntax('unexpected key name', tk.raw_token())
|
|
627
|
+
map_key_tk = tokens[i - 1]
|
|
628
|
+
if is_not_map_key_type(map_key_tk):
|
|
629
|
+
return err_syntax('found an invalid key for this map', tokens[i].raw_token())
|
|
630
|
+
new_tk = YamlParseToken(
|
|
631
|
+
token=map_key_tk.token,
|
|
632
|
+
group=map_key_tk.group,
|
|
633
|
+
)
|
|
634
|
+
map_key_tk.token = None
|
|
635
|
+
map_key_tk.group = YamlParseTokenGroup(
|
|
636
|
+
type=YamlParseTokenGroupType.MAP_KEY,
|
|
637
|
+
tokens=[new_tk, tk],
|
|
638
|
+
)
|
|
639
|
+
else:
|
|
640
|
+
ret.append(tk)
|
|
641
|
+
return ret
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def create_map_key_value_token_groups(tokens: ta.List[YamlParseToken]) -> ta.List[YamlParseToken]:
|
|
645
|
+
ret: ta.List[YamlParseToken] = []
|
|
646
|
+
i = -1
|
|
647
|
+
while True:
|
|
648
|
+
i += 1
|
|
649
|
+
if not (i < len(tokens)):
|
|
650
|
+
break
|
|
651
|
+
tk = tokens[i]
|
|
652
|
+
if tk.group_type() == YamlParseTokenGroupType.MAP_KEY:
|
|
653
|
+
if len(tokens) <= i + 1:
|
|
654
|
+
ret.append(tk)
|
|
655
|
+
continue
|
|
656
|
+
value_tk = tokens[i + 1]
|
|
657
|
+
if tk.line() != value_tk.line():
|
|
658
|
+
ret.append(tk)
|
|
659
|
+
continue
|
|
660
|
+
if value_tk.group_type() == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
661
|
+
ret.append(tk)
|
|
662
|
+
continue
|
|
663
|
+
if (
|
|
664
|
+
value_tk.type() == YamlTokenType.TAG and
|
|
665
|
+
value_tk.group_type() != YamlParseTokenGroupType.SCALAR_TAG
|
|
666
|
+
):
|
|
667
|
+
ret.append(tk)
|
|
668
|
+
continue
|
|
669
|
+
|
|
670
|
+
if is_scalar_type(value_tk) or value_tk.type() == YamlTokenType.TAG:
|
|
671
|
+
ret.append(YamlParseToken(
|
|
672
|
+
group=YamlParseTokenGroup(
|
|
673
|
+
type=YamlParseTokenGroupType.MAP_KEY_VALUE,
|
|
674
|
+
tokens=[tk, value_tk],
|
|
675
|
+
),
|
|
676
|
+
))
|
|
677
|
+
i += 1
|
|
678
|
+
else:
|
|
679
|
+
ret.append(tk)
|
|
680
|
+
continue
|
|
681
|
+
else:
|
|
682
|
+
ret.append(tk)
|
|
683
|
+
return ret
|
|
684
|
+
|
|
685
|
+
|
|
686
|
+
def create_directive_token_groups(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
687
|
+
ret: ta.List[YamlParseToken] = []
|
|
688
|
+
i = -1
|
|
689
|
+
while True:
|
|
690
|
+
i += 1
|
|
691
|
+
if not (i < len(tokens)):
|
|
692
|
+
break
|
|
693
|
+
tk = tokens[i]
|
|
694
|
+
if tk.type() == YamlTokenType.DIRECTIVE:
|
|
695
|
+
if i + 1 >= len(tokens):
|
|
696
|
+
return err_syntax('undefined directive value', tk.raw_token())
|
|
697
|
+
directive_name = YamlParseToken(
|
|
698
|
+
group=YamlParseTokenGroup(
|
|
699
|
+
type=YamlParseTokenGroupType.DIRECTIVE_NAME,
|
|
700
|
+
tokens=[tk, tokens[i + 1]],
|
|
701
|
+
),
|
|
702
|
+
)
|
|
703
|
+
i += 1
|
|
704
|
+
value_tks: ta.List[YamlParseToken] = []
|
|
705
|
+
for j in range(i + 1, len(tokens)):
|
|
706
|
+
if tokens[j].line() != tk.line():
|
|
707
|
+
break
|
|
708
|
+
value_tks.append(tokens[j])
|
|
709
|
+
i += 1
|
|
710
|
+
if i + 1 >= len(tokens) or tokens[i + 1].type() != YamlTokenType.DOCUMENT_HEADER:
|
|
711
|
+
return err_syntax('unexpected directive value. document not started', tk.raw_token())
|
|
712
|
+
if len(value_tks) != 0:
|
|
713
|
+
ret.append(YamlParseToken(
|
|
714
|
+
group=YamlParseTokenGroup(
|
|
715
|
+
type=YamlParseTokenGroupType.DIRECTIVE,
|
|
716
|
+
tokens=[directive_name, *value_tks],
|
|
717
|
+
),
|
|
718
|
+
))
|
|
719
|
+
else:
|
|
720
|
+
ret.append(directive_name)
|
|
721
|
+
else:
|
|
722
|
+
ret.append(tk)
|
|
723
|
+
return ret
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def create_document_tokens(tokens: ta.List[YamlParseToken]) -> YamlErrorOr[ta.List[YamlParseToken]]:
|
|
727
|
+
ret: ta.List[YamlParseToken] = []
|
|
728
|
+
i = -1
|
|
729
|
+
while True:
|
|
730
|
+
i += 1
|
|
731
|
+
if not (i < len(tokens)):
|
|
732
|
+
break
|
|
733
|
+
tk = tokens[i]
|
|
734
|
+
if tk.type() == YamlTokenType.DOCUMENT_HEADER:
|
|
735
|
+
if i != 0:
|
|
736
|
+
ret.append(YamlParseToken(
|
|
737
|
+
group=YamlParseTokenGroup(tokens=tokens[:i]),
|
|
738
|
+
))
|
|
739
|
+
if i + 1 == len(tokens):
|
|
740
|
+
# if current token is last token, add DocumentHeader only tokens to ret.
|
|
741
|
+
ret.append(YamlParseToken(
|
|
742
|
+
group=YamlParseTokenGroup(
|
|
743
|
+
type=YamlParseTokenGroupType.DOCUMENT,
|
|
744
|
+
tokens=[tk],
|
|
745
|
+
),
|
|
746
|
+
))
|
|
747
|
+
return ret
|
|
748
|
+
if tokens[i + 1].type() == YamlTokenType.DOCUMENT_HEADER:
|
|
749
|
+
ret.append(YamlParseToken(
|
|
750
|
+
group=YamlParseTokenGroup(
|
|
751
|
+
type=YamlParseTokenGroupType.DOCUMENT,
|
|
752
|
+
tokens=[tk],
|
|
753
|
+
),
|
|
754
|
+
))
|
|
755
|
+
return ret
|
|
756
|
+
if tokens[i].line() == tokens[i + 1].line():
|
|
757
|
+
if tokens[i + 1].group_type() in (
|
|
758
|
+
YamlParseTokenGroupType.MAP_KEY,
|
|
759
|
+
YamlParseTokenGroupType.MAP_KEY_VALUE,
|
|
760
|
+
):
|
|
761
|
+
return err_syntax(
|
|
762
|
+
'value cannot be placed after document separator',
|
|
763
|
+
tokens[i + 1].raw_token(),
|
|
764
|
+
)
|
|
765
|
+
if tokens[i + 1].type() == YamlTokenType.SEQUENCE_ENTRY:
|
|
766
|
+
return err_syntax(
|
|
767
|
+
'value cannot be placed after document separator',
|
|
768
|
+
tokens[i + 1].raw_token(),
|
|
769
|
+
)
|
|
770
|
+
tks = create_document_tokens(tokens[i + 1:])
|
|
771
|
+
if isinstance(tks, YamlError):
|
|
772
|
+
return tks
|
|
773
|
+
if len(tks) != 0:
|
|
774
|
+
tks[0].set_group_type(YamlParseTokenGroupType.DOCUMENT)
|
|
775
|
+
check.not_none(tks[0].group).tokens = list(check.not_none(tks[0].group).tokens)
|
|
776
|
+
ret.extend(tks)
|
|
777
|
+
return ret
|
|
778
|
+
ret.append(YamlParseToken(
|
|
779
|
+
group=YamlParseTokenGroup(
|
|
780
|
+
type=YamlParseTokenGroupType.DOCUMENT,
|
|
781
|
+
tokens=[tk],
|
|
782
|
+
),
|
|
783
|
+
))
|
|
784
|
+
return ret
|
|
785
|
+
elif tk.type() == YamlTokenType.DOCUMENT_END:
|
|
786
|
+
if i != 0:
|
|
787
|
+
ret.append(YamlParseToken(
|
|
788
|
+
group=YamlParseTokenGroup(
|
|
789
|
+
type=YamlParseTokenGroupType.DOCUMENT,
|
|
790
|
+
tokens=tokens[0: i + 1],
|
|
791
|
+
),
|
|
792
|
+
))
|
|
793
|
+
if i + 1 == len(tokens):
|
|
794
|
+
return ret
|
|
795
|
+
if is_scalar_type(tokens[i + 1]):
|
|
796
|
+
return err_syntax('unexpected end content', tokens[i + 1].raw_token())
|
|
797
|
+
|
|
798
|
+
tks = create_document_tokens(tokens[i + 1:])
|
|
799
|
+
if isinstance(tks, YamlError):
|
|
800
|
+
return tks
|
|
801
|
+
ret.extend(tks)
|
|
802
|
+
return ret
|
|
803
|
+
ret.append(YamlParseToken(
|
|
804
|
+
group=YamlParseTokenGroup(
|
|
805
|
+
type=YamlParseTokenGroupType.DOCUMENT,
|
|
806
|
+
tokens=tokens,
|
|
807
|
+
),
|
|
808
|
+
))
|
|
809
|
+
return ret
|
|
810
|
+
|
|
811
|
+
|
|
812
|
+
def is_scalar_type(tk: YamlParseToken) -> bool:
|
|
813
|
+
if tk.group_type() in (YamlParseTokenGroupType.MAP_KEY, YamlParseTokenGroupType.MAP_KEY_VALUE):
|
|
814
|
+
return False
|
|
815
|
+
typ = tk.type()
|
|
816
|
+
return typ in (
|
|
817
|
+
YamlTokenType.ANCHOR,
|
|
818
|
+
YamlTokenType.ALIAS,
|
|
819
|
+
YamlTokenType.LITERAL,
|
|
820
|
+
YamlTokenType.FOLDED,
|
|
821
|
+
YamlTokenType.NULL,
|
|
822
|
+
YamlTokenType.IMPLICIT_NULL,
|
|
823
|
+
YamlTokenType.BOOL,
|
|
824
|
+
YamlTokenType.INTEGER,
|
|
825
|
+
YamlTokenType.BINARY_INTEGER,
|
|
826
|
+
YamlTokenType.OCTET_INTEGER,
|
|
827
|
+
YamlTokenType.HEX_INTEGER,
|
|
828
|
+
YamlTokenType.FLOAT,
|
|
829
|
+
YamlTokenType.INFINITY,
|
|
830
|
+
YamlTokenType.NAN,
|
|
831
|
+
YamlTokenType.STRING,
|
|
832
|
+
YamlTokenType.SINGLE_QUOTE,
|
|
833
|
+
YamlTokenType.DOUBLE_QUOTE,
|
|
834
|
+
)
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def is_not_map_key_type(tk: YamlParseToken) -> bool:
|
|
838
|
+
typ = tk.type()
|
|
839
|
+
return typ in (
|
|
840
|
+
YamlTokenType.DIRECTIVE,
|
|
841
|
+
YamlTokenType.DOCUMENT_HEADER,
|
|
842
|
+
YamlTokenType.DOCUMENT_END,
|
|
843
|
+
YamlTokenType.COLLECT_ENTRY,
|
|
844
|
+
YamlTokenType.MAPPING_START,
|
|
845
|
+
YamlTokenType.MAPPING_VALUE,
|
|
846
|
+
YamlTokenType.MAPPING_END,
|
|
847
|
+
YamlTokenType.SEQUENCE_START,
|
|
848
|
+
YamlTokenType.SEQUENCE_ENTRY,
|
|
849
|
+
YamlTokenType.SEQUENCE_END,
|
|
850
|
+
)
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def is_flow_type(tk: YamlParseToken) -> bool:
|
|
854
|
+
typ = tk.type()
|
|
855
|
+
return typ in (
|
|
856
|
+
YamlTokenType.MAPPING_START,
|
|
857
|
+
YamlTokenType.MAPPING_END,
|
|
858
|
+
YamlTokenType.SEQUENCE_START,
|
|
859
|
+
YamlTokenType.SEQUENCE_ENTRY,
|
|
860
|
+
)
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
##
|
|
864
|
+
|
|
865
|
+
|
|
866
|
+
class YamlNodeMakers:
|
|
867
|
+
def __new__(cls, *args, **kwargs): # noqa
|
|
868
|
+
raise TypeError
|
|
869
|
+
|
|
870
|
+
@staticmethod
|
|
871
|
+
def new_mapping_node(
|
|
872
|
+
ctx: YamlParsingContext,
|
|
873
|
+
tk: YamlParseToken,
|
|
874
|
+
is_flow: bool,
|
|
875
|
+
*values: ast.MappingValueYamlNode,
|
|
876
|
+
) -> YamlErrorOr[ast.MappingYamlNode]:
|
|
877
|
+
node = ast.mapping(check.not_none(tk.raw_token()), is_flow, *values)
|
|
878
|
+
node.set_path(ctx.path)
|
|
879
|
+
return node
|
|
880
|
+
|
|
881
|
+
@staticmethod
|
|
882
|
+
def new_mapping_value_node(
|
|
883
|
+
ctx: YamlParsingContext,
|
|
884
|
+
colon_tk: YamlParseToken,
|
|
885
|
+
entry_tk: ta.Optional[YamlParseToken],
|
|
886
|
+
key: ast.MapKeyYamlNode,
|
|
887
|
+
value: ast.YamlNode,
|
|
888
|
+
) -> YamlErrorOr[ast.MappingValueYamlNode]:
|
|
889
|
+
node = ast.mapping_value(check.not_none(colon_tk.raw_token()), key, value)
|
|
890
|
+
node.set_path(ctx.path)
|
|
891
|
+
node.collect_entry = YamlParseToken.raw_token(entry_tk)
|
|
892
|
+
if check.not_none(key.get_token()).position.line == check.not_none(value.get_token()).position.line:
|
|
893
|
+
# originally key was commented, but now that null value has been added, value must be commented.
|
|
894
|
+
if (err := set_line_comment(ctx, value, colon_tk)) is not None:
|
|
895
|
+
return err
|
|
896
|
+
# set line comment by colon_tk or entry_tk.
|
|
897
|
+
if (err := set_line_comment(ctx, value, entry_tk)) is not None:
|
|
898
|
+
return err
|
|
899
|
+
else:
|
|
900
|
+
if (err := set_line_comment(ctx, key, colon_tk)) is not None:
|
|
901
|
+
return err
|
|
902
|
+
# set line comment by colon_tk or entry_tk.
|
|
903
|
+
if (err := set_line_comment(ctx, key, entry_tk)) is not None:
|
|
904
|
+
return err
|
|
905
|
+
return node
|
|
906
|
+
|
|
907
|
+
@staticmethod
|
|
908
|
+
def new_mapping_key_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.MappingKeyYamlNode]: # noqa
|
|
909
|
+
node = ast.mapping_key(check.not_none(YamlParseToken.raw_token(tk)))
|
|
910
|
+
node.set_path(ctx.path)
|
|
911
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
912
|
+
return err
|
|
913
|
+
return node
|
|
914
|
+
|
|
915
|
+
@staticmethod
|
|
916
|
+
def new_anchor_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.AnchorYamlNode]:
|
|
917
|
+
node = ast.anchor(check.not_none(YamlParseToken.raw_token(tk)))
|
|
918
|
+
node.set_path(ctx.path)
|
|
919
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
920
|
+
return err
|
|
921
|
+
return node
|
|
922
|
+
|
|
923
|
+
@staticmethod
|
|
924
|
+
def new_alias_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.AliasYamlNode]:
|
|
925
|
+
node = ast.alias(check.not_none(YamlParseToken.raw_token(tk)))
|
|
926
|
+
node.set_path(ctx.path)
|
|
927
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
928
|
+
return err
|
|
929
|
+
return node
|
|
930
|
+
|
|
931
|
+
@staticmethod
|
|
932
|
+
def new_directive_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.DirectiveYamlNode]: # noqa
|
|
933
|
+
node = ast.directive(check.not_none(YamlParseToken.raw_token(tk)))
|
|
934
|
+
node.set_path(ctx.path)
|
|
935
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
936
|
+
return err
|
|
937
|
+
return node
|
|
938
|
+
|
|
939
|
+
@staticmethod
|
|
940
|
+
def new_merge_key_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.MergeKeyYamlNode]: # noqa
|
|
941
|
+
node = ast.merge_key(check.not_none(YamlParseToken.raw_token(tk)))
|
|
942
|
+
node.set_path(ctx.path)
|
|
943
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
944
|
+
return err
|
|
945
|
+
return node
|
|
946
|
+
|
|
947
|
+
@staticmethod
|
|
948
|
+
def new_null_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.NullYamlNode]:
|
|
949
|
+
node = ast.null(check.not_none(YamlParseToken.raw_token(tk)))
|
|
950
|
+
node.set_path(ctx.path)
|
|
951
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
952
|
+
return err
|
|
953
|
+
return node
|
|
954
|
+
|
|
955
|
+
@staticmethod
|
|
956
|
+
def new_bool_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.BoolYamlNode]:
|
|
957
|
+
node = ast.bool_(check.not_none(YamlParseToken.raw_token(tk)))
|
|
958
|
+
node.set_path(ctx.path)
|
|
959
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
960
|
+
return err
|
|
961
|
+
return node
|
|
962
|
+
|
|
963
|
+
@staticmethod
|
|
964
|
+
def new_integer_node(ctx: YamlParsingContext, tk: YamlParseToken) -> YamlErrorOr[ast.IntegerYamlNode]:
|
|
965
|
+
node = ast.integer(check.not_none(YamlParseToken.raw_token(tk)))
|
|
966
|
+
node.set_path(ctx.path)
|
|
967
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
968
|
+
return err
|
|
969
|
+
return node
|
|
970
|
+
|
|
971
|
+
@staticmethod
|
|
972
|
+
def new_float_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.FloatYamlNode]:
|
|
973
|
+
node = ast.float_(check.not_none(YamlParseToken.raw_token(tk)))
|
|
974
|
+
node.set_path(ctx.path)
|
|
975
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
976
|
+
return err
|
|
977
|
+
return node
|
|
978
|
+
|
|
979
|
+
@staticmethod
|
|
980
|
+
def new_infinity_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.InfinityYamlNode]: # noqa
|
|
981
|
+
node = ast.infinity(check.not_none(YamlParseToken.raw_token(tk)))
|
|
982
|
+
node.set_path(ctx.path)
|
|
983
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
984
|
+
return err
|
|
985
|
+
return node
|
|
986
|
+
|
|
987
|
+
@staticmethod
|
|
988
|
+
def new_nan_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.NanYamlNode]:
|
|
989
|
+
node = ast.nan(check.not_none(YamlParseToken.raw_token(tk)))
|
|
990
|
+
node.set_path(ctx.path)
|
|
991
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
992
|
+
return err
|
|
993
|
+
return node
|
|
994
|
+
|
|
995
|
+
@staticmethod
|
|
996
|
+
def new_string_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.StringYamlNode]:
|
|
997
|
+
node = ast.string(check.not_none(YamlParseToken.raw_token(tk)))
|
|
998
|
+
node.set_path(ctx.path)
|
|
999
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
1000
|
+
return err
|
|
1001
|
+
return node
|
|
1002
|
+
|
|
1003
|
+
@staticmethod
|
|
1004
|
+
def new_literal_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.LiteralYamlNode]:
|
|
1005
|
+
node = ast.literal(check.not_none(YamlParseToken.raw_token(tk)))
|
|
1006
|
+
node.set_path(ctx.path)
|
|
1007
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
1008
|
+
return err
|
|
1009
|
+
return node
|
|
1010
|
+
|
|
1011
|
+
@staticmethod
|
|
1012
|
+
def new_tag_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.TagYamlNode]:
|
|
1013
|
+
node = ast.tag(check.not_none(YamlParseToken.raw_token(tk)))
|
|
1014
|
+
node.set_path(ctx.path)
|
|
1015
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
1016
|
+
return err
|
|
1017
|
+
return node
|
|
1018
|
+
|
|
1019
|
+
@staticmethod
|
|
1020
|
+
def new_sequence_node(ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken], is_flow: bool) -> YamlErrorOr[ast.SequenceYamlNode]: # noqa
|
|
1021
|
+
node = ast.sequence(check.not_none(YamlParseToken.raw_token(tk)), is_flow)
|
|
1022
|
+
node.set_path(ctx.path)
|
|
1023
|
+
if (err := set_line_comment(ctx, node, tk)) is not None:
|
|
1024
|
+
return err
|
|
1025
|
+
return node
|
|
1026
|
+
|
|
1027
|
+
@staticmethod
|
|
1028
|
+
def new_tag_default_scalar_value_node(ctx: YamlParsingContext, tag: YamlToken) -> YamlErrorOr[ast.ScalarYamlNode]:
|
|
1029
|
+
pos = copy.copy(tag.position)
|
|
1030
|
+
pos.column += 1
|
|
1031
|
+
|
|
1032
|
+
tk: YamlErrorOr[YamlParseToken]
|
|
1033
|
+
node: YamlErrorOr[ast.ScalarYamlNode]
|
|
1034
|
+
|
|
1035
|
+
if tag.value == YamlReservedTagKeywords.INTEGER:
|
|
1036
|
+
tk = YamlParseToken(token=new_yaml_token('0', '0', pos))
|
|
1037
|
+
n0 = YamlNodeMakers.new_integer_node(ctx, tk)
|
|
1038
|
+
if isinstance(n0, YamlError):
|
|
1039
|
+
return n0
|
|
1040
|
+
node = n0
|
|
1041
|
+
elif tag.value == YamlReservedTagKeywords.FLOAT:
|
|
1042
|
+
tk = YamlParseToken(token=new_yaml_token('0', '0', pos))
|
|
1043
|
+
n1 = YamlNodeMakers.new_float_node(ctx, tk)
|
|
1044
|
+
if isinstance(n1, YamlError):
|
|
1045
|
+
return n1
|
|
1046
|
+
node = n1
|
|
1047
|
+
elif tag.value in (
|
|
1048
|
+
YamlReservedTagKeywords.STRING,
|
|
1049
|
+
YamlReservedTagKeywords.BINARY,
|
|
1050
|
+
YamlReservedTagKeywords.TIMESTAMP,
|
|
1051
|
+
):
|
|
1052
|
+
tk = YamlParseToken(token=new_yaml_token('', '', pos))
|
|
1053
|
+
n2 = YamlNodeMakers.new_string_node(ctx, tk)
|
|
1054
|
+
if isinstance(n2, YamlError):
|
|
1055
|
+
return n2
|
|
1056
|
+
node = n2
|
|
1057
|
+
elif tag.value == YamlReservedTagKeywords.BOOLEAN:
|
|
1058
|
+
tk = YamlParseToken(token=new_yaml_token('false', 'false', pos))
|
|
1059
|
+
n3 = YamlNodeMakers.new_bool_node(ctx, tk)
|
|
1060
|
+
if isinstance(n3, YamlError):
|
|
1061
|
+
return n3
|
|
1062
|
+
node = n3
|
|
1063
|
+
elif tag.value == YamlReservedTagKeywords.NULL:
|
|
1064
|
+
tk = YamlParseToken(token=new_yaml_token('null', 'null', pos))
|
|
1065
|
+
n4 = YamlNodeMakers.new_null_node(ctx, tk)
|
|
1066
|
+
if isinstance(n4, YamlError):
|
|
1067
|
+
return n4
|
|
1068
|
+
node = n4
|
|
1069
|
+
else:
|
|
1070
|
+
return err_syntax(f'cannot assign default value for {tag.value!r} tag', tag)
|
|
1071
|
+
ctx.insert_token(tk)
|
|
1072
|
+
ctx.go_next()
|
|
1073
|
+
return node
|
|
1074
|
+
|
|
1075
|
+
|
|
1076
|
+
def set_line_comment(ctx: YamlParsingContext, node: ast.YamlNode, tk: ta.Optional[YamlParseToken]) -> ta.Optional[YamlError]: # noqa
|
|
1077
|
+
if tk is None or tk.line_comment is None:
|
|
1078
|
+
return None
|
|
1079
|
+
comment = ast.comment_group([tk.line_comment])
|
|
1080
|
+
comment.set_path(ctx.path)
|
|
1081
|
+
if (err := node.set_comment(comment)) is not None:
|
|
1082
|
+
return err
|
|
1083
|
+
return None
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
def set_head_comment(cm: ta.Optional[ast.CommentGroupYamlNode], value: ast.YamlNode) -> ta.Optional[YamlError]:
|
|
1087
|
+
if cm is None:
|
|
1088
|
+
return None
|
|
1089
|
+
n = value
|
|
1090
|
+
if isinstance(n, ast.MappingYamlNode):
|
|
1091
|
+
if len(n.values) != 0 and value.get_comment() is None:
|
|
1092
|
+
cm.set_path(n.values[0].get_path())
|
|
1093
|
+
return n.values[0].set_comment(cm)
|
|
1094
|
+
elif isinstance(n, ast.MappingValueYamlNode):
|
|
1095
|
+
cm.set_path(n.get_path())
|
|
1096
|
+
return n.set_comment(cm)
|
|
1097
|
+
cm.set_path(value.get_path())
|
|
1098
|
+
return value.set_comment(cm)
|
|
1099
|
+
|
|
1100
|
+
|
|
1101
|
+
##
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
ParseMode = int # ta.TypeAlias # omlish-amalg-typing-no-move
|
|
1105
|
+
|
|
1106
|
+
PARSE_COMMENTS = ParseMode(1) # parse comments and add them to AST
|
|
1107
|
+
|
|
1108
|
+
|
|
1109
|
+
# ParseBytes parse from byte slice, and returns ast.File
|
|
1110
|
+
def parse_str(
|
|
1111
|
+
s: str,
|
|
1112
|
+
mode: ParseMode = ParseMode(0),
|
|
1113
|
+
*opts: Option,
|
|
1114
|
+
) -> YamlErrorOr[ast.YamlFile]:
|
|
1115
|
+
tokens = yaml_tokenize(s)
|
|
1116
|
+
f = parse(tokens, mode, *opts)
|
|
1117
|
+
if isinstance(f, YamlError):
|
|
1118
|
+
return f
|
|
1119
|
+
return f
|
|
1120
|
+
|
|
1121
|
+
|
|
1122
|
+
# Parse parse from token instances, and returns ast.File
|
|
1123
|
+
def parse(
|
|
1124
|
+
tokens: YamlTokens,
|
|
1125
|
+
mode: ParseMode = ParseMode(0),
|
|
1126
|
+
*opts: Option,
|
|
1127
|
+
) -> YamlErrorOr[ast.YamlFile]:
|
|
1128
|
+
if (tk := tokens.invalid_token()) is not None:
|
|
1129
|
+
return err_syntax(check.not_none(tk.error).message, tk)
|
|
1130
|
+
p = YamlParser.new_parser(tokens, mode, opts)
|
|
1131
|
+
if isinstance(p, YamlError):
|
|
1132
|
+
return p
|
|
1133
|
+
f = p.parse(YamlParsingContext.new())
|
|
1134
|
+
if isinstance(f, YamlError):
|
|
1135
|
+
return f
|
|
1136
|
+
return f
|
|
1137
|
+
|
|
1138
|
+
|
|
1139
|
+
#
|
|
1140
|
+
|
|
1141
|
+
|
|
1142
|
+
YAMLVersion = str # ta.TypeAlias # omlish-amalg-typing-no-move
|
|
1143
|
+
|
|
1144
|
+
YAML10 = YAMLVersion('1.0')
|
|
1145
|
+
YAML11 = YAMLVersion('1.1')
|
|
1146
|
+
YAML12 = YAMLVersion('1.2')
|
|
1147
|
+
YAML13 = YAMLVersion('1.3')
|
|
1148
|
+
|
|
1149
|
+
YAML_VERSION_MAP: ta.Mapping[str, YAMLVersion] = {
|
|
1150
|
+
'1.0': YAML10,
|
|
1151
|
+
'1.1': YAML11,
|
|
1152
|
+
'1.2': YAML12,
|
|
1153
|
+
'1.3': YAML13,
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
|
|
1157
|
+
#
|
|
1158
|
+
|
|
1159
|
+
@dc.dataclass()
|
|
1160
|
+
class YamlParser:
|
|
1161
|
+
tokens: ta.List[YamlParseToken]
|
|
1162
|
+
path_map: ta.Dict[str, ast.YamlNode]
|
|
1163
|
+
yaml_version: YAMLVersion = YAMLVersion('')
|
|
1164
|
+
allow_duplicate_map_key: bool = False
|
|
1165
|
+
secondary_tag_directive: ta.Optional[ast.DirectiveYamlNode] = None
|
|
1166
|
+
|
|
1167
|
+
@staticmethod
|
|
1168
|
+
def new_parser(
|
|
1169
|
+
tokens: YamlTokens,
|
|
1170
|
+
mode: ParseMode,
|
|
1171
|
+
opts: ta.Iterable[Option],
|
|
1172
|
+
) -> YamlErrorOr['YamlParser']:
|
|
1173
|
+
filtered_tokens: ta.List[YamlToken] = []
|
|
1174
|
+
if mode & PARSE_COMMENTS != 0:
|
|
1175
|
+
filtered_tokens = tokens
|
|
1176
|
+
else:
|
|
1177
|
+
for tk in tokens:
|
|
1178
|
+
if tk.type == YamlTokenType.COMMENT:
|
|
1179
|
+
continue
|
|
1180
|
+
# keep prev/next reference between tokens containing comments
|
|
1181
|
+
# https://github.com/goccy/go-yaml/issues/254
|
|
1182
|
+
filtered_tokens.append(tk)
|
|
1183
|
+
tks = create_grouped_tokens(YamlTokens(filtered_tokens))
|
|
1184
|
+
if isinstance(tks, YamlError):
|
|
1185
|
+
return tks
|
|
1186
|
+
p = YamlParser(
|
|
1187
|
+
tokens=tks,
|
|
1188
|
+
path_map={},
|
|
1189
|
+
)
|
|
1190
|
+
for opt in opts:
|
|
1191
|
+
opt(p)
|
|
1192
|
+
return p
|
|
1193
|
+
|
|
1194
|
+
def parse(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.YamlFile]:
|
|
1195
|
+
file = ast.YamlFile(docs=[])
|
|
1196
|
+
for token in self.tokens:
|
|
1197
|
+
doc = self.parse_document(ctx, check.not_none(token.group))
|
|
1198
|
+
if isinstance(doc, YamlError):
|
|
1199
|
+
return doc
|
|
1200
|
+
file.docs.append(doc)
|
|
1201
|
+
return file
|
|
1202
|
+
|
|
1203
|
+
def parse_document(
|
|
1204
|
+
self,
|
|
1205
|
+
ctx: YamlParsingContext,
|
|
1206
|
+
doc_group: YamlParseTokenGroup,
|
|
1207
|
+
) -> YamlErrorOr[ast.DocumentYamlNode]:
|
|
1208
|
+
if len(doc_group.tokens) == 0:
|
|
1209
|
+
return ast.document(doc_group.raw_token(), None)
|
|
1210
|
+
|
|
1211
|
+
self.path_map: ta.Dict[str, ast.YamlNode] = {}
|
|
1212
|
+
|
|
1213
|
+
tokens = doc_group.tokens
|
|
1214
|
+
start: ta.Optional[YamlToken] = None
|
|
1215
|
+
end: ta.Optional[YamlToken] = None
|
|
1216
|
+
if YamlParseToken.type(doc_group.first()) == YamlTokenType.DOCUMENT_HEADER:
|
|
1217
|
+
start = YamlParseToken.raw_token(doc_group.first())
|
|
1218
|
+
tokens = tokens[1:]
|
|
1219
|
+
|
|
1220
|
+
clear_yaml_version = False
|
|
1221
|
+
try:
|
|
1222
|
+
if YamlParseToken.type(doc_group.last()) == YamlTokenType.DOCUMENT_END:
|
|
1223
|
+
end = YamlParseToken.raw_token(doc_group.last())
|
|
1224
|
+
tokens = tokens[:len(tokens) - 1]
|
|
1225
|
+
# clear yaml version value if DocumentEnd token (...) is specified.
|
|
1226
|
+
clear_yaml_version = True
|
|
1227
|
+
|
|
1228
|
+
if len(tokens) == 0:
|
|
1229
|
+
return ast.document(doc_group.raw_token(), None)
|
|
1230
|
+
|
|
1231
|
+
body = self.parse_document_body(ctx.with_group(YamlParseTokenGroup(
|
|
1232
|
+
type=YamlParseTokenGroupType.DOCUMENT_BODY,
|
|
1233
|
+
tokens=tokens,
|
|
1234
|
+
)))
|
|
1235
|
+
if isinstance(body, YamlError):
|
|
1236
|
+
return body
|
|
1237
|
+
node = ast.document(start, body)
|
|
1238
|
+
node.end = end
|
|
1239
|
+
return node
|
|
1240
|
+
|
|
1241
|
+
finally:
|
|
1242
|
+
if clear_yaml_version:
|
|
1243
|
+
self.yaml_version = ''
|
|
1244
|
+
|
|
1245
|
+
def parse_document_body(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.YamlNode]:
|
|
1246
|
+
node = self.parse_token(ctx, ctx.current_token())
|
|
1247
|
+
if isinstance(node, YamlError):
|
|
1248
|
+
return node
|
|
1249
|
+
if ctx.next():
|
|
1250
|
+
return err_syntax('value is not allowed in this context', YamlParseToken.raw_token(ctx.current_token()))
|
|
1251
|
+
return node
|
|
1252
|
+
|
|
1253
|
+
def parse_token(self, ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ast.YamlNode]:
|
|
1254
|
+
if YamlParseToken.group_type(tk) in (
|
|
1255
|
+
YamlParseTokenGroupType.MAP_KEY,
|
|
1256
|
+
YamlParseTokenGroupType.MAP_KEY_VALUE,
|
|
1257
|
+
):
|
|
1258
|
+
return self.parse_map(ctx)
|
|
1259
|
+
elif YamlParseToken.group_type(tk) == YamlParseTokenGroupType.DIRECTIVE:
|
|
1260
|
+
node0 = self.parse_directive(
|
|
1261
|
+
ctx.with_group(check.not_none(check.not_none(tk).group)),
|
|
1262
|
+
check.not_none(check.not_none(tk).group),
|
|
1263
|
+
)
|
|
1264
|
+
if isinstance(node0, YamlError):
|
|
1265
|
+
return node0
|
|
1266
|
+
ctx.go_next()
|
|
1267
|
+
return node0
|
|
1268
|
+
elif YamlParseToken.group_type(tk) == YamlParseTokenGroupType.DIRECTIVE_NAME:
|
|
1269
|
+
node1 = self.parse_directive_name(ctx.with_group(check.not_none(check.not_none(tk).group)))
|
|
1270
|
+
if isinstance(node1, YamlError):
|
|
1271
|
+
return node1
|
|
1272
|
+
ctx.go_next()
|
|
1273
|
+
return node1
|
|
1274
|
+
elif YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR:
|
|
1275
|
+
node2 = self.parse_anchor(
|
|
1276
|
+
ctx.with_group(check.not_none(check.not_none(tk).group)),
|
|
1277
|
+
check.not_none(check.not_none(tk).group),
|
|
1278
|
+
)
|
|
1279
|
+
if isinstance(node2, YamlError):
|
|
1280
|
+
return node2
|
|
1281
|
+
ctx.go_next()
|
|
1282
|
+
return node2
|
|
1283
|
+
elif YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
1284
|
+
anchor = self.parse_anchor_name(ctx.with_group(check.not_none(check.not_none(tk).group)))
|
|
1285
|
+
if isinstance(anchor, YamlError):
|
|
1286
|
+
return anchor
|
|
1287
|
+
ctx.go_next()
|
|
1288
|
+
if ctx.is_token_not_found():
|
|
1289
|
+
return err_syntax('could not find anchor value', YamlParseToken.raw_token(tk))
|
|
1290
|
+
value = self.parse_token(ctx, ctx.current_token())
|
|
1291
|
+
if isinstance(value, YamlError):
|
|
1292
|
+
return value
|
|
1293
|
+
if isinstance(value, ast.AnchorYamlNode):
|
|
1294
|
+
return err_syntax('anchors cannot be used consecutively', value.get_token())
|
|
1295
|
+
anchor.value = value
|
|
1296
|
+
return anchor
|
|
1297
|
+
elif YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ALIAS:
|
|
1298
|
+
node3 = self.parse_alias(ctx.with_group(check.not_none(check.not_none(tk).group)))
|
|
1299
|
+
if isinstance(node3, YamlError):
|
|
1300
|
+
return node3
|
|
1301
|
+
ctx.go_next()
|
|
1302
|
+
return node3
|
|
1303
|
+
elif YamlParseToken.group_type(tk) in (
|
|
1304
|
+
YamlParseTokenGroupType.LITERAL,
|
|
1305
|
+
YamlParseTokenGroupType.FOLDED,
|
|
1306
|
+
):
|
|
1307
|
+
node4 = self.parse_literal(ctx.with_group(check.not_none(check.not_none(tk).group)))
|
|
1308
|
+
if isinstance(node4, YamlError):
|
|
1309
|
+
return node4
|
|
1310
|
+
ctx.go_next()
|
|
1311
|
+
return node4
|
|
1312
|
+
elif YamlParseToken.group_type(tk) == YamlParseTokenGroupType.SCALAR_TAG:
|
|
1313
|
+
node5 = self.parse_tag(ctx.with_group(check.not_none(check.not_none(tk).group)))
|
|
1314
|
+
if isinstance(node5, YamlError):
|
|
1315
|
+
return node5
|
|
1316
|
+
ctx.go_next()
|
|
1317
|
+
return node5
|
|
1318
|
+
if YamlParseToken.type(tk) == YamlTokenType.COMMENT:
|
|
1319
|
+
return ta.cast('YamlErrorOr[ast.YamlNode]', check.not_none(self.parse_comment(ctx)))
|
|
1320
|
+
elif YamlParseToken.type(tk) == YamlTokenType.TAG:
|
|
1321
|
+
return self.parse_tag(ctx)
|
|
1322
|
+
elif YamlParseToken.type(tk) == YamlTokenType.MAPPING_START:
|
|
1323
|
+
return self.parse_flow_map(ctx.with_flow(True))
|
|
1324
|
+
elif YamlParseToken.type(tk) == YamlTokenType.SEQUENCE_START:
|
|
1325
|
+
return self.parse_flow_sequence(ctx.with_flow(True))
|
|
1326
|
+
elif YamlParseToken.type(tk) == YamlTokenType.SEQUENCE_ENTRY:
|
|
1327
|
+
return self.parse_sequence(ctx)
|
|
1328
|
+
elif YamlParseToken.type(tk) == YamlTokenType.SEQUENCE_END:
|
|
1329
|
+
# SequenceEndType is always validated in parse_flow_sequence.
|
|
1330
|
+
# Therefore, if this is found in other cases, it is treated as a syntax error.
|
|
1331
|
+
return err_syntax("could not find '[' character corresponding to ']'", YamlParseToken.raw_token(tk))
|
|
1332
|
+
elif YamlParseToken.type(tk) == YamlTokenType.MAPPING_END:
|
|
1333
|
+
# MappingEndType is always validated in parse_flow_map.
|
|
1334
|
+
# Therefore, if this is found in other cases, it is treated as a syntax error.
|
|
1335
|
+
return err_syntax("could not find '{' character corresponding to '}'", YamlParseToken.raw_token(tk))
|
|
1336
|
+
elif YamlParseToken.type(tk) == YamlTokenType.MAPPING_VALUE:
|
|
1337
|
+
return err_syntax('found an invalid key for this map', YamlParseToken.raw_token(tk))
|
|
1338
|
+
node6 = self.parse_scalar_value(ctx, tk)
|
|
1339
|
+
if isinstance(node6, YamlError):
|
|
1340
|
+
return node6
|
|
1341
|
+
ctx.go_next()
|
|
1342
|
+
return check.not_none(node6)
|
|
1343
|
+
|
|
1344
|
+
def parse_scalar_value(self, ctx: YamlParsingContext, tk: ta.Optional[YamlParseToken]) -> YamlErrorOr[ta.Optional[ast.ScalarYamlNode]]: # noqa
|
|
1345
|
+
tk = check.not_none(tk)
|
|
1346
|
+
if tk.group is not None:
|
|
1347
|
+
if tk.group_type() == YamlParseTokenGroupType.ANCHOR:
|
|
1348
|
+
return self.parse_anchor(ctx.with_group(tk.group), tk.group)
|
|
1349
|
+
elif tk.group_type() == YamlParseTokenGroupType.ANCHOR_NAME:
|
|
1350
|
+
anchor = self.parse_anchor_name(ctx.with_group(tk.group))
|
|
1351
|
+
if isinstance(anchor, YamlError):
|
|
1352
|
+
return anchor
|
|
1353
|
+
ctx.go_next()
|
|
1354
|
+
if ctx.is_token_not_found():
|
|
1355
|
+
return err_syntax('could not find anchor value', tk.raw_token())
|
|
1356
|
+
value = self.parse_token(ctx, ctx.current_token())
|
|
1357
|
+
if isinstance(value, YamlError):
|
|
1358
|
+
return value
|
|
1359
|
+
if isinstance(value, ast.AnchorYamlNode):
|
|
1360
|
+
return err_syntax('anchors cannot be used consecutively', value.get_token())
|
|
1361
|
+
anchor.value = value
|
|
1362
|
+
return anchor
|
|
1363
|
+
elif tk.group_type() == YamlParseTokenGroupType.ALIAS:
|
|
1364
|
+
return self.parse_alias(ctx.with_group(tk.group))
|
|
1365
|
+
elif tk.group_type() in (
|
|
1366
|
+
YamlParseTokenGroupType.LITERAL,
|
|
1367
|
+
YamlParseTokenGroupType.FOLDED,
|
|
1368
|
+
):
|
|
1369
|
+
return self.parse_literal(ctx.with_group(tk.group))
|
|
1370
|
+
elif tk.group_type() == YamlParseTokenGroupType.SCALAR_TAG:
|
|
1371
|
+
return self.parse_tag(ctx.with_group(tk.group))
|
|
1372
|
+
else:
|
|
1373
|
+
return err_syntax('unexpected scalar value', tk.raw_token())
|
|
1374
|
+
if tk.type() == YamlTokenType.MERGE_KEY:
|
|
1375
|
+
return YamlNodeMakers.new_merge_key_node(ctx, tk)
|
|
1376
|
+
if tk.type() in (YamlTokenType.NULL, YamlTokenType.IMPLICIT_NULL):
|
|
1377
|
+
return YamlNodeMakers.new_null_node(ctx, tk)
|
|
1378
|
+
if tk.type() == YamlTokenType.BOOL:
|
|
1379
|
+
return YamlNodeMakers.new_bool_node(ctx, tk)
|
|
1380
|
+
if tk.type() in (
|
|
1381
|
+
YamlTokenType.INTEGER,
|
|
1382
|
+
YamlTokenType.BINARY_INTEGER,
|
|
1383
|
+
YamlTokenType.OCTET_INTEGER,
|
|
1384
|
+
YamlTokenType.HEX_INTEGER,
|
|
1385
|
+
):
|
|
1386
|
+
return YamlNodeMakers.new_integer_node(ctx, tk)
|
|
1387
|
+
if tk.type() == YamlTokenType.FLOAT:
|
|
1388
|
+
return YamlNodeMakers.new_float_node(ctx, tk)
|
|
1389
|
+
if tk.type() == YamlTokenType.INFINITY:
|
|
1390
|
+
return YamlNodeMakers.new_infinity_node(ctx, tk)
|
|
1391
|
+
if tk.type() == YamlTokenType.NAN:
|
|
1392
|
+
return YamlNodeMakers.new_nan_node(ctx, tk)
|
|
1393
|
+
if tk.type() in (
|
|
1394
|
+
YamlTokenType.STRING,
|
|
1395
|
+
YamlTokenType.SINGLE_QUOTE,
|
|
1396
|
+
YamlTokenType.DOUBLE_QUOTE,
|
|
1397
|
+
):
|
|
1398
|
+
return YamlNodeMakers.new_string_node(ctx, tk)
|
|
1399
|
+
if tk.type() == YamlTokenType.TAG:
|
|
1400
|
+
# this case applies when it is a scalar tag and its value does not exist.
|
|
1401
|
+
# Examples of cases where the value does not exist include cases like `key: !!str,` or `!!str : value`.
|
|
1402
|
+
return self.parse_scalar_tag(ctx)
|
|
1403
|
+
return err_syntax('unexpected scalar value type', tk.raw_token())
|
|
1404
|
+
|
|
1405
|
+
def parse_flow_map(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.MappingYamlNode]:
|
|
1406
|
+
node = YamlNodeMakers.new_mapping_node(ctx, check.not_none(ctx.current_token()), True)
|
|
1407
|
+
if isinstance(node, YamlError):
|
|
1408
|
+
return node
|
|
1409
|
+
ctx.go_next() # skip MappingStart token
|
|
1410
|
+
|
|
1411
|
+
is_first = True
|
|
1412
|
+
while ctx.next():
|
|
1413
|
+
tk = ctx.current_token()
|
|
1414
|
+
if YamlParseToken.type(tk) == YamlTokenType.MAPPING_END:
|
|
1415
|
+
node.end = YamlParseToken.raw_token(tk)
|
|
1416
|
+
break
|
|
1417
|
+
|
|
1418
|
+
entry_tk: ta.Optional[YamlParseToken] = None
|
|
1419
|
+
if YamlParseToken.type(tk) == YamlTokenType.COLLECT_ENTRY:
|
|
1420
|
+
entry_tk = tk
|
|
1421
|
+
ctx.go_next()
|
|
1422
|
+
elif not is_first:
|
|
1423
|
+
return err_syntax("',' or '}' must be specified", YamlParseToken.raw_token(tk))
|
|
1424
|
+
|
|
1425
|
+
if YamlParseToken.type(tk := ctx.current_token()) == YamlTokenType.MAPPING_END:
|
|
1426
|
+
# this case is here: "{ elem, }".
|
|
1427
|
+
# In this case, ignore the last element and break mapping parsing.
|
|
1428
|
+
node.end = YamlParseToken.raw_token(tk)
|
|
1429
|
+
break
|
|
1430
|
+
|
|
1431
|
+
map_key_tk = ctx.current_token()
|
|
1432
|
+
if YamlParseToken.group_type(map_key_tk) == YamlParseTokenGroupType.MAP_KEY_VALUE:
|
|
1433
|
+
value0 = self.parse_map_key_value(
|
|
1434
|
+
ctx.with_group(check.not_none(check.not_none(map_key_tk).group)),
|
|
1435
|
+
check.not_none(check.not_none(map_key_tk).group),
|
|
1436
|
+
entry_tk,
|
|
1437
|
+
)
|
|
1438
|
+
if isinstance(value0, YamlError):
|
|
1439
|
+
return value0
|
|
1440
|
+
node.values.append(value0)
|
|
1441
|
+
ctx.go_next()
|
|
1442
|
+
elif YamlParseToken.group_type(map_key_tk) == YamlParseTokenGroupType.MAP_KEY:
|
|
1443
|
+
key0 = self.parse_map_key(
|
|
1444
|
+
ctx.with_group(check.not_none(check.not_none(map_key_tk).group)),
|
|
1445
|
+
check.not_none(check.not_none(map_key_tk).group),
|
|
1446
|
+
)
|
|
1447
|
+
if isinstance(key0, YamlError):
|
|
1448
|
+
return key0
|
|
1449
|
+
ctx = ctx.with_child(self.map_key_text(key0))
|
|
1450
|
+
colon_tk = check.not_none(check.not_none(map_key_tk).group).last()
|
|
1451
|
+
if self.is_flow_map_delim(check.not_none(ctx.next_token())):
|
|
1452
|
+
value1 = YamlNodeMakers.new_null_node(ctx, ctx.insert_null_token(check.not_none(colon_tk)))
|
|
1453
|
+
if isinstance(value1, YamlError):
|
|
1454
|
+
return value1
|
|
1455
|
+
map_value = YamlNodeMakers.new_mapping_value_node(
|
|
1456
|
+
ctx,
|
|
1457
|
+
check.not_none(colon_tk),
|
|
1458
|
+
entry_tk,
|
|
1459
|
+
key0,
|
|
1460
|
+
value1,
|
|
1461
|
+
)
|
|
1462
|
+
if isinstance(map_value, YamlError):
|
|
1463
|
+
return map_value
|
|
1464
|
+
node.values.append(map_value)
|
|
1465
|
+
ctx.go_next()
|
|
1466
|
+
else:
|
|
1467
|
+
ctx.go_next()
|
|
1468
|
+
if ctx.is_token_not_found():
|
|
1469
|
+
return err_syntax('could not find map value', YamlParseToken.raw_token(colon_tk))
|
|
1470
|
+
value2 = self.parse_token(ctx, ctx.current_token())
|
|
1471
|
+
if isinstance(value2, YamlError):
|
|
1472
|
+
return value2
|
|
1473
|
+
map_value = YamlNodeMakers.new_mapping_value_node(
|
|
1474
|
+
ctx,
|
|
1475
|
+
check.not_none(colon_tk),
|
|
1476
|
+
entry_tk,
|
|
1477
|
+
key0,
|
|
1478
|
+
value2,
|
|
1479
|
+
)
|
|
1480
|
+
if isinstance(map_value, YamlError):
|
|
1481
|
+
return map_value
|
|
1482
|
+
node.values.append(map_value)
|
|
1483
|
+
else:
|
|
1484
|
+
if not self.is_flow_map_delim(check.not_none(ctx.next_token())):
|
|
1485
|
+
err_tk = map_key_tk
|
|
1486
|
+
if err_tk is None:
|
|
1487
|
+
err_tk = tk
|
|
1488
|
+
return err_syntax('could not find flow map content', YamlParseToken.raw_token(err_tk))
|
|
1489
|
+
key1 = self.parse_scalar_value(ctx, map_key_tk)
|
|
1490
|
+
if isinstance(key1, YamlError):
|
|
1491
|
+
return key1
|
|
1492
|
+
value3 = YamlNodeMakers.new_null_node(ctx, ctx.insert_null_token(check.not_none(map_key_tk)))
|
|
1493
|
+
if isinstance(value3, YamlError):
|
|
1494
|
+
return value3
|
|
1495
|
+
map_value = YamlNodeMakers.new_mapping_value_node(
|
|
1496
|
+
ctx,
|
|
1497
|
+
check.not_none(map_key_tk),
|
|
1498
|
+
entry_tk,
|
|
1499
|
+
check.not_none(key1),
|
|
1500
|
+
value3,
|
|
1501
|
+
)
|
|
1502
|
+
if isinstance(map_value, YamlError):
|
|
1503
|
+
return map_value
|
|
1504
|
+
node.values.append(map_value)
|
|
1505
|
+
ctx.go_next()
|
|
1506
|
+
is_first = False
|
|
1507
|
+
if node.end is None:
|
|
1508
|
+
return err_syntax("could not find flow mapping end token '}'", node.start)
|
|
1509
|
+
ctx.go_next() # skip mapping end token.
|
|
1510
|
+
return node
|
|
1511
|
+
|
|
1512
|
+
def is_flow_map_delim(self, tk: YamlParseToken) -> bool:
|
|
1513
|
+
return tk.type() == YamlTokenType.MAPPING_END or tk.type() == YamlTokenType.COLLECT_ENTRY
|
|
1514
|
+
|
|
1515
|
+
def parse_map(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.MappingYamlNode]:
|
|
1516
|
+
key_tk = check.not_none(ctx.current_token())
|
|
1517
|
+
if key_tk.group is None:
|
|
1518
|
+
return err_syntax('unexpected map key', YamlParseToken.raw_token(key_tk))
|
|
1519
|
+
key_value_node: ast.MappingValueYamlNode
|
|
1520
|
+
if YamlParseToken.group_type(key_tk) == YamlParseTokenGroupType.MAP_KEY_VALUE:
|
|
1521
|
+
node0 = self.parse_map_key_value(
|
|
1522
|
+
ctx.with_group(check.not_none(key_tk.group)),
|
|
1523
|
+
check.not_none(key_tk.group),
|
|
1524
|
+
None,
|
|
1525
|
+
)
|
|
1526
|
+
if isinstance(node0, YamlError):
|
|
1527
|
+
return node0
|
|
1528
|
+
key_value_node = node0
|
|
1529
|
+
ctx.go_next()
|
|
1530
|
+
if (err := self.validate_map_key_value_next_token(ctx, key_tk, ctx.current_token())) is not None:
|
|
1531
|
+
return err
|
|
1532
|
+
else:
|
|
1533
|
+
key = self.parse_map_key(ctx.with_group(check.not_none(key_tk.group)), check.not_none(key_tk.group))
|
|
1534
|
+
if isinstance(key, YamlError):
|
|
1535
|
+
return key
|
|
1536
|
+
ctx.go_next()
|
|
1537
|
+
|
|
1538
|
+
value_tk = ctx.current_token()
|
|
1539
|
+
if (
|
|
1540
|
+
YamlParseToken.line(key_tk) == YamlParseToken.line(value_tk) and
|
|
1541
|
+
YamlParseToken.type(value_tk) == YamlTokenType.SEQUENCE_ENTRY
|
|
1542
|
+
):
|
|
1543
|
+
return err_syntax(
|
|
1544
|
+
'block sequence entries are not allowed in this context',
|
|
1545
|
+
YamlParseToken.raw_token(value_tk),
|
|
1546
|
+
)
|
|
1547
|
+
ctx = ctx.with_child(self.map_key_text(key))
|
|
1548
|
+
value = self.parse_map_value(ctx, key, check.not_none(check.not_none(key_tk.group).last()))
|
|
1549
|
+
if isinstance(value, YamlError):
|
|
1550
|
+
return value
|
|
1551
|
+
node1 = YamlNodeMakers.new_mapping_value_node(
|
|
1552
|
+
ctx,
|
|
1553
|
+
check.not_none(check.not_none(key_tk.group).last()),
|
|
1554
|
+
None,
|
|
1555
|
+
key,
|
|
1556
|
+
value,
|
|
1557
|
+
)
|
|
1558
|
+
if isinstance(node1, YamlError):
|
|
1559
|
+
return node1
|
|
1560
|
+
key_value_node = node1
|
|
1561
|
+
map_node = YamlNodeMakers.new_mapping_node(
|
|
1562
|
+
ctx,
|
|
1563
|
+
YamlParseToken(token=key_value_node.get_token()),
|
|
1564
|
+
False,
|
|
1565
|
+
key_value_node,
|
|
1566
|
+
)
|
|
1567
|
+
if isinstance(map_node, YamlError):
|
|
1568
|
+
return map_node
|
|
1569
|
+
tk: ta.Optional[YamlParseToken]
|
|
1570
|
+
if ctx.is_comment():
|
|
1571
|
+
tk = ctx.next_not_comment_token()
|
|
1572
|
+
else:
|
|
1573
|
+
tk = ctx.current_token()
|
|
1574
|
+
while YamlParseToken.column(tk) == YamlParseToken.column(key_tk):
|
|
1575
|
+
typ = YamlParseToken.type(tk)
|
|
1576
|
+
if ctx.is_flow and typ == YamlTokenType.SEQUENCE_END:
|
|
1577
|
+
# [
|
|
1578
|
+
# key: value
|
|
1579
|
+
# ] <=
|
|
1580
|
+
break
|
|
1581
|
+
if not self.is_map_token(check.not_none(tk)):
|
|
1582
|
+
return err_syntax('non-map value is specified', YamlParseToken.raw_token(tk))
|
|
1583
|
+
cm = self.parse_head_comment(ctx)
|
|
1584
|
+
if typ == YamlTokenType.MAPPING_END:
|
|
1585
|
+
# a: {
|
|
1586
|
+
# b: c
|
|
1587
|
+
# } <=
|
|
1588
|
+
ctx.go_next()
|
|
1589
|
+
break
|
|
1590
|
+
node2 = self.parse_map(ctx)
|
|
1591
|
+
if isinstance(node2, YamlError):
|
|
1592
|
+
return node2
|
|
1593
|
+
if len(node2.values) != 0:
|
|
1594
|
+
if (err := set_head_comment(cm, node2.values[0])) is not None:
|
|
1595
|
+
return err
|
|
1596
|
+
map_node.values.extend(node2.values)
|
|
1597
|
+
if node2.foot_comment is not None:
|
|
1598
|
+
map_node.values[len(map_node.values) - 1].foot_comment = node2.foot_comment
|
|
1599
|
+
tk = ctx.current_token()
|
|
1600
|
+
if ctx.is_comment():
|
|
1601
|
+
if YamlParseToken.column(key_tk) <= YamlParseToken.column(ctx.current_token()):
|
|
1602
|
+
# If the comment is in the same or deeper column as the last element column in map value,
|
|
1603
|
+
# treat it as a footer comment for the last element.
|
|
1604
|
+
if len(map_node.values) == 1:
|
|
1605
|
+
map_node.values[0].foot_comment = self.parse_foot_comment(ctx, YamlParseToken.column(key_tk))
|
|
1606
|
+
ast.BaseYamlNode.set_path(map_node.values[0].foot_comment, map_node.values[0].key.get_path())
|
|
1607
|
+
else:
|
|
1608
|
+
map_node.foot_comment = self.parse_foot_comment(ctx, YamlParseToken.column(key_tk))
|
|
1609
|
+
ast.BaseYamlNode.set_path(map_node.foot_comment, map_node.get_path())
|
|
1610
|
+
return map_node
|
|
1611
|
+
|
|
1612
|
+
def validate_map_key_value_next_token(self, ctx: YamlParsingContext, key_tk, tk: ta.Optional[YamlParseToken]) -> ta.Optional[YamlError]: # noqa
|
|
1613
|
+
if tk is None:
|
|
1614
|
+
return None
|
|
1615
|
+
if tk.column() <= key_tk.column():
|
|
1616
|
+
return None
|
|
1617
|
+
if ctx.is_comment():
|
|
1618
|
+
return None
|
|
1619
|
+
if (
|
|
1620
|
+
ctx.is_flow and
|
|
1621
|
+
(tk.type() == YamlTokenType.COLLECT_ENTRY or tk.type() == YamlTokenType.SEQUENCE_END)
|
|
1622
|
+
):
|
|
1623
|
+
return None
|
|
1624
|
+
# a: b
|
|
1625
|
+
# c <= this token is invalid.
|
|
1626
|
+
return err_syntax('value is not allowed in this context. map key-value is pre-defined', tk.raw_token())
|
|
1627
|
+
|
|
1628
|
+
def is_map_token(self, tk: YamlParseToken) -> bool:
|
|
1629
|
+
if tk.group is None:
|
|
1630
|
+
return tk.type() == YamlTokenType.MAPPING_START or tk.type() == YamlTokenType.MAPPING_END
|
|
1631
|
+
g = tk.group
|
|
1632
|
+
return g.type == YamlParseTokenGroupType.MAP_KEY or g.type == YamlParseTokenGroupType.MAP_KEY_VALUE
|
|
1633
|
+
|
|
1634
|
+
def parse_map_key_value(self,
|
|
1635
|
+
ctx: YamlParsingContext,
|
|
1636
|
+
g: YamlParseTokenGroup,
|
|
1637
|
+
entry_tk: ta.Optional[YamlParseToken],
|
|
1638
|
+
) -> YamlErrorOr[ast.MappingValueYamlNode]:
|
|
1639
|
+
if g.type != YamlParseTokenGroupType.MAP_KEY_VALUE:
|
|
1640
|
+
return err_syntax('unexpected map key-value pair', g.raw_token())
|
|
1641
|
+
if check.not_none(g.first()).group is None:
|
|
1642
|
+
return err_syntax('unexpected map key', g.raw_token())
|
|
1643
|
+
key_group = check.not_none(check.not_none(g.first()).group)
|
|
1644
|
+
key = self.parse_map_key(ctx.with_group(key_group), key_group)
|
|
1645
|
+
if isinstance(key, YamlError):
|
|
1646
|
+
return key
|
|
1647
|
+
|
|
1648
|
+
c = ctx.with_child(self.map_key_text(key))
|
|
1649
|
+
value = self.parse_token(c, g.last())
|
|
1650
|
+
if isinstance(value, YamlError):
|
|
1651
|
+
return value
|
|
1652
|
+
return YamlNodeMakers.new_mapping_value_node(c, check.not_none(key_group.last()), entry_tk, key, value)
|
|
1653
|
+
|
|
1654
|
+
def parse_map_key(self, ctx: YamlParsingContext, g: YamlParseTokenGroup) -> YamlErrorOr[ast.MapKeyYamlNode]:
|
|
1655
|
+
if g.type != YamlParseTokenGroupType.MAP_KEY:
|
|
1656
|
+
return err_syntax('unexpected map key', g.raw_token())
|
|
1657
|
+
if YamlParseToken.type(g.first()) == YamlTokenType.MAPPING_KEY:
|
|
1658
|
+
map_key_tk = check.not_none(g.first())
|
|
1659
|
+
if map_key_tk.group is not None:
|
|
1660
|
+
ctx = ctx.with_group(map_key_tk.group)
|
|
1661
|
+
key0 = YamlNodeMakers.new_mapping_key_node(ctx, map_key_tk)
|
|
1662
|
+
if isinstance(key0, YamlError):
|
|
1663
|
+
return key0
|
|
1664
|
+
ctx.go_next() # skip mapping key token
|
|
1665
|
+
if ctx.is_token_not_found():
|
|
1666
|
+
return err_syntax('could not find value for mapping key', YamlParseToken.raw_token(map_key_tk))
|
|
1667
|
+
|
|
1668
|
+
scalar0 = self.parse_scalar_value(ctx, ctx.current_token())
|
|
1669
|
+
if isinstance(scalar0, YamlError):
|
|
1670
|
+
return scalar0
|
|
1671
|
+
key0.value = scalar0
|
|
1672
|
+
key_text = self.map_key_text(scalar0)
|
|
1673
|
+
key_path = ctx.with_child(key_text).path
|
|
1674
|
+
key0.set_path(key_path)
|
|
1675
|
+
if (err := self.validate_map_key(
|
|
1676
|
+
ctx,
|
|
1677
|
+
check.not_none(key0.get_token()),
|
|
1678
|
+
key_path,
|
|
1679
|
+
check.not_none(g.last()),
|
|
1680
|
+
)) is not None:
|
|
1681
|
+
return err
|
|
1682
|
+
self.path_map[key_path] = key0
|
|
1683
|
+
return key0
|
|
1684
|
+
if YamlParseToken.type(g.last()) != YamlTokenType.MAPPING_VALUE:
|
|
1685
|
+
return err_syntax("expected map key-value delimiter ':'", YamlParseToken.raw_token(g.last()))
|
|
1686
|
+
|
|
1687
|
+
scalar1 = self.parse_scalar_value(ctx, g.first())
|
|
1688
|
+
if isinstance(scalar1, YamlError):
|
|
1689
|
+
return scalar1
|
|
1690
|
+
if not isinstance(scalar1, ast.MapKeyYamlNode):
|
|
1691
|
+
# FIXME: not possible
|
|
1692
|
+
return err_syntax(
|
|
1693
|
+
'cannot take map-key node',
|
|
1694
|
+
check.not_none(scalar1).get_token(),
|
|
1695
|
+
)
|
|
1696
|
+
key1: ast.MapKeyYamlNode = ta.cast(ast.MapKeyYamlNode, scalar1)
|
|
1697
|
+
key_text = self.map_key_text(key1)
|
|
1698
|
+
key_path = ctx.with_child(key_text).path
|
|
1699
|
+
key1.set_path(key_path)
|
|
1700
|
+
if (err := self.validate_map_key(
|
|
1701
|
+
ctx,
|
|
1702
|
+
check.not_none(key1.get_token()),
|
|
1703
|
+
key_path,
|
|
1704
|
+
check.not_none(g.last()),
|
|
1705
|
+
)) is not None:
|
|
1706
|
+
return err
|
|
1707
|
+
self.path_map[key_path] = key1
|
|
1708
|
+
return key1
|
|
1709
|
+
|
|
1710
|
+
def validate_map_key(
|
|
1711
|
+
self,
|
|
1712
|
+
ctx: YamlParsingContext,
|
|
1713
|
+
tk: YamlToken,
|
|
1714
|
+
key_path: str,
|
|
1715
|
+
colon_tk: YamlParseToken,
|
|
1716
|
+
) -> ta.Optional[YamlError]:
|
|
1717
|
+
if not self.allow_duplicate_map_key:
|
|
1718
|
+
if (n := self.path_map.get(key_path)) is not None:
|
|
1719
|
+
pos = check.not_none(n.get_token()).position
|
|
1720
|
+
return err_syntax(
|
|
1721
|
+
f'mapping key {tk.value!r} already defined at [{pos.line:d}:{pos.column:d}]',
|
|
1722
|
+
tk,
|
|
1723
|
+
)
|
|
1724
|
+
origin = self.remove_left_white_space(tk.origin)
|
|
1725
|
+
if ctx.is_flow:
|
|
1726
|
+
if tk.type == YamlTokenType.STRING:
|
|
1727
|
+
origin = self.remove_right_white_space(origin)
|
|
1728
|
+
if tk.position.line + self.new_line_character_num(origin) != colon_tk.line():
|
|
1729
|
+
return err_syntax('map key definition includes an implicit line break', tk)
|
|
1730
|
+
return None
|
|
1731
|
+
if (
|
|
1732
|
+
tk.type != YamlTokenType.STRING and
|
|
1733
|
+
tk.type != YamlTokenType.SINGLE_QUOTE and
|
|
1734
|
+
tk.type != YamlTokenType.DOUBLE_QUOTE
|
|
1735
|
+
):
|
|
1736
|
+
return None
|
|
1737
|
+
if self.exists_new_line_character(origin):
|
|
1738
|
+
return err_syntax('unexpected key name', tk)
|
|
1739
|
+
return None
|
|
1740
|
+
|
|
1741
|
+
def remove_left_white_space(self, src: str) -> str:
|
|
1742
|
+
# CR or LF or CRLF
|
|
1743
|
+
return src.lstrip(' \r\n')
|
|
1744
|
+
|
|
1745
|
+
def remove_right_white_space(self, src: str) -> str:
|
|
1746
|
+
# CR or LF or CRLF
|
|
1747
|
+
return src.rstrip(' \r\n')
|
|
1748
|
+
|
|
1749
|
+
def exists_new_line_character(self, src: str) -> bool:
|
|
1750
|
+
return self.new_line_character_num(src) > 0
|
|
1751
|
+
|
|
1752
|
+
def new_line_character_num(self, src: str) -> int:
|
|
1753
|
+
num = 0
|
|
1754
|
+
i = -1
|
|
1755
|
+
while True:
|
|
1756
|
+
i += 1
|
|
1757
|
+
if not (i < len(src)):
|
|
1758
|
+
break
|
|
1759
|
+
if src[i] == '\r':
|
|
1760
|
+
if len(src) > i + 1 and src[i + 1] == '\n':
|
|
1761
|
+
i += 1
|
|
1762
|
+
num += 1
|
|
1763
|
+
elif src[i] == '\n':
|
|
1764
|
+
num += 1
|
|
1765
|
+
return num
|
|
1766
|
+
|
|
1767
|
+
def map_key_text(self, n: ta.Optional[ast.YamlNode]) -> str:
|
|
1768
|
+
if n is None:
|
|
1769
|
+
return ''
|
|
1770
|
+
nn = n
|
|
1771
|
+
if isinstance(nn, ast.MappingKeyYamlNode):
|
|
1772
|
+
return self.map_key_text(nn.value)
|
|
1773
|
+
if isinstance(nn, ast.TagYamlNode):
|
|
1774
|
+
return self.map_key_text(nn.value)
|
|
1775
|
+
if isinstance(nn, ast.AnchorYamlNode):
|
|
1776
|
+
return self.map_key_text(nn.value)
|
|
1777
|
+
if isinstance(nn, ast.AliasYamlNode):
|
|
1778
|
+
return ''
|
|
1779
|
+
return check.not_none(n.get_token()).value
|
|
1780
|
+
|
|
1781
|
+
def parse_map_value(
|
|
1782
|
+
self,
|
|
1783
|
+
ctx: YamlParsingContext,
|
|
1784
|
+
key: ast.MapKeyYamlNode,
|
|
1785
|
+
colon_tk: YamlParseToken,
|
|
1786
|
+
) -> YamlErrorOr[ast.YamlNode]:
|
|
1787
|
+
tk = ctx.current_token()
|
|
1788
|
+
if tk is None:
|
|
1789
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.add_null_value_token(colon_tk))
|
|
1790
|
+
|
|
1791
|
+
if ctx.is_comment():
|
|
1792
|
+
tk = ctx.next_not_comment_token()
|
|
1793
|
+
key_col = check.not_none(key.get_token()).position.column
|
|
1794
|
+
key_line = check.not_none(key.get_token()).position.line
|
|
1795
|
+
|
|
1796
|
+
if (
|
|
1797
|
+
YamlParseToken.column(tk) != key_col and
|
|
1798
|
+
YamlParseToken.line(tk) == key_line and
|
|
1799
|
+
(
|
|
1800
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.MAP_KEY or
|
|
1801
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.MAP_KEY_VALUE
|
|
1802
|
+
)
|
|
1803
|
+
):
|
|
1804
|
+
# a: b:
|
|
1805
|
+
# ^
|
|
1806
|
+
#
|
|
1807
|
+
# a: b: c
|
|
1808
|
+
# ^
|
|
1809
|
+
return err_syntax('mapping value is not allowed in this context', YamlParseToken.raw_token(tk))
|
|
1810
|
+
|
|
1811
|
+
if YamlParseToken.column(tk) == key_col and self.is_map_token(check.not_none(tk)):
|
|
1812
|
+
# in this case,
|
|
1813
|
+
# ----
|
|
1814
|
+
# key: <value does not defined>
|
|
1815
|
+
# next
|
|
1816
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.insert_null_token(colon_tk))
|
|
1817
|
+
|
|
1818
|
+
if (
|
|
1819
|
+
YamlParseToken.line(tk) == key_line and
|
|
1820
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME and
|
|
1821
|
+
YamlParseToken.column(ctx.next_token()) == key_col and
|
|
1822
|
+
self.is_map_token(check.not_none(ctx.next_token()))
|
|
1823
|
+
):
|
|
1824
|
+
# in this case,
|
|
1825
|
+
# ----
|
|
1826
|
+
# key: &anchor
|
|
1827
|
+
# next
|
|
1828
|
+
group = YamlParseTokenGroup(
|
|
1829
|
+
type=YamlParseTokenGroupType.ANCHOR,
|
|
1830
|
+
tokens=[check.not_none(tk), ctx.create_implicit_null_token(check.not_none(tk))],
|
|
1831
|
+
)
|
|
1832
|
+
anchor = self.parse_anchor(ctx.with_group(group), group)
|
|
1833
|
+
if isinstance(anchor, YamlError):
|
|
1834
|
+
return anchor
|
|
1835
|
+
ctx.go_next()
|
|
1836
|
+
return anchor
|
|
1837
|
+
|
|
1838
|
+
if (
|
|
1839
|
+
YamlParseToken.column(tk) <= key_col and
|
|
1840
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME
|
|
1841
|
+
):
|
|
1842
|
+
# key: <value does not defined>
|
|
1843
|
+
# &anchor
|
|
1844
|
+
return err_syntax('anchor is not allowed in this context', YamlParseToken.raw_token(tk))
|
|
1845
|
+
if YamlParseToken.column(tk) <= key_col and YamlParseToken.type(tk) == YamlTokenType.TAG:
|
|
1846
|
+
# key: <value does not defined>
|
|
1847
|
+
# !!tag
|
|
1848
|
+
return err_syntax('tag is not allowed in this context', YamlParseToken.raw_token(tk))
|
|
1849
|
+
|
|
1850
|
+
if YamlParseToken.column(tk) < key_col:
|
|
1851
|
+
# in this case,
|
|
1852
|
+
# ----
|
|
1853
|
+
# key: <value does not defined>
|
|
1854
|
+
# next
|
|
1855
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.insert_null_token(colon_tk))
|
|
1856
|
+
|
|
1857
|
+
if (
|
|
1858
|
+
YamlParseToken.line(tk) == key_line and
|
|
1859
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME and
|
|
1860
|
+
YamlParseToken.column(ctx.next_token()) < key_col
|
|
1861
|
+
):
|
|
1862
|
+
# in this case,
|
|
1863
|
+
# ----
|
|
1864
|
+
# key: &anchor
|
|
1865
|
+
# next
|
|
1866
|
+
group = YamlParseTokenGroup(
|
|
1867
|
+
type=YamlParseTokenGroupType.ANCHOR,
|
|
1868
|
+
tokens=[check.not_none(tk), ctx.create_implicit_null_token(check.not_none(tk))],
|
|
1869
|
+
)
|
|
1870
|
+
anchor = self.parse_anchor(ctx.with_group(group), group)
|
|
1871
|
+
if isinstance(anchor, YamlError):
|
|
1872
|
+
return anchor
|
|
1873
|
+
ctx.go_next()
|
|
1874
|
+
return anchor
|
|
1875
|
+
|
|
1876
|
+
value = self.parse_token(ctx, ctx.current_token())
|
|
1877
|
+
if isinstance(value, YamlError):
|
|
1878
|
+
return value
|
|
1879
|
+
if (err := self.validate_anchor_value_in_map_or_seq(value, key_col)) is not None:
|
|
1880
|
+
return err
|
|
1881
|
+
return value
|
|
1882
|
+
|
|
1883
|
+
def validate_anchor_value_in_map_or_seq(self, value: ast.YamlNode, col: int) -> ta.Optional[YamlError]:
|
|
1884
|
+
if not isinstance(value, ast.AnchorYamlNode):
|
|
1885
|
+
return None
|
|
1886
|
+
anchor: ast.AnchorYamlNode = value
|
|
1887
|
+
if not isinstance(anchor.value, ast.TagYamlNode):
|
|
1888
|
+
return None
|
|
1889
|
+
tag: ast.TagYamlNode = anchor.value
|
|
1890
|
+
anchor_tk = anchor.get_token()
|
|
1891
|
+
tag_tk = tag.get_token()
|
|
1892
|
+
|
|
1893
|
+
if anchor_tk.position.line == tag_tk.position.line:
|
|
1894
|
+
# key:
|
|
1895
|
+
# &anchor !!tag
|
|
1896
|
+
#
|
|
1897
|
+
# - &anchor !!tag
|
|
1898
|
+
return None
|
|
1899
|
+
|
|
1900
|
+
if tag_tk.position.column <= col:
|
|
1901
|
+
# key: &anchor
|
|
1902
|
+
# !!tag
|
|
1903
|
+
#
|
|
1904
|
+
# - &anchor
|
|
1905
|
+
# !!tag
|
|
1906
|
+
return err_syntax('tag is not allowed in this context', tag_tk)
|
|
1907
|
+
return None
|
|
1908
|
+
|
|
1909
|
+
def parse_anchor(self, ctx: YamlParsingContext, g: YamlParseTokenGroup) -> YamlErrorOr[ast.AnchorYamlNode]:
|
|
1910
|
+
anchor_name_group = check.not_none(check.not_none(g.first()).group)
|
|
1911
|
+
anchor = self.parse_anchor_name(ctx.with_group(anchor_name_group))
|
|
1912
|
+
if isinstance(anchor, YamlError):
|
|
1913
|
+
return anchor
|
|
1914
|
+
ctx.go_next()
|
|
1915
|
+
if ctx.is_token_not_found():
|
|
1916
|
+
return err_syntax('could not find anchor value', anchor.get_token())
|
|
1917
|
+
|
|
1918
|
+
value = self.parse_token(ctx, ctx.current_token())
|
|
1919
|
+
if isinstance(value, YamlError):
|
|
1920
|
+
return value
|
|
1921
|
+
if isinstance(value, ast.AnchorYamlNode):
|
|
1922
|
+
return err_syntax('anchors cannot be used consecutively', value.get_token())
|
|
1923
|
+
anchor.value = value
|
|
1924
|
+
return anchor
|
|
1925
|
+
|
|
1926
|
+
def parse_anchor_name(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.AnchorYamlNode]:
|
|
1927
|
+
anchor = YamlNodeMakers.new_anchor_node(ctx, ctx.current_token())
|
|
1928
|
+
if isinstance(anchor, YamlError):
|
|
1929
|
+
return anchor
|
|
1930
|
+
ctx.go_next()
|
|
1931
|
+
if ctx.is_token_not_found():
|
|
1932
|
+
return err_syntax('could not find anchor value', anchor.get_token())
|
|
1933
|
+
|
|
1934
|
+
anchor_name = self.parse_scalar_value(ctx, ctx.current_token())
|
|
1935
|
+
if isinstance(anchor_name, YamlError):
|
|
1936
|
+
return anchor_name
|
|
1937
|
+
if anchor_name is None:
|
|
1938
|
+
return err_syntax(
|
|
1939
|
+
'unexpected anchor. anchor name is not scalar value',
|
|
1940
|
+
YamlParseToken.raw_token(ctx.current_token()),
|
|
1941
|
+
)
|
|
1942
|
+
anchor.name = anchor_name
|
|
1943
|
+
return anchor
|
|
1944
|
+
|
|
1945
|
+
def parse_alias(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.AliasYamlNode]:
|
|
1946
|
+
alias = YamlNodeMakers.new_alias_node(ctx, ctx.current_token())
|
|
1947
|
+
if isinstance(alias, YamlError):
|
|
1948
|
+
return alias
|
|
1949
|
+
ctx.go_next()
|
|
1950
|
+
if ctx.is_token_not_found():
|
|
1951
|
+
return err_syntax('could not find alias value', alias.get_token())
|
|
1952
|
+
|
|
1953
|
+
alias_name = self.parse_scalar_value(ctx, ctx.current_token())
|
|
1954
|
+
if isinstance(alias_name, YamlError):
|
|
1955
|
+
return alias_name
|
|
1956
|
+
if alias_name is None:
|
|
1957
|
+
return err_syntax(
|
|
1958
|
+
'unexpected alias. alias name is not scalar value',
|
|
1959
|
+
YamlParseToken.raw_token(ctx.current_token()),
|
|
1960
|
+
)
|
|
1961
|
+
alias.value = alias_name
|
|
1962
|
+
return alias
|
|
1963
|
+
|
|
1964
|
+
def parse_literal(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.LiteralYamlNode]:
|
|
1965
|
+
node = YamlNodeMakers.new_literal_node(ctx, ctx.current_token())
|
|
1966
|
+
if isinstance(node, YamlError):
|
|
1967
|
+
return node
|
|
1968
|
+
ctx.go_next() # skip literal/folded token
|
|
1969
|
+
|
|
1970
|
+
tk = ctx.current_token()
|
|
1971
|
+
if tk is None:
|
|
1972
|
+
value0 = YamlNodeMakers.new_string_node(
|
|
1973
|
+
ctx,
|
|
1974
|
+
YamlParseToken(token=new_yaml_token('', '', node.start.position)),
|
|
1975
|
+
)
|
|
1976
|
+
if isinstance(value0, YamlError):
|
|
1977
|
+
return value0
|
|
1978
|
+
node.value = value0
|
|
1979
|
+
return node
|
|
1980
|
+
value1 = self.parse_token(ctx, tk)
|
|
1981
|
+
if isinstance(value1, YamlError):
|
|
1982
|
+
return value1
|
|
1983
|
+
if not isinstance(s := value1, ast.StringYamlNode):
|
|
1984
|
+
return err_syntax('unexpected token. required string token', value1.get_token())
|
|
1985
|
+
node.value = s
|
|
1986
|
+
return node
|
|
1987
|
+
|
|
1988
|
+
def parse_scalar_tag(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.TagYamlNode]:
|
|
1989
|
+
tag = self.parse_tag(ctx)
|
|
1990
|
+
if isinstance(tag, YamlError):
|
|
1991
|
+
return tag
|
|
1992
|
+
if tag.value is None:
|
|
1993
|
+
return err_syntax('specified not scalar tag', tag.get_token())
|
|
1994
|
+
if not isinstance(tag.value, ast.ScalarYamlNode):
|
|
1995
|
+
return err_syntax('specified not scalar tag', tag.get_token())
|
|
1996
|
+
return tag
|
|
1997
|
+
|
|
1998
|
+
def parse_tag(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.TagYamlNode]:
|
|
1999
|
+
tag_tk = ctx.current_token()
|
|
2000
|
+
tag_raw_tk = YamlParseToken.raw_token(tag_tk)
|
|
2001
|
+
node = YamlNodeMakers.new_tag_node(ctx, tag_tk)
|
|
2002
|
+
if isinstance(node, YamlError):
|
|
2003
|
+
return node
|
|
2004
|
+
ctx.go_next()
|
|
2005
|
+
|
|
2006
|
+
comment = self.parse_head_comment(ctx)
|
|
2007
|
+
|
|
2008
|
+
tag_value: ast.YamlNode
|
|
2009
|
+
if self.secondary_tag_directive is not None:
|
|
2010
|
+
value0 = YamlNodeMakers.new_string_node(ctx, ctx.current_token())
|
|
2011
|
+
if isinstance(value0, YamlError):
|
|
2012
|
+
return value0
|
|
2013
|
+
tag_value = value0
|
|
2014
|
+
node.directive = self.secondary_tag_directive
|
|
2015
|
+
else:
|
|
2016
|
+
value1 = self.parse_tag_value(ctx, check.not_none(tag_raw_tk), ctx.current_token())
|
|
2017
|
+
if isinstance(value1, YamlError):
|
|
2018
|
+
return value1
|
|
2019
|
+
tag_value = check.not_none(value1)
|
|
2020
|
+
if (err := set_head_comment(comment, tag_value)) is not None:
|
|
2021
|
+
return err
|
|
2022
|
+
node.value = tag_value
|
|
2023
|
+
return node
|
|
2024
|
+
|
|
2025
|
+
def parse_tag_value(
|
|
2026
|
+
self,
|
|
2027
|
+
ctx: YamlParsingContext,
|
|
2028
|
+
tag_raw_tk: YamlToken,
|
|
2029
|
+
tk: ta.Optional[YamlParseToken],
|
|
2030
|
+
) -> YamlErrorOr[ta.Optional[ast.YamlNode]]:
|
|
2031
|
+
if tk is None:
|
|
2032
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.create_implicit_null_token(YamlParseToken(token=tag_raw_tk)))
|
|
2033
|
+
if tag_raw_tk.value in (
|
|
2034
|
+
YamlReservedTagKeywords.MAPPING,
|
|
2035
|
+
YamlReservedTagKeywords.SET,
|
|
2036
|
+
):
|
|
2037
|
+
if not self.is_map_token(tk):
|
|
2038
|
+
return err_syntax('could not find map', tk.raw_token())
|
|
2039
|
+
if tk.type() == YamlTokenType.MAPPING_START:
|
|
2040
|
+
return self.parse_flow_map(ctx.with_flow(True))
|
|
2041
|
+
return self.parse_map(ctx)
|
|
2042
|
+
elif tag_raw_tk.value in (
|
|
2043
|
+
YamlReservedTagKeywords.INTEGER,
|
|
2044
|
+
YamlReservedTagKeywords.FLOAT,
|
|
2045
|
+
YamlReservedTagKeywords.STRING,
|
|
2046
|
+
YamlReservedTagKeywords.BINARY,
|
|
2047
|
+
YamlReservedTagKeywords.TIMESTAMP,
|
|
2048
|
+
YamlReservedTagKeywords.BOOLEAN,
|
|
2049
|
+
YamlReservedTagKeywords.NULL,
|
|
2050
|
+
):
|
|
2051
|
+
if tk.group_type() == YamlParseTokenGroupType.LITERAL or tk.group_type() == YamlParseTokenGroupType.FOLDED:
|
|
2052
|
+
return self.parse_literal(ctx.with_group(check.not_none(tk.group)))
|
|
2053
|
+
elif tk.type() == YamlTokenType.COLLECT_ENTRY or tk.type() == YamlTokenType.MAPPING_VALUE:
|
|
2054
|
+
return YamlNodeMakers.new_tag_default_scalar_value_node(ctx, tag_raw_tk)
|
|
2055
|
+
scalar = self.parse_scalar_value(ctx, tk)
|
|
2056
|
+
if isinstance(scalar, YamlError):
|
|
2057
|
+
return scalar
|
|
2058
|
+
ctx.go_next()
|
|
2059
|
+
return scalar
|
|
2060
|
+
elif tag_raw_tk.value in (
|
|
2061
|
+
YamlReservedTagKeywords.SEQUENCE,
|
|
2062
|
+
YamlReservedTagKeywords.ORDERED_MAP,
|
|
2063
|
+
):
|
|
2064
|
+
if tk.type() == YamlTokenType.SEQUENCE_START:
|
|
2065
|
+
return self.parse_flow_sequence(ctx.with_flow(True))
|
|
2066
|
+
return self.parse_sequence(ctx)
|
|
2067
|
+
return self.parse_token(ctx, tk)
|
|
2068
|
+
|
|
2069
|
+
def parse_flow_sequence(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.SequenceYamlNode]:
|
|
2070
|
+
node = YamlNodeMakers.new_sequence_node(ctx, ctx.current_token(), True)
|
|
2071
|
+
if isinstance(node, YamlError):
|
|
2072
|
+
return node
|
|
2073
|
+
ctx.go_next() # skip SequenceStart token
|
|
2074
|
+
|
|
2075
|
+
is_first = True
|
|
2076
|
+
while ctx.next():
|
|
2077
|
+
tk = ctx.current_token()
|
|
2078
|
+
if YamlParseToken.type(tk) == YamlTokenType.SEQUENCE_END:
|
|
2079
|
+
node.end = YamlParseToken.raw_token(tk)
|
|
2080
|
+
break
|
|
2081
|
+
|
|
2082
|
+
entry_tk: ta.Optional[YamlParseToken] = None
|
|
2083
|
+
if YamlParseToken.type(tk) == YamlTokenType.COLLECT_ENTRY:
|
|
2084
|
+
if is_first:
|
|
2085
|
+
return err_syntax("expected sequence element, but found ','", YamlParseToken.raw_token(tk))
|
|
2086
|
+
entry_tk = tk
|
|
2087
|
+
ctx.go_next()
|
|
2088
|
+
elif not is_first:
|
|
2089
|
+
return err_syntax("',' or ']' must be specified", YamlParseToken.raw_token(tk))
|
|
2090
|
+
|
|
2091
|
+
if YamlParseToken.type(tk := ctx.current_token()) == YamlTokenType.SEQUENCE_END:
|
|
2092
|
+
# this case is here: "[ elem, ]".
|
|
2093
|
+
# In this case, ignore the last element and break sequence parsing.
|
|
2094
|
+
node.end = YamlParseToken.raw_token(tk)
|
|
2095
|
+
break
|
|
2096
|
+
|
|
2097
|
+
if ctx.is_token_not_found():
|
|
2098
|
+
break
|
|
2099
|
+
|
|
2100
|
+
ctx = ctx.with_index(len(node.values))
|
|
2101
|
+
value = self.parse_token(ctx, ctx.current_token())
|
|
2102
|
+
if isinstance(value, YamlError):
|
|
2103
|
+
return value
|
|
2104
|
+
node.values.append(value)
|
|
2105
|
+
seq_entry = ast.sequence_entry(
|
|
2106
|
+
entry_tk.raw_token() if entry_tk is not None else None,
|
|
2107
|
+
value,
|
|
2108
|
+
None,
|
|
2109
|
+
)
|
|
2110
|
+
if (err := set_line_comment(ctx, seq_entry, entry_tk)) is not None:
|
|
2111
|
+
return err
|
|
2112
|
+
seq_entry.set_path(ctx.path)
|
|
2113
|
+
node.entries.append(seq_entry)
|
|
2114
|
+
|
|
2115
|
+
is_first = False
|
|
2116
|
+
if node.end is None:
|
|
2117
|
+
return err_syntax("sequence end token ']' not found", node.start)
|
|
2118
|
+
ctx.go_next() # skip sequence end token.
|
|
2119
|
+
return node
|
|
2120
|
+
|
|
2121
|
+
def parse_sequence(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.SequenceYamlNode]:
|
|
2122
|
+
seq_tk = ctx.current_token()
|
|
2123
|
+
seq_node = YamlNodeMakers.new_sequence_node(ctx, seq_tk, False)
|
|
2124
|
+
if isinstance(seq_node, YamlError):
|
|
2125
|
+
return seq_node
|
|
2126
|
+
|
|
2127
|
+
tk = seq_tk
|
|
2128
|
+
while (
|
|
2129
|
+
YamlParseToken.type(tk) == YamlTokenType.SEQUENCE_ENTRY and
|
|
2130
|
+
YamlParseToken.column(tk) == YamlParseToken.column(seq_tk)
|
|
2131
|
+
):
|
|
2132
|
+
head_comment = self.parse_head_comment(ctx)
|
|
2133
|
+
ctx.go_next() # skip sequence entry token
|
|
2134
|
+
|
|
2135
|
+
ctx = ctx.with_index(len(seq_node.values))
|
|
2136
|
+
value = self.parse_sequence_value(ctx, check.not_none(seq_tk))
|
|
2137
|
+
if isinstance(value, YamlError):
|
|
2138
|
+
return value
|
|
2139
|
+
seq_entry = ast.sequence_entry(YamlParseToken.raw_token(seq_tk), value, head_comment)
|
|
2140
|
+
if (err := set_line_comment(ctx, seq_entry, seq_tk)) is not None:
|
|
2141
|
+
return err
|
|
2142
|
+
seq_entry.set_path(ctx.path)
|
|
2143
|
+
seq_node.value_head_comments.append(head_comment)
|
|
2144
|
+
seq_node.values.append(value)
|
|
2145
|
+
seq_node.entries.append(seq_entry)
|
|
2146
|
+
|
|
2147
|
+
if ctx.is_comment():
|
|
2148
|
+
tk = ctx.next_not_comment_token()
|
|
2149
|
+
else:
|
|
2150
|
+
tk = ctx.current_token()
|
|
2151
|
+
if ctx.is_comment():
|
|
2152
|
+
if YamlParseToken.column(seq_tk) <= YamlParseToken.column(ctx.current_token()):
|
|
2153
|
+
# If the comment is in the same or deeper column as the last element column in sequence value,
|
|
2154
|
+
# treat it as a footer comment for the last element.
|
|
2155
|
+
seq_node.foot_comment = self.parse_foot_comment(ctx, YamlParseToken.column(seq_tk))
|
|
2156
|
+
if len(seq_node.values) != 0:
|
|
2157
|
+
check.not_none(seq_node.foot_comment).set_path(
|
|
2158
|
+
check.not_none(seq_node.values[len(seq_node.values) - 1]).get_path(),
|
|
2159
|
+
)
|
|
2160
|
+
return seq_node
|
|
2161
|
+
|
|
2162
|
+
def parse_sequence_value(self, ctx: YamlParsingContext, seq_tk: YamlParseToken) -> YamlErrorOr[ast.YamlNode]:
|
|
2163
|
+
tk = ctx.current_token()
|
|
2164
|
+
if tk is None:
|
|
2165
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.add_null_value_token(seq_tk))
|
|
2166
|
+
|
|
2167
|
+
if ctx.is_comment():
|
|
2168
|
+
tk = ctx.next_not_comment_token()
|
|
2169
|
+
seq_col = seq_tk.column()
|
|
2170
|
+
seq_line = seq_tk.line()
|
|
2171
|
+
|
|
2172
|
+
if YamlParseToken.column(tk) == seq_col and YamlParseToken.type(tk) == YamlTokenType.SEQUENCE_ENTRY:
|
|
2173
|
+
# in this case,
|
|
2174
|
+
# ----
|
|
2175
|
+
# - <value does not defined>
|
|
2176
|
+
# -
|
|
2177
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.insert_null_token(seq_tk))
|
|
2178
|
+
|
|
2179
|
+
if (
|
|
2180
|
+
YamlParseToken.line(tk) == seq_line and
|
|
2181
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME and
|
|
2182
|
+
YamlParseToken.column(ctx.next_token()) == seq_col and
|
|
2183
|
+
YamlParseToken.type(ctx.next_token()) == YamlTokenType.SEQUENCE_ENTRY
|
|
2184
|
+
):
|
|
2185
|
+
# in this case,
|
|
2186
|
+
# ----
|
|
2187
|
+
# - &anchor
|
|
2188
|
+
# -
|
|
2189
|
+
group = YamlParseTokenGroup(
|
|
2190
|
+
type=YamlParseTokenGroupType.ANCHOR,
|
|
2191
|
+
tokens=[check.not_none(tk), ctx.create_implicit_null_token(check.not_none(tk))],
|
|
2192
|
+
)
|
|
2193
|
+
anchor = self.parse_anchor(ctx.with_group(group), group)
|
|
2194
|
+
if isinstance(anchor, YamlError):
|
|
2195
|
+
return anchor
|
|
2196
|
+
ctx.go_next()
|
|
2197
|
+
return anchor
|
|
2198
|
+
|
|
2199
|
+
if (
|
|
2200
|
+
YamlParseToken.column(tk) <= seq_col and
|
|
2201
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME
|
|
2202
|
+
):
|
|
2203
|
+
# - <value does not defined>
|
|
2204
|
+
# &anchor
|
|
2205
|
+
return err_syntax('anchor is not allowed in this sequence context', YamlParseToken.raw_token(tk))
|
|
2206
|
+
if YamlParseToken.column(tk) <= seq_col and YamlParseToken.type(tk) == YamlTokenType.TAG:
|
|
2207
|
+
# - <value does not defined>
|
|
2208
|
+
# !!tag
|
|
2209
|
+
return err_syntax('tag is not allowed in this sequence context', YamlParseToken.raw_token(tk))
|
|
2210
|
+
|
|
2211
|
+
if YamlParseToken.column(tk) < seq_col:
|
|
2212
|
+
# in this case,
|
|
2213
|
+
# ----
|
|
2214
|
+
# - <value does not defined>
|
|
2215
|
+
# next
|
|
2216
|
+
return YamlNodeMakers.new_null_node(ctx, ctx.insert_null_token(seq_tk))
|
|
2217
|
+
|
|
2218
|
+
if (
|
|
2219
|
+
YamlParseToken.line(tk) == seq_line and
|
|
2220
|
+
YamlParseToken.group_type(tk) == YamlParseTokenGroupType.ANCHOR_NAME and
|
|
2221
|
+
YamlParseToken.column(ctx.next_token()) < seq_col
|
|
2222
|
+
):
|
|
2223
|
+
# in this case,
|
|
2224
|
+
# ----
|
|
2225
|
+
# - &anchor
|
|
2226
|
+
# next
|
|
2227
|
+
group = YamlParseTokenGroup(
|
|
2228
|
+
type=YamlParseTokenGroupType.ANCHOR,
|
|
2229
|
+
tokens=[check.not_none(tk), ctx.create_implicit_null_token(check.not_none(tk))],
|
|
2230
|
+
)
|
|
2231
|
+
anchor = self.parse_anchor(ctx.with_group(group), group)
|
|
2232
|
+
if isinstance(anchor, YamlError):
|
|
2233
|
+
return anchor
|
|
2234
|
+
ctx.go_next()
|
|
2235
|
+
return anchor
|
|
2236
|
+
|
|
2237
|
+
value = self.parse_token(ctx, ctx.current_token())
|
|
2238
|
+
if isinstance(value, YamlError):
|
|
2239
|
+
return value
|
|
2240
|
+
if (err := self.validate_anchor_value_in_map_or_seq(value, seq_col)) is not None:
|
|
2241
|
+
return err
|
|
2242
|
+
return value
|
|
2243
|
+
|
|
2244
|
+
def parse_directive(self, ctx: YamlParsingContext, g: YamlParseTokenGroup) -> YamlErrorOr[ast.DirectiveYamlNode]:
|
|
2245
|
+
directive_name_group = check.not_none(check.not_none(g.first()).group)
|
|
2246
|
+
directive = self.parse_directive_name(ctx.with_group(directive_name_group))
|
|
2247
|
+
if isinstance(directive, YamlError):
|
|
2248
|
+
return directive
|
|
2249
|
+
|
|
2250
|
+
if directive.name == 'YAML':
|
|
2251
|
+
if len(g.tokens) != 2:
|
|
2252
|
+
return err_syntax('unexpected format YAML directive', YamlParseToken.raw_token(g.first()))
|
|
2253
|
+
value_tk = g.tokens[1]
|
|
2254
|
+
value_raw_tk = check.not_none(value_tk.raw_token())
|
|
2255
|
+
value0 = value_raw_tk.value
|
|
2256
|
+
ver = YAML_VERSION_MAP.get(value0)
|
|
2257
|
+
if ver is None:
|
|
2258
|
+
return err_syntax(f'unknown YAML version {value0!r}', value_raw_tk)
|
|
2259
|
+
if self.yaml_version != '':
|
|
2260
|
+
return err_syntax('YAML version has already been specified', value_raw_tk)
|
|
2261
|
+
self.yaml_version = ver
|
|
2262
|
+
version_node = YamlNodeMakers.new_string_node(ctx, value_tk)
|
|
2263
|
+
if isinstance(version_node, YamlError):
|
|
2264
|
+
return version_node
|
|
2265
|
+
directive.values.append(version_node)
|
|
2266
|
+
elif directive.name == 'TAG':
|
|
2267
|
+
if len(g.tokens) != 3:
|
|
2268
|
+
return err_syntax('unexpected format TAG directive', YamlParseToken.raw_token(g.first()))
|
|
2269
|
+
tag_key = YamlNodeMakers.new_string_node(ctx, g.tokens[1])
|
|
2270
|
+
if isinstance(tag_key, YamlError):
|
|
2271
|
+
return tag_key
|
|
2272
|
+
if tag_key.value == '!!':
|
|
2273
|
+
self.secondary_tag_directive = directive
|
|
2274
|
+
tag_value = YamlNodeMakers.new_string_node(ctx, g.tokens[2])
|
|
2275
|
+
if isinstance(tag_value, YamlError):
|
|
2276
|
+
return tag_value
|
|
2277
|
+
directive.values.extend([tag_key, tag_value])
|
|
2278
|
+
elif len(g.tokens) > 1:
|
|
2279
|
+
for tk in g.tokens[1:]:
|
|
2280
|
+
value1 = YamlNodeMakers.new_string_node(ctx, tk)
|
|
2281
|
+
if isinstance(value1, YamlError):
|
|
2282
|
+
return value1
|
|
2283
|
+
directive.values.append(value1)
|
|
2284
|
+
return directive
|
|
2285
|
+
|
|
2286
|
+
def parse_directive_name(self, ctx: YamlParsingContext) -> YamlErrorOr[ast.DirectiveYamlNode]:
|
|
2287
|
+
directive = YamlNodeMakers.new_directive_node(ctx, ctx.current_token())
|
|
2288
|
+
if isinstance(directive, YamlError):
|
|
2289
|
+
return directive
|
|
2290
|
+
ctx.go_next()
|
|
2291
|
+
if ctx.is_token_not_found():
|
|
2292
|
+
return err_syntax('could not find directive value', directive.get_token())
|
|
2293
|
+
|
|
2294
|
+
directive_name = self.parse_scalar_value(ctx, ctx.current_token())
|
|
2295
|
+
if isinstance(directive_name, YamlError):
|
|
2296
|
+
return directive_name
|
|
2297
|
+
if directive_name is None:
|
|
2298
|
+
return err_syntax(
|
|
2299
|
+
'unexpected directive. directive name is not scalar value',
|
|
2300
|
+
YamlParseToken.raw_token(ctx.current_token()),
|
|
2301
|
+
)
|
|
2302
|
+
directive.name = directive_name
|
|
2303
|
+
return directive
|
|
2304
|
+
|
|
2305
|
+
def parse_comment(self, ctx: YamlParsingContext) -> YamlErrorOr[ta.Optional[ast.YamlNode]]:
|
|
2306
|
+
cm = self.parse_head_comment(ctx)
|
|
2307
|
+
if ctx.is_token_not_found():
|
|
2308
|
+
return cm
|
|
2309
|
+
node = self.parse_token(ctx, ctx.current_token())
|
|
2310
|
+
if isinstance(node, YamlError):
|
|
2311
|
+
return node
|
|
2312
|
+
if (err := set_head_comment(cm, node)) is not None:
|
|
2313
|
+
return err
|
|
2314
|
+
return node
|
|
2315
|
+
|
|
2316
|
+
def parse_head_comment(self, ctx: YamlParsingContext) -> ta.Optional[ast.CommentGroupYamlNode]:
|
|
2317
|
+
tks: ta.List[ta.Optional[YamlToken]] = []
|
|
2318
|
+
while ctx.is_comment():
|
|
2319
|
+
tks.append(YamlParseToken.raw_token(ctx.current_token()))
|
|
2320
|
+
ctx.go_next()
|
|
2321
|
+
if len(tks) == 0:
|
|
2322
|
+
return None
|
|
2323
|
+
return ast.comment_group(tks)
|
|
2324
|
+
|
|
2325
|
+
def parse_foot_comment(self, ctx: YamlParsingContext, col: int) -> ta.Optional[ast.CommentGroupYamlNode]:
|
|
2326
|
+
tks: ta.List[ta.Optional[YamlToken]] = []
|
|
2327
|
+
while ctx.is_comment() and col <= YamlParseToken.column(ctx.current_token()):
|
|
2328
|
+
tks.append(YamlParseToken.raw_token(ctx.current_token()))
|
|
2329
|
+
ctx.go_next()
|
|
2330
|
+
if len(tks) == 0:
|
|
2331
|
+
return None
|
|
2332
|
+
return ast.comment_group(tks)
|