From f249813b9048b166f26670ca79282817e56e16d6 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Tue, 22 Dec 2015 16:28:35 +0100 Subject: [PATCH] Implemented alias resolution * Parse alias nodes properly * Fixed a bug that prevented you from having objects as map keys * Use AnchorId for reporting anchors instead of strings * Do not use nil strings for anchor and tag --- src/yaml/private/lexer.nim | 21 ++++ src/yaml/sequential.nim | 252 ++++++++++++++++++++++++------------- test/parsing.nim | 80 ++++++++++-- 3 files changed, 257 insertions(+), 96 deletions(-) diff --git a/src/yaml/private/lexer.nim b/src/yaml/private/lexer.nim index 968fd15..c6df249 100644 --- a/src/yaml/private/lexer.nim +++ b/src/yaml/private/lexer.nim @@ -573,10 +573,13 @@ iterator tokens*(my: var YamlLexer): YamlLexerToken {.closure.} = my.column = curPos state = ylSingleQuotedScalar of '!': + my.column = curPos lastSpecialChar = '!' of '&': + my.column = curPos state = ylAnchor of '*': + my.column = curPos state = ylAlias of ' ': discard @@ -588,6 +591,7 @@ iterator tokens*(my: var YamlLexer): YamlLexerToken {.closure.} = my.column = curPos state = ylPlainScalar of '?', ':': + my.column = curPos lastSpecialChar = c of '|': yieldToken(yamlPipe) @@ -833,11 +837,28 @@ iterator tokens*(my: var YamlLexer): YamlLexerToken {.closure.} = else: my.content.add(c) of ylAlias: + if lastSpecialChar != '\0': + case c + of EndOfFile, '\r', '\x0A', ' ', '\t', '{', '}', '[', ']': + yieldToken(yamlAlias) + state = ylInitialInLine + continue + else: + my.content.add(lastSpecialChar) + lastSpecialChar = '\0' case c of EndOfFile, '\r', '\x0A', ' ', '\t', '{', '}', '[', ']': yieldToken(yamlAlias) state = ylInitialInLine continue + of ':': + lastSpecialChar = ':' + of ',': + if flowDepth > 0: + yieldToken(yamlAlias) + state = ylInitialInLine + continue + my.content.add(c) else: my.content.add(c) diff --git a/src/yaml/sequential.nim b/src/yaml/sequential.nim index d076651..f25307c 100644 --- a/src/yaml/sequential.nim +++ b/src/yaml/sequential.nim @@ -9,20 +9,21 @@ type yamlError, yamlWarning TagId* = distinct int + AnchorId* = distinct int YamlParserEvent* = ref object case kind*: YamlParserEventKind of yamlStartMap, yamlStartSequence: - objAnchor* : string # may be nil, may not be empty + objAnchor* : AnchorId objTag* : TagId of yamlScalar: - scalarAnchor* : string # may be nil + scalarAnchor* : AnchorId scalarTag* : TagId scalarContent*: string # may not be nil (but empty) of yamlEndMap, yamlEndSequence, yamlStartDocument, yamlEndDocument: discard of yamlAlias: - aliasName* : string # may not be nil nor empty + aliasTarget* : AnchorId of yamlError, yamlWarning: description* : string line* : int @@ -31,10 +32,10 @@ type YamlParserState = enum ylInitial, ylSkipDirective, ylBlockLineStart, ylBlockAfterTag, ylBlockAfterAnchor, ylBlockAfterAnchorAndTag, ylBlockAfterScalar, - ylBlockAfterColon, ylBlockMultilineScalar, ylBlockLineEnd, - ylBlockScalarHeader, ylBlockScalar, ylFlow, ylFlowAfterObject, - ylFlowAfterTag, ylFlowAfterAnchor, ylFlowAfterAnchorAndTag, - ylExpectingDocumentEnd + ylBlockAfterAlias, ylBlockAfterColon, ylBlockMultilineScalar, + ylBlockLineEnd, ylBlockScalarHeader, ylBlockScalar, ylFlow, + ylFlowAfterObject, ylFlowAfterTag, ylFlowAfterAnchor, + ylFlowAfterAnchorAndTag, ylExpectingDocumentEnd DocumentLevelMode = enum mBlockSequenceItem, mFlowSequenceItem, mExplicitBlockMapKey, @@ -54,10 +55,12 @@ type YamlSequentialParser* = object tags: OrderedTable[string, TagId] + anchors: OrderedTable[string, AnchorId] const - tagNonSpecificEmark*: TagId = 0.TagId # "!" non-specific tag - tagNonSpecificQmark*: TagId = 1.TagId # "?" non-specific tag + tagExclamationMark*: TagId = 0.TagId # "!" non-specific tag + tagQuestionMark* : TagId = 1.TagId # "?" non-specific tag + anchorNone*: AnchorId = (-1).AnchorId # no anchor defined # interface @@ -66,6 +69,9 @@ proc `==`*(left: YamlParserEvent, right: YamlParserEvent): bool proc `==`*(left, right: TagId): bool {.borrow.} proc `$`*(id: TagId): string {.borrow.} +proc `==`*(left, right: AnchorId): bool {.borrow.} +proc `$`*(id: AnchorId): string {.borrow.} + proc initParser*(): YamlSequentialParser # iterators cannot be pre-declared. @@ -77,12 +83,15 @@ proc uri*(parser: YamlSequentialParser, id: TagId): string proc registerUri*(parser: var YamlSequentialParser, uri: string): TagId +proc anchor*(parser: YamlSequentialParser, id: AnchorId): string + # implementation proc initParser*(): YamlSequentialParser = result.tags = initOrderedTable[string, TagId]() - result.tags["!"] = tagNonSpecificEmark - result.tags["?"] = tagNonSpecificQmark + result.tags["!"] = tagExclamationMark + result.tags["?"] = tagQuestionMark + result.anchors = initOrderedTable[string, AnchorId]() proc uri*(parser: YamlSequentialParser, id: TagId): string = for pair in parser.tags.pairs: @@ -95,6 +104,12 @@ proc registerUri*(parser: var YamlSequentialParser, uri: string): TagId = if parser.tags.hasKeyOrPut(uri, result): result = parser.tags[uri] +proc anchor*(parser: YamlSequentialParser, id: AnchorId): string = + for pair in parser.anchors.pairs: + if pair[1] == id: + return pair[0] + return nil + proc `==`*(left: YamlParserEvent, right: YamlParserEvent): bool = if left.kind != right.kind: return false @@ -109,7 +124,7 @@ proc `==`*(left: YamlParserEvent, right: YamlParserEvent): bool = left.scalarTag == right.scalarTag and left.scalarContent == right.scalarContent of yamlAlias: - result = left.aliasName == right.aliasName + result = left.aliasTarget == right.aliasTarget of yamlError, yamlWarning: result = left.description == right.description and left.line == right.line and left.column == right.column @@ -123,43 +138,56 @@ template yieldError(d: string) {.dirty.} = line: lex.line, column: lex.column) break parserLoop -template yieldScalar(content: string = "", quoted: bool = false) {.dirty.} = - var retTag: TagId - if isNil(tag): - retTag = if quoted: tagNonSpecificEmark else: tagNonSpecificQmark +template yieldUnexpectedToken(expected: string = "") {.dirty.} = + var msg = "[" & $state & "] Unexpected token" + if expected.len > 0: + msg.add(" (expected " & expected & ")") + msg.add(": " & $token) + yieldError(msg) + +proc resolveAnchor(parser: var YamlSequentialParser, anchor: var string): + AnchorId {.inline.} = + result = anchorNone + if anchor.len > 0: + result = cast[AnchorId](parser.anchors.len) + if parser.anchors.hasKeyOrPut(anchor, result): + result = parser.anchors[anchor] + anchor = "" + +proc resolveAlias(parser: var YamlSequentialParser, name: string): AnchorId = + try: + result = parser.anchors[name] + except KeyError: + result = anchorNone + +proc resolveTag(parser: var YamlSequentialParser, tag: var string, + quotedString: bool = false): TagId {.inline.} = + if tag.len == 0: + result = if quotedString: tagExclamationMark else: tagQuestionMark else: try: - retTag = parser.tags[tag] + result = parser.tags[tag] except KeyError: - retTag = cast[TagId](parser.tags.len) - parser.tags[tag] = retTag - + result = cast[TagId](parser.tags.len) + parser.tags[tag] = result + tag = "" + +template yieldScalar(content: string = "", quoted: bool = false) {.dirty.} = yield YamlParserEvent(kind: yamlScalar, - scalarAnchor: anchor, scalarTag: retTag, + scalarAnchor: resolveAnchor(parser, anchor), + scalarTag: resolveTag(parser, tag, quoted), scalarContent: content) - anchor = nil - tag = nil template yieldStart(k: YamlParserEventKind) {.dirty.} = - var retTag: TagId - if isNil(tag): - retTag = tagNonSpecificQmark - else: - try: - retTag = parser.tags[tag] - except KeyError: - retTag = cast[TagId](parser.tags.len) - parser.tags[tag] = retTag - - yield YamlParserEvent(kind: k, objAnchor: anchor, objTag: retTag) - anchor = nil - tag = nil + yield YamlParserEvent(kind: k, objAnchor: resolveAnchor(parser, anchor), + objTag: resolveTag(parser, tag)) template yieldDocumentEnd() {.dirty.} = yield YamlParserEvent(kind: yamlEndDocument) tagShorthands = initTable[string, string]() tagShorthands["!"] = "!" tagShorthands["!!"] = "tag:yaml.org,2002:" + parser.anchors = initOrderedTable[string, AnchorId]() template closeLevel(lvl: DocumentLevel) {.dirty.} = case lvl.mode @@ -171,20 +199,11 @@ template closeLevel(lvl: DocumentLevel) {.dirty.} = of mBlockSequenceItem, mFlowSequenceItem: yield YamlParserEvent(kind: yamlEndSequence) of mScalar: - var retTag: TagId - if isNil(tag): - retTag = tagNonSpecificQmark - else: - try: - retTag = parser.tags[tag] - except KeyError: - retTag = cast[TagId](parser.tags.len) - parser.tags[tag] = retTag + yield YamlParserEvent(kind: yamlScalar, + scalarAnchor: resolveAnchor(parser, anchor), + scalarTag: resolveTag(parser, tag), + scalarContent: scalarCache) - yield YamlParserEvent(kind: yamlScalar, scalarAnchor: anchor, - scalarTag: retTag, scalarContent: scalarCache) - anchor = nil - tag = nil else: yieldScalar() @@ -221,7 +240,7 @@ template handleBlockIndicator(expected, next: DocumentLevelMode, elif level.mode != mUnknown: yieldError("Invalid indentation") elif entering == yamlError: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() else: level.mode = next level.indicatorColumn = lex.column @@ -272,11 +291,12 @@ iterator events*(parser: var YamlSequentialParser, blockScalarTrailing: string = nil # cached values - tag: string = nil - anchor: string = nil + tag: string = "" + anchor: string = "" scalarCache: string = nil scalarIndentation: int scalarCacheIsQuoted: bool = false + aliasCache = anchorNone lex.open(input) tagShorthands["!"] = "!" @@ -407,6 +427,21 @@ iterator events*(parser: var YamlSequentialParser, state = ylBlockAfterScalar else: yieldError("Unexpected scalar") + of lexer.yamlAlias: + aliasCache = resolveAlias(parser, lex.content) + if aliasCache == anchorNone: + yieldError("[alias] Unknown anchor: " & lex.content) + if ancestry.len > 0: + if level.mode == mUnknown: + level = ancestry.pop() + else: + assert level.mode == mImplicitBlockMapKey + leaveMoreIndentedLevels() + case level.mode + of mUnknown, mImplicitBlockMapKey, mBlockSequenceItem: + state = ylBlockAfterAlias + else: + yieldError("Unexpected alias") of yamlStreamEnd: closeAllLevels() yield YamlParserEvent(kind: yamlEndDocument) @@ -422,7 +457,7 @@ iterator events*(parser: var YamlSequentialParser, state = ylFlow continue else: - yieldError("[block line start] Unexpected token: " & $token) + yieldUnexpectedToken() of ylBlockMultilineScalar: case token of yamlScalarPart: @@ -439,7 +474,7 @@ iterator events*(parser: var YamlSequentialParser, if level.mode != mScalar: state = ylBlockLineStart continue - yieldError("[multiline scalar ?:-] Unexpected token: " & $token) + yieldUnexpectedToken() of yamlDocumentEnd, yamlStreamEnd: closeAllLevels() scalarCache = nil @@ -449,8 +484,12 @@ iterator events*(parser: var YamlSequentialParser, closeAllLevels() state = ylInitial continue + of lexer.yamlAlias: + leaveMoreIndentedLevels() + state = ylBlockLineStart + continue else: - yieldError("[multiline scalar] Unexpected token: " & $token) + yieldUnexpectedToken() of ylBlockAfterScalar: case token of yamlColon: @@ -459,8 +498,8 @@ iterator events*(parser: var YamlSequentialParser, level.indentationColumn = scalarIndentation # tags and anchors are for key scalar, not for map. yield YamlParserEvent(kind: yamlStartMap, - objAnchor: nil, - objTag: tagNonSpecificQmark) + objAnchor: anchorNone, + objTag: tagQuestionMark) level.mode = mImplicitBlockMapValue ancestry.add(level) level = DocumentLevel(mode: mUnknown, indicatorColumn: -1, @@ -489,7 +528,38 @@ iterator events*(parser: var YamlSequentialParser, yield YamlParserEvent(kind: yamlEndDocument) break else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() + of ylBlockAfterAlias: + case token + of yamlColon: + assert level.mode in [mUnknown, mImplicitBlockMapKey] + if level.mode == mUnknown: + yield YamlParserEvent(kind: yamlStartMap, + objAnchor: anchorNone, + objTag: tagQuestionMark) + level.mode = mImplicitBlockMapValue + ancestry.add(level) + level = DocumentLevel(mode: mUnknown, indicatorColumn: -1, + indentationColumn: -1) + yield YamlParserEvent(kind: yamlAlias, aliasTarget: aliasCache) + state = ylBlockAfterColon + of yamlLineStart: + if level.mode == mImplicitBlockMapKey: + yieldError("Missing colon after implicit map key") + if level.mode == mUnknown: + assert ancestry.len > 0 + level = ancestry.pop() + yield YamlParserEvent(kind: yamlAlias, aliasTarget: aliasCache) + state = ylBlockLineStart + of yamlStreamEnd: + yield YamlParserEvent(kind: yamlAlias, aliasTarget: aliasCache) + if level.mode == mUnknown: + assert ancestry.len > 0 + level = ancestry.pop() + state = ylBlockLineEnd + continue + else: + yieldUnexpectedToken() of ylBlockAfterTag: case token of yamlAnchor: @@ -506,7 +576,7 @@ iterator events*(parser: var YamlSequentialParser, state = ylFlow continue else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() of ylBlockAfterAnchor: case token of lexer.yamlScalar: @@ -521,13 +591,13 @@ iterator events*(parser: var YamlSequentialParser, continue of yamlTagHandle: handleTagHandle() - state = ylBlockAfterTag + state = ylBlockAfterAnchorAndTag of yamlVerbatimTag: tag = lex.content - state = ylBlockAfterTag + state = ylBlockAfterAnchorAndTag level.indentationColumn = lex.column else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() of ylBlockAfterAnchorAndTag: case token of lexer.yamlScalar: @@ -541,7 +611,7 @@ iterator events*(parser: var YamlSequentialParser, state = ylFlow continue else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() of ylBlockAfterColon: case token of lexer.yamlScalar: @@ -574,9 +644,20 @@ iterator events*(parser: var YamlSequentialParser, of yamlAnchor: anchor = lex.content state = ylBlockAfterAnchor + of lexer.yamlAlias: + var noAnchor = false + try: + aliasCache = parser.anchors[lex.content] + except KeyError: + noAnchor = true + if noAnchor: + # cannot use yield within try/except, so do it here + yieldError("[alias] Unknown anchor: " & lex.content) + yield YamlParserEvent(kind: yamlAlias, aliasTarget: aliasCache) + level = ancestry.pop() + state = ylBlockLineEnd else: - yieldError("Unexpected token (expected scalar or line end): " & - $token) + yieldUnexpectedToken("scalar or line end") of ylBlockLineEnd: case token of yamlLineStart: @@ -587,7 +668,7 @@ iterator events*(parser: var YamlSequentialParser, yield YamlParserEvent(kind: yamlEndDocument) break else: - yieldError("Unexpected token (expected line end):" & $token) + yieldUnexpectedToken("line end") of ylBlockScalarHeader: case token of yamlPlus: @@ -609,7 +690,7 @@ iterator events*(parser: var YamlSequentialParser, blockScalarTrailing = "" state = ylBlockScalar else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() of ylBlockScalar: case token of yamlLineStart: @@ -683,9 +764,7 @@ iterator events*(parser: var YamlSequentialParser, level = DocumentLevel(mode: mUnknown, indicatorColumn: -1, indentationColumn: -1) else: - yieldError( - "Unexpected token (expected scalar, comma or " & - " map end): " & $token) + yieldUnexpectedToken("scalar, comma or map end") of yamlComma: yieldScalar() level = ancestry.pop() @@ -701,7 +780,7 @@ iterator events*(parser: var YamlSequentialParser, yieldError("Internal error! Please report this bug.") of yamlOpeningBrace: if level.mode != mUnknown: - yieldError("Unexpected token") + yieldUnexpectedToken() level.mode = mFlowMapKey yieldStart(yamlStartMap) ancestry.add(level) @@ -709,7 +788,7 @@ iterator events*(parser: var YamlSequentialParser, indentationColumn: -1) of yamlOpeningBracket: if level.mode != mUnknown: - yieldError("Unexpected token") + yieldUnexpectedToken() level.mode = mFlowSequenceItem yieldStart(yamlStartSequence) ancestry.add(level) @@ -720,7 +799,7 @@ iterator events*(parser: var YamlSequentialParser, yieldScalar() level = ancestry.pop() if level.mode != mFlowMapValue: - yieldError("Unexpected token") + yieldUnexpectedToken() yield YamlParserEvent(kind: yamlEndMap) if ancestry.len > 0: level = ancestry.pop() @@ -736,7 +815,7 @@ iterator events*(parser: var YamlSequentialParser, yieldScalar() level = ancestry.pop() if level.mode != mFlowSequenceItem: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() else: yield YamlParserEvent(kind: yamlEndSequence) if ancestry.len > 0: @@ -754,8 +833,13 @@ iterator events*(parser: var YamlSequentialParser, of yamlAnchor: anchor = lex.content state = ylFlowAfterAnchor + of lexer.yamlAlias: + yield YamlParserEvent(kind: yamlAlias, + aliasTarget: resolveAlias(parser, lex.content)) + state = ylFlowAfterObject + level = ancestry.pop() else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() of ylFlowAfterTag: case token of yamlTagHandle: @@ -791,7 +875,7 @@ iterator events*(parser: var YamlSequentialParser, discard of yamlColon: if level.mode != mFlowMapKey: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() else: level.mode = mFlowMapValue ancestry.add(level) @@ -812,37 +896,38 @@ iterator events*(parser: var YamlSequentialParser, indentationColumn: -1) state = ylFlow else: - yieldError("Unexpected token: " & $token) + echo "level.mode = ", level.mode + yieldUnexpectedToken() of yamlClosingBrace: if level.mode != mFlowMapValue: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() else: yield YamlParserEvent(kind: yamlEndMap) if ancestry.len > 0: level = ancestry.pop() case level.mode of mFlowMapKey, mFlowMapValue, mFlowSequenceItem: - state = ylFlow + state = ylFlowAfterObject else: state = ylBlockLineEnd else: state = ylExpectingDocumentEnd of yamlClosingBracket: if level.mode != mFlowSequenceItem: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() else: yield YamlParserEvent(kind: yamlEndSequence) if ancestry.len > 0: level = ancestry.pop() case level.mode of mFlowMapKey, mFlowMapValue, mFlowSequenceItem: - state = ylFlow + state = ylFlowAfterObject else: state = ylBlockLineEnd else: state = ylExpectingDocumentEnd else: - yieldError("Unexpected token: " & $token) + yieldUnexpectedToken() of ylExpectingDocumentEnd: case token of yamlComment, yamlLineStart: @@ -855,6 +940,5 @@ iterator events*(parser: var YamlSequentialParser, state = ylInitial continue else: - yieldError("Unexpected token (expected document end): " & - $token) + yieldUnexpectedToken("document end") token = nextToken(lex) \ No newline at end of file diff --git a/test/parsing.nim b/test/parsing.nim index 430f644..3a6f40f 100644 --- a/test/parsing.nim +++ b/test/parsing.nim @@ -11,15 +11,16 @@ proc endDoc(): YamlParserEvent = new(result) result.kind = yamlEndDocument -proc scalar(content: string, tag: TagId = tagNonSpecificQmark, - anchor: string = nil): YamlParserEvent = +proc scalar(content: string, tag: TagId = tagQuestionMark, + anchor: AnchorId = anchorNone): YamlParserEvent = new(result) result.kind = yamlScalar result.scalarAnchor = anchor result.scalarTag = tag result.scalarContent = content -proc startSequence(tag: TagId = tagNonSpecificQmark, anchor: string = nil): +proc startSequence(tag: TagId = tagQuestionMark, + anchor: AnchorId = anchorNone): YamlParserEvent = new(result) result.kind = yamlStartSequence @@ -30,7 +31,7 @@ proc endSequence(): YamlParserEvent = new(result) result.kind = yamlEndSequence -proc startMap(tag: TagId = tagNonSpecificQmark, anchor: string = nil): +proc startMap(tag: TagId = tagQuestionMark, anchor: AnchorId = anchorNone): YamlParserEvent = new(result) result.kind = yamlStartMap @@ -41,6 +42,11 @@ proc endMap(): YamlParserEvent = new(result) result.kind = yamlEndMap +proc alias(target: AnchorId): YamlParserEvent = + new(result) + result.kind = yamlAlias + result.aliasTarget = target + proc printDifference(expected, actual: YamlParserEvent) = if expected.kind != actual.kind: echo "expected " & $expected.kind & ", got " & $actual.kind @@ -56,8 +62,8 @@ proc printDifference(expected, actual: YamlParserEvent) = echo "[\"", actual.scalarContent, "\".tag] expected tag ", expected.scalarTag, ", got ", actual.scalarTag elif expected.scalarAnchor != actual.scalarAnchor: - echo "[scalar] expected anchor " & expected.scalarAnchor & - ", got " & actual.scalarAnchor + echo "[scalar] expected anchor ", expected.scalarAnchor, + ", got ", actual.scalarAnchor elif expected.scalarContent != actual.scalarContent: let msg = "[scalar] expected content \"" & expected.scalarContent & "\", got \"" & @@ -77,8 +83,16 @@ proc printDifference(expected, actual: YamlParserEvent) = echo "[scalar] Unknown difference" of yamlStartMap, yamlStartSequence: if expected.objTag != actual.objTag: - echo "[object.tag] expected ", expected.objTag, ", got", + echo "[object.tag] expected ", expected.objTag, ", got ", actual.objTag + else: + echo "[object.tag] Unknown difference" + of yamlAlias: + if expected.aliasTarget != actual.aliasTarget: + echo "[alias] expected ", expected.aliasTarget, ", got ", + actual.aliasTarget + else: + echo "[alias] Unknown difference" else: echo "Unknown difference in event kind " & $expected.kind @@ -138,9 +152,10 @@ suite "Parsing": startSequence(), scalar("b"), scalar("c"), endSequence(), endSequence(), endDoc()) test "Parsing: Flow Sequence in Flow Map": - ensure("{a: [b, c]}", startDoc(), startMap(), scalar("a"), + ensure("{a: [b, c], [d, e]: f}", startDoc(), startMap(), scalar("a"), startSequence(), scalar("b"), scalar("c"), endSequence(), - endMap(), endDoc()) + startSequence(), scalar("d"), scalar("e"), endSequence(), + scalar("f"), endMap(), endDoc()) test "Parsing: Flow Sequence in Map": ensure("a: [b, c]", startDoc(), startMap(), scalar("a"), startSequence(), scalar("b"), scalar("c"), endSequence(), @@ -168,9 +183,9 @@ suite "Parsing": ensure("a: |-\x0A ab\x0A \x0A \x0A", startDoc(), startMap(), scalar("a"), scalar("ab"), endMap(), endDoc()) test "Parsing: non-specific tags of quoted strings": - ensure("\"a\"", startDoc(), scalar("a", tagNonSpecificEmark), endDoc()) + ensure("\"a\"", startDoc(), scalar("a", tagExclamationMark), endDoc()) test "Parsing: explicit non-specific tag": - ensure("! a", startDoc(), scalar("a", tagNonSpecificEmark), endDoc()) + ensure("! a", startDoc(), scalar("a", tagExclamationMark), endDoc()) test "Parsing: secondary tag handle resolution": let id = parser.registerUri("tag:yaml.org,2002:str") ensure("!!str a", startDoc(), scalar("a", id), endDoc()) @@ -207,4 +222,45 @@ suite "Parsing": idSeq = parser.registerUri("tag:yaml.org,2002:seq") ensure("!!map { k: !!seq [ a, !!str b] }", startDoc(), startMap(idMap), scalar("k"), startSequence(idSeq), scalar("a"), - scalar("b", idStr), endSequence(), endMap(), endDoc()) \ No newline at end of file + scalar("b", idStr), endSequence(), endMap(), endDoc()) + test "Parsing: Simple Anchor": + ensure("&a str", startDoc(), scalar("str", tagQuestionMark, + 0.AnchorId), endDoc()) + test "Parsing: Anchors in sequence": + ensure(" - &a a\n - b\n - &c c\n - &a d", startDoc(), startSequence(), + scalar("a", tagQuestionMark, 0.AnchorId), scalar("b"), + scalar("c", tagQuestionMark, 1.AnchorId), + scalar("d", tagQuestionMark, 0.AnchorId), endSequence(), + endDoc()) + test "Parsing: Anchors in map": + ensure("&a a: b\nc: &d d", startDoc(), startMap(), + scalar("a", tagQuestionMark, 0.AnchorId), + scalar("b"), scalar("c"), + scalar("d", tagQuestionMark, 1.AnchorId), + endMap(), endDoc()) + test "Parsing: Anchors and tags": + let + idStr = parser.registerUri("tag:yaml.org,2002:str") + idInt = parser.registerUri("tag:yaml.org,2002:int") + ensure(" - &a !!str a\n - !!int b\n - &c !!int c\n - &d d", startDoc(), + startSequence(), scalar("a", idStr, 0.AnchorId), + scalar("b", idInt), scalar("c", idInt, 1.AnchorId), + scalar("d", tagQuestionMark, 2.AnchorId), endSequence(), + endDoc()) + test "Parsing: Aliases in sequence": + ensure(" - &a a\n - &b b\n - *a\n - *b", startDoc(), startSequence(), + scalar("a", tagQuestionMark, 0.AnchorId), + scalar("b", tagQuestionMark, 1.AnchorId), alias(0.AnchorId), + alias(1.AnchorId), endSequence(), endDoc()) + test "Parsing: Aliases in map": + ensure("&a a: &b b\n*a: *b", startDoc(), startMap(), + scalar("a", tagQuestionMark, 0.AnchorId), + scalar("b", tagQuestionMark, 1.AnchorId), alias(0.AnchorId), + alias(1.AnchorId), endMap(), endDoc()) + test "Parsing: Aliases in flow": + ensure("{ &a [a, &b b]: *b, *a: [c, *b, d]}", startDoc(), startMap(), + startSequence(tagQuestionMark, 0.AnchorId), scalar("a"), + scalar("b", tagQuestionMark, 1.AnchorId), endSequence(), + alias(1.AnchorId), alias(0.AnchorId), startSequence(), + scalar("c"), alias(1.AnchorId), scalar("d"), endSequence(), + endMap(), endDoc()) \ No newline at end of file