parser.nim
1 # NimYAML - YAML implementation in Nim 2 # (c) Copyright 2015-2023 Felix Krause 3 # 4 # See the file "copying.txt", included in this 5 # distribution, for details about the copyright. 6 7 ## ================== 8 ## Module yaml/parser 9 ## ================== 10 ## 11 ## This is the low-level parser API. A ``YamlParser`` enables you to parse any 12 ## non-nil string or Stream object as YAML character stream. 13 14 import tables, strutils, macros, streams 15 import stream, private/lex, private/internal, private/escaping, data 16 17 when defined(nimNoNil): 18 {.experimental: "notnil".} 19 20 type 21 YamlParser* = object 22 ## A parser object. Retains its ``TagLibrary`` across calls to 23 ## `parse <#parse,YamlParser,Stream>`_. Can be used 24 ## to access anchor names while parsing a YAML character stream, but 25 ## only until the document goes out of scope (i.e. until 26 ## ``yamlEndDocument`` is yielded). 27 issueWarnings: bool 28 29 YamlParserError* = object of YamlLoadingError 30 ## A parser error is raised if the character stream that is parsed is 31 ## not a valid YAML character stream. This stream cannot and will not be 32 ## parsed wholly nor partially and all events that have been emitted by 33 ## the YamlStream the parser provides should be discarded. 34 ## 35 ## A character stream is invalid YAML if and only if at least one of the 36 ## following conditions apply: 37 ## 38 ## - There are invalid characters in an element whose contents is 39 ## restricted to a limited set of characters. For example, there are 40 ## characters in a tag URI which are not valid URI characters. 41 ## - An element has invalid indentation. This can happen for example if 42 ## a block list element indicated by ``"- "`` is less indented than 43 ## the element in the previous line, but there is no block sequence 44 ## list open at the same indentation level. 45 ## - The YAML structure is invalid. For example, an explicit block map 46 ## indicated by ``"? "`` and ``": "`` may not suddenly have a block 47 ## sequence item (``"- "``) at the same indentation level. Another 48 ## possible violation is closing a flow style object with the wrong 49 ## closing character (``}``, ``]``) or not closing it at all. 50 ## - A custom tag shorthand is used that has not previously been 51 ## declared with a ``%TAG`` directive. 52 ## - Multiple tags or anchors are defined for the same node. 53 ## - An alias is used which does not map to any anchor that has 54 ## previously been declared in the same document. 55 ## - An alias has a tag or anchor associated with it. 56 ## 57 ## Some elements in this list are vague. For a detailed description of a 58 ## valid YAML character stream, see the YAML specification. 59 60 State = proc(ctx: Context, e: var Event): bool {.gcSafe, raises: [CatchableError].} 61 62 Level = object 63 state: State 64 indentation: int 65 66 Context = ref object of YamlStream 67 handles: seq[tuple[handle, uriPrefix: string]] 68 issueWarnings: bool 69 lex: Lexer 70 levels: seq[Level] 71 keyCache: seq[Event] 72 keyCachePos: int 73 caching: bool 74 75 headerProps, inlineProps: Properties 76 headerStart, inlineStart: Mark 77 blockIndentation: int 78 79 const defaultProperties = (yAnchorNone, yTagQuestionMark) 80 81 # parser states 82 83 {.push gcSafe, raises: [CatchableError].} 84 proc atStreamStart(ctx: Context, e: var Event): bool 85 proc atStreamEnd(ctx: Context, e : var Event): bool {.hint[XCannotRaiseY]: off.} 86 proc beforeDoc(ctx: Context, e: var Event): bool 87 proc beforeDocEnd(ctx: Context, e: var Event): bool 88 proc afterDirectivesEnd(ctx: Context, e: var Event): bool 89 proc beforeImplicitRoot(ctx: Context, e: var Event): bool 90 proc atBlockIndentation(ctx: Context, e: var Event): bool 91 proc beforeBlockIndentation(ctx: Context, e: var Event): bool 92 proc beforeNodeProperties(ctx: Context, e: var Event): bool 93 proc afterCompactParent(ctx: Context, e: var Event): bool 94 proc afterCompactParentProps(ctx: Context, e: var Event): bool 95 proc mergePropsOnNewline(ctx: Context, e: var Event): bool 96 proc beforeFlowItemProps(ctx: Context, e: var Event): bool 97 proc inBlockSeq(ctx: Context, e: var Event): bool 98 proc beforeBlockMapValue(ctx: Context, e: var Event): bool 99 proc atBlockIndentationProps(ctx: Context, e: var Event): bool 100 proc beforeFlowItem(ctx: Context, e: var Event): bool 101 proc afterFlowSeqSep(ctx: Context, e: var Event): bool 102 proc afterFlowMapSep(ctx: Context, e: var Event): bool 103 proc atBlockMapKeyProps(ctx: Context, e: var Event): bool 104 proc afterImplicitKey(ctx: Context, e: var Event): bool 105 proc afterBlockParent(ctx: Context, e: var Event): bool 106 proc afterBlockParentProps(ctx: Context, e: var Event): bool 107 proc afterImplicitPairStart(ctx: Context, e: var Event): bool 108 proc beforePairValue(ctx: Context, e: var Event): bool 109 proc atEmptyPairKey(ctx: Context, e: var Event): bool {.hint[XCannotRaiseY]: off.} 110 proc afterFlowMapValue(ctx: Context, e: var Event): bool 111 proc afterFlowSeqSepProps(ctx: Context, e: var Event): bool 112 proc afterFlowSeqItem(ctx: Context, e: var Event): bool 113 proc afterPairValue(ctx: Context, e: var Event): bool {.hint[XCannotRaiseY]: off.} 114 proc emitCached(ctx: Context, e: var Event): bool {.hint[XCannotRaiseY]: off.} 115 {.pop.} 116 117 template pushLevel(ctx: Context, newState: State, newIndent: int) = 118 debug("parser: push " & newState.astToStr & ", indent = " & $newIndent) 119 ctx.levels.add(Level(state: newState, indentation: newIndent)) 120 121 template pushLevel(ctx: Context, newState: State) = 122 debug("parser: push " & newState.astToStr) 123 ctx.levels.add(Level(state: newState)) 124 125 template transition(ctx: Context, newState: State) = 126 debug("parser: transition " & newState.astToStr) 127 ctx.levels[^1].state = newState 128 129 template transition(ctx: Context, newState: State, newIndent) = 130 debug("parser: transtion " & newState.astToStr & ", indent = " & $newIndent) 131 ctx.levels[^1] = Level(state: newState, indentation: newIndent) 132 133 template updateIndentation(ctx: Context, newIndent: int) = 134 debug("parser: update indent = " & $newIndent) 135 ctx.levels[^1].indentation = newIndent 136 137 template popLevel(ctx: Context) = 138 debug("parser: pop") 139 discard ctx.levels.pop() 140 141 proc resolveHandle(ctx: Context, handle: string): string {.raises: [].} = 142 for item in ctx.handles: 143 if item.handle == handle: 144 return item.uriPrefix 145 return "" 146 147 proc init[T](ctx: Context, p: YamlParser, source: T) {.inline.} = 148 ctx.pushLevel(atStreamStart, -2) 149 ctx.nextImpl = proc(s: YamlStream, e: var Event): bool {.raises: [CatchableError].} = 150 let c = Context(s) 151 return c.levels[^1].state(c, e) 152 ctx.lastTokenContextImpl = proc(s: YamlStream, lineContent: var string): bool = 153 lineContent = Context(s).lex.currentLine() 154 return true 155 ctx.headerProps = defaultProperties 156 ctx.inlineProps = defaultProperties 157 ctx.issueWarnings = p.issueWarnings 158 ctx.lex.init(source) 159 ctx.keyCachePos = 0 160 ctx.caching = false 161 162 # interface 163 164 proc init*(p: var YamlParser, issueWarnings: bool = false) = 165 ## Initializes a YAML parser. 166 p.issueWarnings = issueWarnings 167 168 proc initYamlParser*(issueWarnings: bool = false): YamlParser = 169 ## Creates an initializes YAML parser and returns it 170 result.issueWarnings = issueWarnings 171 172 proc parse*(p: YamlParser, s: Stream): YamlStream = 173 let ctx = new(Context) 174 ctx.init(p, s) 175 return ctx 176 177 proc parse*(p: YamlParser, s: string): YamlStream = 178 let ctx = new(Context) 179 ctx.init(p, s) 180 return ctx 181 182 # implementation 183 184 proc isEmpty(props: Properties): bool = 185 result = props.anchor == yAnchorNone and 186 props.tag == yTagQuestionMark 187 188 proc generateError(ctx: Context, message: string): 189 ref YamlParserError {.raises: [], .} = 190 result = (ref YamlParserError)( 191 msg: message, parent: nil, mark: ctx.lex.curStartPos, 192 lineContent: ctx.lex.currentLine()) 193 194 proc safeNext(ctx: Context) = 195 try: 196 ctx.lex.next() 197 except LexerError as e: 198 raise (ref YamlParserError)( 199 msg: e.msg, parent: nil, mark: Mark(line: e.line, column: e.column), 200 lineContent: e.lineContent) 201 202 proc parseTag(ctx: Context): Tag = 203 let handle = ctx.lex.fullLexeme() 204 var uri = ctx.resolveHandle(handle) 205 if uri == "": 206 raise ctx.generateError("unknown handle: " & escape(handle)) 207 ctx.safeNext() 208 if ctx.lex.cur != Token.Suffix: 209 raise ctx.generateError("unexpected token (expected tag suffix): " & $ctx.lex.cur) 210 uri.add(ctx.lex.evaluated) 211 return Tag(uri) 212 213 proc toStyle(t: Token): ScalarStyle = 214 return (case t 215 of Plain: ssPlain 216 of SingleQuoted: ssSingleQuoted 217 of DoubleQuoted: ssDoubleQuoted 218 of Literal: ssLiteral 219 of Folded: ssFolded 220 else: ssAny) 221 222 proc mergeProps(ctx: Context, src, target: var Properties) = 223 if src.tag != yTagQuestionMark: 224 if target.tag != yTagQuestionMark: 225 raise ctx.generateError("Only one tag allowed per node") 226 target.tag = src.tag 227 src.tag = yTagQuestionMark 228 if src.anchor != yAnchorNone: 229 if target.anchor != yAnchorNone: 230 raise ctx.generateError("Only one anchor allowed per node") 231 target.anchor = src.anchor 232 src.anchor = yAnchorNone 233 234 proc autoScalarTag(props: Properties, t: Token): Properties = 235 result = props 236 if t in {Token.SingleQuoted, Token.DoubleQuoted} and 237 props.tag == yTagQuestionMark: 238 result.tag = yTagExclamationMark 239 240 proc atStreamStart(ctx: Context, e: var Event): bool = 241 ctx.transition(atStreamEnd) 242 ctx.pushLevel(beforeDoc, -1) 243 e = Event(startPos: ctx.lex.curStartPos, endPos: ctx.lex.curStartPos, kind: yamlStartStream) 244 ctx.safeNext() 245 resetHandles(ctx.handles) 246 return true 247 248 proc atStreamEnd(ctx: Context, e : var Event): bool = 249 e = Event(startPos: ctx.lex.curStartPos, 250 endPos: ctx.lex.curStartPos, kind: yamlEndStream) 251 return true 252 253 proc beforeDoc(ctx: Context, e: var Event): bool = 254 var version = "" 255 var seenDirectives = false 256 while true: 257 case ctx.lex.cur 258 of DocumentEnd: 259 if seenDirectives: 260 raise ctx.generateError("Missing `---` after directives") 261 ctx.safeNext() 262 of DirectivesEnd: 263 e = startDocEvent(true, version, ctx.handles, ctx.lex.curStartPos, ctx.lex.curEndPos) 264 ctx.safeNext() 265 ctx.transition(beforeDocEnd) 266 ctx.pushLevel(afterDirectivesEnd, -1) 267 return true 268 of StreamEnd: 269 if seenDirectives: 270 raise ctx.generateError("Missing `---` after directives") 271 ctx.popLevel() 272 return false 273 of Indentation: 274 e = startDocEvent(false, version, ctx.handles, ctx.lex.curStartPos, ctx.lex.curEndPos) 275 ctx.transition(beforeDocEnd) 276 ctx.pushLevel(beforeImplicitRoot, -1) 277 return true 278 of YamlDirective: 279 seenDirectives = true 280 ctx.safeNext() 281 if ctx.lex.cur != Token.DirectiveParam: 282 raise ctx.generateError("Invalid token (expected YAML version string): " & $ctx.lex.cur) 283 elif version != "": 284 raise ctx.generateError("Duplicate %YAML") 285 version = ctx.lex.fullLexeme() 286 if version != "1.2" and ctx.issueWarnings: 287 discard # TODO 288 ctx.safeNext() 289 of TagDirective: 290 seenDirectives = true 291 ctx.safeNext() 292 if ctx.lex.cur != Token.TagHandle: 293 raise ctx.generateError("Invalid token (expected tag handle): " & $ctx.lex.cur) 294 let tagHandle = ctx.lex.fullLexeme() 295 ctx.safeNext() 296 if ctx.lex.cur != Token.Suffix: 297 raise ctx.generateError("Invalid token (expected tag URI): " & $ctx.lex.cur) 298 discard registerHandle(ctx.handles, tagHandle, ctx.lex.evaluated) 299 ctx.safeNext() 300 of UnknownDirective: 301 seenDirectives = true 302 # TODO: issue warning 303 while true: 304 ctx.safeNext() 305 if ctx.lex.cur != Token.DirectiveParam: break 306 else: 307 raise ctx.generateError("Unexpected token (expected directive or document start): " & $ctx.lex.cur) 308 309 proc afterDirectivesEnd(ctx: Context, e: var Event): bool = 310 case ctx.lex.cur 311 of nodePropertyKind: 312 ctx.inlineStart = ctx.lex.curStartPos 313 ctx.pushLevel(beforeNodeProperties) 314 return false 315 of Indentation: 316 ctx.headerStart = ctx.inlineStart 317 ctx.transition(atBlockIndentation) 318 ctx.pushLevel(beforeBlockIndentation) 319 return false 320 of DocumentEnd, DirectivesEnd, StreamEnd: 321 e = scalarEvent("", ctx.inlineProps, ssPlain, ctx.lex.curStartPos, ctx.lex.curEndPos) 322 ctx.popLevel() 323 return true 324 of scalarTokenKind: 325 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 326 toStyle(ctx.lex.cur), ctx.lex.curStartPos, ctx.lex.curEndPos) 327 ctx.popLevel() 328 ctx.safeNext() 329 return true 330 else: 331 raise ctx.generateError("Illegal content at `---`: " & $ctx.lex.cur) 332 333 proc beforeImplicitRoot(ctx: Context, e: var Event): bool = 334 if ctx.lex.cur != Token.Indentation: 335 raise ctx.generateError("Unexpected token (expected line start): " & $ctx.lex.cur) 336 ctx.inlineStart = ctx.lex.curEndPos 337 ctx.headerStart = ctx.lex.curEndPos 338 ctx.updateIndentation(ctx.lex.recentIndentation()) 339 ctx.safeNext() 340 case ctx.lex.cur 341 of SeqItemInd, MapKeyInd, MapValueInd: 342 ctx.transition(afterCompactParent) 343 return false 344 of scalarTokenKind, MapStart, SeqStart: 345 ctx.transition(atBlockIndentationProps) 346 return false 347 of nodePropertyKind: 348 ctx.transition(atBlockIndentationProps) 349 ctx.pushLevel(beforeNodeProperties) 350 else: 351 raise ctx.generateError("Unexpected token (expected collection start): " & $ctx.lex.cur) 352 353 proc atBlockIndentation(ctx: Context, e: var Event): bool = 354 if ctx.blockIndentation == ctx.levels[^1].indentation and 355 (ctx.lex.cur != Token.SeqItemInd or 356 ctx.levels[^3].state == inBlockSeq): 357 e = scalarEvent("", ctx.headerProps, ssPlain, 358 ctx.headerStart, ctx.headerStart) 359 ctx.headerProps = defaultProperties 360 ctx.popLevel() 361 ctx.popLevel() 362 return true 363 ctx.inlineStart = ctx.lex.curStartPos 364 ctx.updateIndentation(ctx.lex.recentIndentation()) 365 case ctx.lex.cur 366 of nodePropertyKind: 367 if isEmpty(ctx.headerProps): 368 ctx.transition(mergePropsOnNewline) 369 else: 370 ctx.transition(atBlockIndentationProps) 371 ctx.pushLevel(beforeNodeProperties) 372 return false 373 of SeqItemInd: 374 e = startSeqEvent(csBlock, ctx.headerProps, 375 ctx.headerStart, ctx.lex.curEndPos) 376 ctx.headerProps = defaultProperties 377 ctx.transition(inBlockSeq, ctx.lex.recentIndentation()) 378 ctx.pushLevel(beforeBlockIndentation) 379 ctx.pushLevel(afterCompactParent, ctx.lex.recentIndentation()) 380 ctx.safeNext() 381 return true 382 of MapKeyInd: 383 e = startMapEvent(csBlock, ctx.headerProps, 384 ctx.headerStart, ctx.lex.curEndPos) 385 ctx.headerProps = defaultProperties 386 ctx.transition(beforeBlockMapValue, ctx.lex.recentIndentation()) 387 ctx.pushLevel(beforeBlockIndentation) 388 ctx.pushLevel(afterCompactParent, ctx.lex.recentIndentation()) 389 ctx.safeNext() 390 return true 391 of Plain, SingleQuoted, DoubleQuoted: 392 ctx.updateIndentation(ctx.lex.recentIndentation()) 393 let scalarToken = ctx.lex.cur 394 e = scalarEvent(ctx.lex.evaluated, ctx.headerProps, 395 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 396 ctx.headerProps = defaultProperties 397 let headerEnd = ctx.lex.curStartPos 398 ctx.safeNext() 399 if ctx.lex.cur == Token.MapValueInd: 400 if ctx.lex.lastScalarWasMultiline(): 401 raise ctx.generateError("Implicit mapping key may not be multiline") 402 let props = e.scalarProperties 403 e.scalarProperties = autoScalarTag(defaultProperties, scalarToken) 404 ctx.keyCache.add(move(e)) 405 e = startMapEvent(csBlock, props, ctx.headerStart, headerEnd) 406 ctx.transition(afterImplicitKey) 407 ctx.pushLevel(emitCached) 408 else: 409 e.scalarProperties = autoScalarTag(e.scalarProperties, scalarToken) 410 ctx.popLevel() 411 return true 412 of Alias: 413 e = aliasEvent(ctx.lex.shortLexeme().Anchor, ctx.inlineStart, ctx.lex.curEndPos) 414 ctx.inlineProps = defaultProperties 415 let headerEnd = ctx.lex.curStartPos 416 ctx.safeNext() 417 if ctx.lex.cur == Token.MapValueInd: 418 ctx.keyCache.add(move(e)) 419 e = startMapEvent(csBlock, ctx.headerProps, ctx.headerStart, headerEnd) 420 ctx.headerProps = defaultProperties 421 ctx.transition(afterImplicitKey) 422 ctx.pushLevel(emitCached) 423 elif not isEmpty(ctx.headerProps): 424 raise ctx.generateError("Alias may not have properties") 425 else: 426 ctx.popLevel() 427 return true 428 else: 429 ctx.transition(atBlockIndentationProps) 430 return false 431 432 proc atBlockIndentationProps(ctx: Context, e: var Event): bool = 433 ctx.updateIndentation(ctx.lex.recentIndentation()) 434 case ctx.lex.cur 435 of MapValueInd: 436 ctx.keyCache.add(scalarEvent("", ctx.inlineProps, ssPlain, ctx.inlineStart, ctx.lex.curEndPos)) 437 ctx.inlineProps = defaultProperties 438 e = startMapEvent(csBlock, ctx.headerProps, ctx.lex.curStartPos, ctx.lex.curEndPos) 439 ctx.headerProps = defaultProperties 440 ctx.transition(afterImplicitKey) 441 ctx.pushLevel(emitCached) 442 return true 443 of Plain, SingleQuoted, DoubleQuoted: 444 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 445 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 446 ctx.inlineProps = defaultProperties 447 let headerEnd = ctx.lex.curStartPos 448 ctx.safeNext() 449 if ctx.lex.cur == Token.MapValueInd: 450 if ctx.lex.lastScalarWasMultiline(): 451 raise ctx.generateError("Implicit mapping key may not be multiline") 452 ctx.keyCache.add(move(e)) 453 e = startMapEvent(csBlock, ctx.headerProps, ctx.headerStart, headerEnd) 454 ctx.headerProps = defaultProperties 455 ctx.transition(afterImplicitKey) 456 ctx.pushLevel(emitCached) 457 else: 458 ctx.mergeProps(ctx.headerProps, e.scalarProperties) 459 ctx.popLevel() 460 return true 461 of MapStart, SeqStart: 462 let 463 startPos = ctx.lex.curStartPos 464 indent = ctx.lex.currentIndentation() 465 levelDepth = ctx.levels.len 466 ctx.transition(beforeFlowItemProps) 467 ctx.caching = true 468 while ctx.levels.len >= levelDepth: 469 ctx.keyCache.add(ctx.next()) 470 ctx.caching = false 471 if ctx.lex.cur == Token.MapValueInd: 472 ctx.pushLevel(afterImplicitKey, indent) 473 ctx.pushLevel(emitCached) 474 if ctx.lex.curStartPos.line != startPos.line: 475 raise ctx.generateError("Implicit mapping key may not be multiline") 476 e = startMapEvent(csBlock, ctx.headerProps, ctx.headerStart, startPos) 477 ctx.headerProps = defaultProperties 478 return true 479 else: 480 ctx.pushLevel(emitCached) 481 return false 482 of Literal, Folded: 483 ctx.mergeProps(ctx.inlineProps, ctx.headerProps) 484 e = scalarEvent(ctx.lex.evaluated, ctx.headerProps, toStyle(ctx.lex.cur), 485 ctx.inlineStart, ctx.lex.curEndPos) 486 ctx.headerProps = defaultProperties 487 ctx.safeNext() 488 ctx.popLevel() 489 return true 490 of Indentation: 491 ctx.safeNext() 492 ctx.transition(atBlockIndentation) 493 return false 494 of StreamEnd, DocumentEnd, DirectivesEnd: 495 e = scalarEvent("", ctx.inlineProps, ssPlain, ctx.inlineStart, ctx.lex.curStartPos) 496 ctx.inlineProps = defaultProperties 497 ctx.popLevel() 498 return true 499 else: 500 raise ctx.generateError("Unexpected token (expected block content): " & $ctx.lex.cur) 501 502 proc beforeNodeProperties(ctx: Context, e: var Event): bool = 503 case ctx.lex.cur 504 of TagHandle: 505 if ctx.inlineProps.tag != yTagQuestionMark: 506 raise ctx.generateError("Only one tag allowed per node") 507 ctx.inlineProps.tag = ctx.parseTag() 508 of VerbatimTag: 509 if ctx.inlineProps.tag != yTagQuestionMark: 510 raise ctx.generateError("Only one tag allowed per node") 511 ctx.inlineProps.tag = Tag(move(ctx.lex.evaluated)) 512 of Token.Anchor: 513 if ctx.inlineProps.anchor != yAnchorNone: 514 raise ctx.generateError("Only one anchor allowed per node") 515 ctx.inlineProps.anchor = ctx.lex.shortLexeme().Anchor 516 of Indentation: 517 ctx.mergeProps(ctx.inlineProps, ctx.headerProps) 518 ctx.popLevel() 519 return false 520 of Alias: 521 raise ctx.generateError("Alias may not have node properties") 522 else: 523 ctx.popLevel() 524 return false 525 ctx.safeNext() 526 return false 527 528 proc afterCompactParent(ctx: Context, e: var Event): bool = 529 ctx.inlineStart = ctx.lex.curStartPos 530 case ctx.lex.cur 531 of nodePropertyKind: 532 ctx.transition(afterCompactParentProps) 533 ctx.pushLevel(beforeNodeProperties) 534 of SeqItemInd: 535 e = startSeqEvent(csBlock, ctx.headerProps, ctx.headerStart, ctx.lex.curEndPos) 536 ctx.headerProps = defaultProperties 537 ctx.transition(inBlockSeq, ctx.lex.recentIndentation()) 538 ctx.pushLevel(beforeBlockIndentation) 539 ctx.pushLevel(afterCompactParent, ctx.lex.recentIndentation()) 540 ctx.safeNext() 541 return true 542 of MapKeyInd: 543 e = startMapEvent(csBlock, ctx.headerProps, ctx.headerStart, ctx.lex.curEndPos) 544 ctx.headerProps = defaultProperties 545 ctx.transition(beforeBlockMapValue, ctx.lex.recentIndentation()) 546 ctx.pushLevel(beforeBlockIndentation) 547 ctx.pushLevel(afterCompactParent, ctx.lex.recentIndentation) 548 ctx.safeNext() 549 return true 550 else: 551 ctx.transition(afterCompactParentProps) 552 return false 553 554 proc afterCompactParentProps(ctx: Context, e: var Event): bool = 555 ctx.updateIndentation(ctx.lex.recentIndentation()) 556 case ctx.lex.cur 557 of nodePropertyKind: 558 ctx.pushLevel(beforeNodeProperties) 559 return false 560 of Indentation: 561 ctx.headerStart = ctx.inlineStart 562 ctx.transition(atBlockIndentation, ctx.levels[^3].indentation) 563 ctx.pushLevel(beforeBlockIndentation) 564 return false 565 of MapValueInd: 566 ctx.keyCache.add(scalarEvent("", ctx.inlineProps, ssPlain, ctx.inlineStart, ctx.lex.curStartPos)) 567 ctx.inlineProps = defaultProperties 568 e = startMapEvent(csBlock, defaultProperties, ctx.lex.curStartPos, ctx.lex.curStartPos) 569 ctx.transition(afterImplicitKey) 570 ctx.pushLevel(emitCached) 571 return true 572 of Alias: 573 e = aliasEvent(ctx.lex.shortLexeme().Anchor, ctx.inlineStart, ctx.lex.curEndPos) 574 let headerEnd = ctx.lex.curStartPos 575 ctx.safeNext() 576 if ctx.lex.cur == Token.MapValueInd: 577 ctx.keyCache.add(move(e)) 578 e = startMapEvent(csBlock, defaultProperties, headerEnd, headerEnd) 579 ctx.transition(afterImplicitKey) 580 ctx.pushLevel(emitCached) 581 else: 582 ctx.popLevel() 583 return true 584 of scalarTokenKind: 585 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 586 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 587 ctx.inlineProps = defaultProperties 588 let headerEnd = ctx.lex.curStartPos 589 ctx.updateIndentation(ctx.lex.recentIndentation()) 590 ctx.safeNext() 591 if ctx.lex.cur == Token.MapValueInd: 592 if ctx.lex.lastScalarWasMultiline(): 593 raise ctx.generateError("Implicit mapping key may not be multiline") 594 ctx.keyCache.add(move(e)) 595 e = startMapEvent(csBlock, defaultProperties, headerEnd, headerEnd) 596 ctx.transition(afterImplicitKey) 597 ctx.pushLevel(emitCached) 598 else: 599 ctx.popLevel() 600 return true 601 of MapStart, SeqStart, StreamEnd, DocumentEnd, DirectivesEnd: 602 ctx.transition(atBlockIndentationProps) 603 return false 604 else: 605 raise ctx.generateError("Unexpected token (expected newline or flow item start: " & $ctx.lex.cur) 606 607 proc afterBlockParent(ctx: Context, e: var Event): bool = 608 ctx.inlineStart = ctx.lex.curStartPos 609 case ctx.lex.cur 610 of nodePropertyKind: 611 ctx.transition(afterBlockParentProps) 612 ctx.pushLevel(beforeNodeProperties) 613 of SeqItemInd, MapKeyInd: 614 raise ctx.generateError("Compact notation not allowed after implicit key") 615 else: 616 ctx.transition(afterBlockParentProps) 617 return false 618 619 proc afterBlockParentProps(ctx: Context, e: var Event): bool = 620 ctx.updateIndentation(ctx.lex.recentIndentation()) 621 case ctx.lex.cur 622 of nodePropertyKind: 623 ctx.pushLevel(beforeNodeProperties) 624 return false 625 of MapValueInd: 626 raise ctx.generateError("Compact notation not allowed after implicit key") 627 of scalarTokenKind: 628 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 629 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 630 ctx.inlineProps = defaultProperties 631 ctx.safeNext() 632 if ctx.lex.cur == Token.MapValueInd: 633 raise ctx.generateError("Compact notation not allowed after implicit key") 634 ctx.popLevel() 635 return true 636 else: 637 ctx.transition(afterCompactParentProps) 638 return false 639 640 proc mergePropsOnNewline(ctx: Context, e: var Event): bool = 641 ctx.updateIndentation(ctx.lex.recentIndentation()) 642 if ctx.lex.cur == Token.Indentation: 643 ctx.mergeProps(ctx.inlineProps, ctx.headerProps) 644 ctx.transition(afterCompactParentProps) 645 return false 646 647 proc beforeDocEnd(ctx: Context, e: var Event): bool = 648 case ctx.lex.cur 649 of DocumentEnd: 650 e = endDocEvent(true, ctx.lex.curStartPos, ctx.lex.curEndPos) 651 ctx.transition(beforeDoc) 652 ctx.safeNext() 653 resetHandles(ctx.handles) 654 of StreamEnd: 655 e = endDocEvent(false, ctx.lex.curStartPos, ctx.lex.curEndPos) 656 ctx.popLevel() 657 of DirectivesEnd: 658 e = endDocEvent(false, ctx.lex.curStartPos, ctx.lex.curStartPos) 659 ctx.transition(beforeDoc) 660 resetHandles(ctx.handles) 661 else: 662 raise ctx.generateError("Unexpected token (expected document end): " & $ctx.lex.cur) 663 return true 664 665 proc inBlockSeq(ctx: Context, e: var Event): bool = 666 if ctx.blockIndentation > ctx.levels[^1].indentation: 667 raise ctx.generateError("Invalid indentation: got " & $ctx.blockIndentation & ", expected " & $ctx.levels[^1].indentation) 668 case ctx.lex.cur 669 of SeqItemInd: 670 ctx.safeNext() 671 ctx.pushLevel(beforeBlockIndentation) 672 ctx.pushLevel(afterCompactParent, ctx.blockIndentation) 673 return false 674 else: 675 if ctx.levels[^3].indentation == ctx.levels[^1].indentation: 676 e = endSeqEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 677 ctx.popLevel() 678 ctx.popLevel() 679 return true 680 else: 681 raise ctx.generateError("Illegal token (expected block sequence indicator): " & $ctx.lex.cur) 682 683 proc beforeBlockMapKey(ctx: Context, e: var Event): bool = 684 if ctx.blockIndentation > ctx.levels[^1].indentation: 685 raise ctx.generateError("Invalid indentation: got " & $ctx.blockIndentation & ", expected " & $ctx.levels[^1].indentation) 686 ctx.inlineStart = ctx.lex.curStartPos 687 case ctx.lex.cur 688 of MapKeyInd: 689 ctx.transition(beforeBlockMapValue) 690 ctx.pushLevel(beforeBlockIndentation) 691 ctx.pushLevel(afterCompactParent, ctx.blockIndentation) 692 ctx.safeNext() 693 return false 694 of nodePropertyKind: 695 ctx.transition(atBlockMapKeyProps) 696 ctx.pushLevel(beforeNodeProperties) 697 return false 698 of Plain, SingleQuoted, DoubleQuoted: 699 ctx.transition(atBlockMapKeyProps) 700 return false 701 of Alias: 702 e = aliasEvent(ctx.lex.shortLexeme().Anchor, ctx.inlineStart, ctx.lex.curEndPos) 703 ctx.safeNext() 704 ctx.transition(afterImplicitKey) 705 return true 706 of MapValueInd: 707 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curEndPos) 708 ctx.transition(beforeBlockMapValue) 709 return true 710 else: 711 raise ctx.generateError("Unexpected token (expected mapping key): " & $ctx.lex.cur) 712 713 proc atBlockMapKeyProps(ctx: Context, e: var Event): bool = 714 case ctx.lex.cur 715 of nodePropertyKind: 716 ctx.pushLevel(beforeNodeProperties) 717 of Alias: 718 e = aliasEvent(ctx.lex.shortLexeme().Anchor, ctx.inlineStart, ctx.lex.curEndPos) 719 of Plain, SingleQuoted, DoubleQuoted: 720 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 721 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 722 ctx.inlineProps = defaultProperties 723 if ctx.lex.lastScalarWasMultiline(): 724 raise ctx.generateError("Implicit mapping key may not be multiline") 725 of MapValueInd: 726 e = scalarEvent("", ctx.inlineProps, ssPlain, ctx.inlineStart, ctx.lex.curStartPos) 727 ctx.inlineProps = defaultProperties 728 ctx.transition(afterImplicitKey) 729 return true 730 else: 731 raise ctx.generateError("Unexpected token (expected implicit mapping key): " & $ctx.lex.cur) 732 ctx.safeNext() 733 ctx.transition(afterImplicitKey) 734 return true 735 736 proc afterImplicitKey(ctx: Context, e: var Event): bool = 737 if ctx.lex.cur != Token.MapValueInd: 738 raise ctx.generateError("Unexpected token (expected ':'): " & $ctx.lex.cur) 739 ctx.safeNext() 740 ctx.transition(beforeBlockMapKey) 741 ctx.pushLevel(beforeBlockIndentation) 742 ctx.pushLevel(afterBlockParent, max(0, ctx.levels[^2].indentation)) 743 return false 744 745 proc beforeBlockMapValue(ctx: Context, e: var Event): bool = 746 if ctx.blockIndentation > ctx.levels[^1].indentation: 747 raise ctx.generateError("Invalid indentation") 748 case ctx.lex.cur 749 of MapValueInd: 750 ctx.transition(beforeBlockMapKey) 751 ctx.pushLevel(beforeBlockIndentation) 752 ctx.pushLevel(afterCompactParent, ctx.blockIndentation) 753 ctx.safeNext() 754 of MapKeyInd, Plain, SingleQuoted, DoubleQuoted, nodePropertyKind: 755 # the value is allowed to be missing after an explicit key 756 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curEndPos) 757 ctx.transition(beforeBlockMapKey) 758 return true 759 else: 760 raise ctx.generateError("Unexpected token (expected mapping value): " & $ctx.lex.cur) 761 762 proc beforeBlockIndentation(ctx: Context, e: var Event): bool = 763 proc endBlockNode(e: var Event) = 764 if ctx.levels[^1].state == beforeBlockMapKey: 765 e = endMapEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 766 elif ctx.levels[^1].state == beforeBlockMapValue: 767 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curEndPos) 768 ctx.transition(beforeBlockMapKey) 769 ctx.pushLevel(beforeBlockIndentation) 770 return 771 elif ctx.levels[^1].state == inBlockSeq: 772 e = endSeqEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 773 elif ctx.levels[^1].state == atBlockIndentation: 774 e = scalarEvent("", ctx.headerProps, ssPlain, ctx.headerStart, ctx.headerStart) 775 ctx.headerProps = defaultProperties 776 elif ctx.levels[^1].state == beforeBlockIndentation: 777 raise ctx.generateError("Unexpected double beforeBlockIndentation") 778 else: 779 raise ctx.generateError("Internal error (please report this bug): unexpected state at endBlockNode") 780 ctx.popLevel() 781 ctx.popLevel() 782 case ctx.lex.cur 783 of Indentation: 784 ctx.blockIndentation = ctx.lex.currentIndentation() 785 if ctx.blockIndentation < ctx.levels[^1].indentation: 786 endBlockNode(e) 787 return true 788 else: 789 ctx.safeNext() 790 return false 791 of StreamEnd, DocumentEnd, DirectivesEnd: 792 ctx.blockIndentation = 0 793 if ctx.levels[^1].state != beforeDocEnd: 794 endBlockNode(e) 795 return true 796 else: 797 return false 798 else: 799 raise ctx.generateError("Unexpected content after node in block context (expected newline): " & $ctx.lex.cur) 800 801 proc beforeFlowItem(ctx: Context, e: var Event): bool = 802 ctx.inlineStart = ctx.lex.curStartPos 803 case ctx.lex.cur 804 of nodePropertyKind: 805 ctx.transition(beforeFlowItemProps) 806 ctx.pushLevel(beforeNodeProperties) 807 of Alias: 808 e = aliasEvent(ctx.lex.shortLexeme().Anchor, ctx.inlineStart, ctx.lex.curEndPos) 809 ctx.safeNext() 810 ctx.popLevel() 811 return true 812 else: 813 ctx.transition(beforeFlowItemProps) 814 return false 815 816 proc beforeFlowItemProps(ctx: Context, e: var Event): bool = 817 case ctx.lex.cur 818 of nodePropertyKind: 819 ctx.pushLevel(beforeNodeProperties) 820 of Alias: 821 e = aliasEvent(ctx.lex.shortLexeme().Anchor, ctx.inlineStart, ctx.lex.curEndPos) 822 ctx.safeNext() 823 ctx.popLevel() 824 of scalarTokenKind: 825 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 826 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 827 ctx.inlineProps = defaultProperties 828 ctx.safeNext() 829 ctx.popLevel() 830 of MapStart: 831 e = startMapEvent(csFlow, ctx.inlineProps, ctx.inlineStart, ctx.lex.curEndPos) 832 ctx.transition(afterFlowMapSep) 833 ctx.safeNext() 834 of SeqStart: 835 e = startSeqEvent(csFlow, ctx.inlineProps, ctx.inlineStart, ctx.lex.curEndPos) 836 ctx.transition(afterFlowSeqSep) 837 ctx.safeNext() 838 of MapEnd, SeqEnd, SeqSep, MapValueInd: 839 e = scalarEvent("", ctx.inlineProps, ssPlain, ctx.inlineStart, ctx.lex.curEndPos) 840 ctx.popLevel() 841 else: 842 raise ctx.generateError("Unexpected token (expected flow node): " & $ctx.lex.cur) 843 ctx.inlineProps = defaultProperties 844 return true 845 846 proc afterFlowMapKey(ctx: Context, e: var Event): bool = 847 case ctx.lex.cur 848 of MapValueInd: 849 ctx.transition(afterFlowMapValue) 850 ctx.pushLevel(beforeFlowItem) 851 ctx.safeNext() 852 return false 853 of SeqSep, MapEnd: 854 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curEndPos) 855 ctx.transition(afterFlowMapValue) 856 return true 857 else: 858 raise ctx.generateError("Unexpected token (expected ':'): " & $ctx.lex.cur) 859 860 proc afterFlowMapValue(ctx: Context, e: var Event): bool = 861 case ctx.lex.cur 862 of SeqSep: 863 ctx.transition(afterFlowMapSep) 864 ctx.safeNext() 865 return false 866 of MapEnd: 867 e = endMapEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 868 ctx.safeNext() 869 ctx.popLevel() 870 return true 871 of Plain, SingleQuoted, DoubleQuoted, MapKeyInd, Token.Anchor, Alias, MapStart, SeqStart: 872 raise ctx.generateError("Missing ','") 873 else: 874 raise ctx.generateError("Unexpected token (expected ',' or '}'): " & $ctx.lex.cur) 875 876 proc afterFlowSeqItem(ctx: Context, e: var Event): bool = 877 case ctx.lex.cur 878 of SeqSep: 879 ctx.transition(afterFlowSeqSep) 880 ctx.safeNext() 881 return false 882 of SeqEnd: 883 e = endSeqEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 884 ctx.safeNext() 885 ctx.popLevel() 886 return true 887 of Plain, SingleQuoted, DoubleQuoted, MapKeyInd, Token.Anchor, Alias, MapStart, SeqStart: 888 raise ctx.generateError("Missing ','") 889 else: 890 raise ctx.generateError("Unexpected token (expected ',' or ']'): " & $ctx.lex.cur) 891 892 proc afterFlowMapSep(ctx: Context, e: var Event): bool = 893 case ctx.lex.cur 894 of MapKeyInd: 895 ctx.safeNext() 896 of MapEnd: 897 e = endMapEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 898 ctx.safeNext() 899 ctx.popLevel() 900 return true 901 of SeqSep: 902 raise ctx.generateError("Missing mapping entry between commas (use '?' for an empty mapping entry)") 903 else: discard 904 ctx.transition(afterFlowMapKey) 905 ctx.pushLevel(beforeFlowItem) 906 return false 907 908 proc afterFlowSeqSep(ctx: Context, e: var Event): bool = 909 ctx.inlineStart = ctx.lex.curStartPos 910 case ctx.lex.cur 911 of SeqSep: 912 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curStartPos) 913 ctx.safeNext() 914 return true 915 of nodePropertyKind: 916 ctx.transition(afterFlowSeqSepProps) 917 ctx.pushLevel(beforeNodeProperties) 918 return false 919 of Plain, SingleQuoted, DoubleQuoted, MapStart, SeqStart: 920 ctx.transition(afterFlowSeqSepProps) 921 return false 922 of MapKeyInd: 923 ctx.transition(afterFlowSeqSepProps) 924 e = startMapEvent(csFlow, defaultProperties, ctx.lex.curStartPos, ctx.lex.curEndPos) 925 ctx.safeNext() 926 ctx.transition(afterFlowSeqItem) 927 ctx.pushLevel(beforePairValue) 928 ctx.pushLevel(beforeFlowItem) 929 return true 930 of MapValueInd: 931 ctx.transition(afterFlowSeqItem) 932 e = startMapEvent(csFlow, defaultProperties, ctx.lex.curStartPos, ctx.lex.curEndPos) 933 ctx.pushLevel(atEmptyPairKey) 934 return true 935 of SeqEnd: 936 e = endSeqEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 937 ctx.safeNext() 938 ctx.popLevel() 939 return true 940 else: 941 ctx.transition(afterFlowSeqItem) 942 ctx.pushLevel(beforeFlowItem) 943 return false 944 945 proc afterFlowSeqSepProps(ctx: Context, e: var Event): bool = 946 # here we handle potential implicit single pairs within flow sequences. 947 ctx.transition(afterFlowSeqItem) 948 case ctx.lex.cur 949 of Plain, SingleQuoted, DoubleQuoted: 950 e = scalarEvent(ctx.lex.evaluated, autoScalarTag(ctx.inlineProps, ctx.lex.cur), 951 toStyle(ctx.lex.cur), ctx.inlineStart, ctx.lex.curEndPos) 952 ctx.inlineProps = defaultProperties 953 ctx.safeNext() 954 if ctx.lex.cur == Token.MapValueInd: 955 ctx.pushLevel(afterImplicitPairStart) 956 if ctx.caching: 957 ctx.keyCache.add(startMapEvent(csFlow, defaultProperties, ctx.lex.curStartPos, ctx.lex.curStartPos)) 958 else: 959 ctx.keyCache.add(move(e)) 960 e = startMapEvent(csFlow, defaultProperties, ctx.lex.curStartPos, ctx.lex.curStartPos) 961 ctx.pushLevel(emitCached) 962 return true 963 of MapStart, SeqStart: 964 let 965 startPos = ctx.lex.curStartPos 966 indent = ctx.levels[^1].indentation 967 cacheStart = ctx.keyCache.len 968 levelDepth = ctx.levels.len 969 alreadyCaching = ctx.caching 970 ctx.pushLevel(beforeFlowItemProps) 971 ctx.caching = true 972 while ctx.levels.len > levelDepth: 973 ctx.keyCache.add(ctx.next()) 974 ctx.caching = alreadyCaching 975 if ctx.lex.cur == Token.MapValueInd: 976 ctx.pushLevel(afterImplicitPairStart, indent) 977 if ctx.lex.curStartPos.line != startPos.line: 978 raise ctx.generateError("Implicit mapping key may not be multiline") 979 if not alreadyCaching: 980 ctx.pushLevel(emitCached) 981 e = startMapEvent(csPair, defaultProperties, startPos, startPos) 982 return true 983 else: 984 # we are already filling a cache. 985 # so we just squeeze the map start in. 986 ctx.keyCache.insert(startMapEvent(csPair, defaultProperties, startPos, startPos), cacheStart) 987 return false 988 else: 989 if not alreadyCaching: 990 ctx.pushLevel(emitCached) 991 return false 992 else: 993 ctx.pushLevel(beforeFlowItem) 994 return false 995 996 proc atEmptyPairKey(ctx: Context, e: var Event): bool = 997 ctx.transition(beforePairValue) 998 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curStartPos) 999 return true 1000 1001 proc beforePairValue(ctx: Context, e: var Event): bool = 1002 if ctx.lex.cur == Token.MapValueInd: 1003 ctx.transition(afterPairValue) 1004 ctx.pushLevel(beforeFlowItem) 1005 ctx.safeNext() 1006 return false 1007 else: 1008 # pair ends here without value 1009 e = scalarEvent("", defaultProperties, ssPlain, ctx.lex.curStartPos, ctx.lex.curEndPos) 1010 ctx.popLevel() 1011 return true 1012 1013 proc afterImplicitPairStart(ctx: Context, e: var Event): bool = 1014 ctx.safeNext() 1015 ctx.transition(afterPairValue) 1016 ctx.pushLevel(beforeFlowItem) 1017 return false 1018 1019 proc afterPairValue(ctx: Context, e: var Event): bool = 1020 e = endMapEvent(ctx.lex.curStartPos, ctx.lex.curEndPos) 1021 ctx.popLevel() 1022 return true 1023 1024 proc emitCached(ctx: Context, e: var Event): bool = 1025 debug("emitCollection key: pos = " & $ctx.keyCachePos & ", len = " & $ctx.keyCache.len) 1026 yAssert(ctx.keyCachePos < ctx.keyCache.len) 1027 e = move(ctx.keyCache[ctx.keyCachePos]) 1028 inc(ctx.keyCachePos) 1029 if ctx.keyCachePos == len(ctx.keyCache): 1030 ctx.keyCache.setLen(0) 1031 ctx.keyCachePos = 0 1032 ctx.popLevel() 1033 return true 1034 1035 proc display*(p: YamlParser, event: Event): string = 1036 ## Generate a representation of the given event with proper visualization of 1037 ## anchor and tag (if any). The generated representation is conformant to the 1038 ## format used in the yaml test suite. 1039 ## 1040 ## This proc is an informed version of ``$`` on ``YamlStreamEvent`` which can 1041 ## properly display the anchor and tag name as it occurs in the input. 1042 ## However, it shall only be used while using the streaming API because after 1043 ## finishing the parsing of a document, the parser drops all information about 1044 ## anchor and tag names. 1045 case event.kind 1046 of yamlStartStream: result = "+STR" 1047 of yamlEndStream: result = "-STR" 1048 of yamlEndMap: result = "-MAP" 1049 of yamlEndSeq: result = "-SEQ" 1050 of yamlStartDoc: 1051 result = "+DOC" 1052 if event.explicitDirectivesEnd: result &= " ---" 1053 of yamlEndDoc: 1054 result = "-DOC" 1055 if event.explicitDocumentEnd: result &= " ..." 1056 of yamlStartMap: 1057 result = "+MAP" & renderAttrs(event.mapProperties, true) 1058 of yamlStartSeq: 1059 result = "+SEQ" & renderAttrs(event.seqProperties, true) 1060 of yamlScalar: 1061 result = "=VAL" & renderAttrs(event.scalarProperties, 1062 event.scalarStyle in {ssPlain, ssFolded, ssLiteral}) 1063 case event.scalarStyle 1064 of ssPlain, ssAny: result &= " :" 1065 of ssSingleQuoted: result &= " \'" 1066 of ssDoubleQuoted: result &= " \"" 1067 of ssLiteral: result &= " |" 1068 of ssFolded: result &= " >" 1069 result &= yamlTestSuiteEscape(event.scalarContent) 1070 of yamlAlias: result = "=ALI *" & $event.aliasTarget