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
This commit is contained in:
Felix Krause 2015-12-22 16:28:35 +01:00
parent e254d0f282
commit f249813b90
3 changed files with 257 additions and 96 deletions

View File

@ -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)

View File

@ -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)

View File

@ -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())
@ -208,3 +223,44 @@ suite "Parsing":
ensure("!!map { k: !!seq [ a, !!str b] }", startDoc(), startMap(idMap),
scalar("k"), startSequence(idSeq), scalar("a"),
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())