fastparse: made it pass the parsing tests

* Scan for implicit map key as soon as a property is encountered
 * Proper handling of node properties throughout parser
 * Fixed an issue where plain scalars sometimes contained garbage
   when yielded
 * Catch reserved chars @ and `
 * Fixed some test cases
 * Made parser test cases use fastparse
This commit is contained in:
Felix Krause 2016-01-23 17:44:50 +01:00
parent 7f8afef3d4
commit 971218314e
2 changed files with 241 additions and 283 deletions

View File

@ -63,10 +63,13 @@ template yieldLevelEnd() {.dirty.} =
anchor = yAnchorNone
yield endMapEvent()
of fplScalar:
applyObjectProperties()
yield scalarEvent(content, tag, anchor)
tag = yTagQuestionMark
anchor = yAnchorNone
of fplUnknown:
yield scalarEvent("")
yield scalarEvent("", tag, anchor)
tag = yTagQuestionMark
anchor = yAnchorNone
template handleLineEnd(insideDocument: bool) {.dirty.} =
case lexer.buf[lexer.bufpos]
@ -99,31 +102,24 @@ template handleObjectEnd(nextState: FastParseState) {.dirty.} =
of fplUnknown, fplScalar:
assert(false)
template handleObjectStart(k: YamlStreamEventKind, isFlow: bool) {.dirty.} =
template handleObjectStart(k: YamlStreamEventKind) {.dirty.} =
assert(level.kind == fplUnknown)
when k == yamlStartMap:
when isFlow:
yield startMapEvent(tag, anchor)
else:
yield startMapEvent(objectTag, objectAnchor)
debug("started map at " & $indentation)
yield startMapEvent(tag, anchor)
debug("started map at " & (if level.indentation == -1: $indentation else:
$level.indentation))
level.kind = fplMapKey
else:
when isFlow:
yield startSeqEvent(tag, anchor)
else:
yield startSeqEvent(objectTag, objectAnchor)
debug("started sequence at " & $indentation)
yield startSeqEvent(tag, anchor)
debug("started sequence at " & (if level.indentation == -1: $indentation else:
$level.indentation))
level.kind = fplSequence
when isFlow:
tag = yTagQuestionmark
anchor = yAnchorNone
else:
objectTag = yTagQuestionmark
objectAnchor = yAnchorNone
level.indentation = indentation
tag = yTagQuestionmark
anchor = yAnchorNone
if level.indentation == -1:
level.indentation = indentation
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
template closeMoreIndentedLevels() {.dirty.} =
while ancestry.len > 0:
@ -145,23 +141,22 @@ template closeEverything() {.dirty.} =
template handleBlockSequenceIndicator() {.dirty.} =
case level.kind
of fplUnknown:
handleObjectStart(yamlStartSequence, false)
handleObjectStart(yamlStartSequence)
of fplSequence:
if level.indentation != indentation:
raiseError("Invalid indentation of block sequence indicator",
lexer.bufpos)
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
else:
raiseError("Illegal sequence item in map")
lexer.skipWhitespace()
indentation = lexer.getColNumber(lexer.bufpos)
level.indentation = indentation
template handleMapKeyIndicator() {.dirty.} =
case level.kind
of fplUnknown:
handleObjectStart(yamlStartMap, false)
handleObjectStart(yamlStartMap)
of fplMapValue:
if level.indentation != indentation:
raiseError("Invalid indentation of map key indicator",
@ -169,26 +164,31 @@ template handleMapKeyIndicator() {.dirty.} =
yield scalarEvent("", yTagQuestionmark, yAnchorNone)
level.kind = fplMapKey
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
of fplMapKey:
if level.indentation != indentation:
raiseError("Invalid indentation of map key indicator",
lexer.bufpos)
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
of fplSequence:
raiseError("Unexpected map key indicator (expected '- ')")
of fplScalar:
raiseError("Unexpected map key indicator (expected multiline scalar end)")
lexer.skipWhitespace()
indentation = lexer.getColNumber(lexer.bufpos)
level.indentation = indentation
template handleMapValueIndicator() {.dirty.} =
case level.kind
of fplUnknown:
handleObjectStart(yamlStartMap, false)
yield scalarEvent("", yTagQuestionmark, yAnchorNone)
if level.indentation == -1:
handleObjectStart(yamlStartMap)
yield scalarEvent("", yTagQuestionmark, yAnchorNone)
else:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
ancestry[ancestry.high].kind = fplMapValue
of fplMapKey:
if level.indentation != indentation:
raiseError("Invalid indentation of map key indicator",
@ -196,32 +196,19 @@ template handleMapValueIndicator() {.dirty.} =
yield scalarEvent("", yTagQuestionmark, yAnchorNone)
level.kind = fplMapValue
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
of fplMapValue:
if level.indentation != indentation:
raiseError("Invalid indentation of map key indicator",
lexer.bufpos)
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
of fplSequence:
raiseError("Unexpected map value indicator (expected '- ')")
of fplScalar:
raiseError("Unexpected map value indicator (expected multiline scalar end)")
lexer.skipWhitespace()
indentation = lexer.getColNumber(lexer.bufpos)
level.indentation = indentation
template propsToObjectProps() {.dirty.} =
if objectTag == yTagQuestionmark:
objectTag = tag
tag = yTagQuestionmark
elif tag != yTagQuestionMark:
raiseError("Only one tag is allowed per node")
if objectAnchor == yAnchorNone:
objectAnchor = anchor
anchor = yAnchorNone
elif anchor != yAnchorNone:
raiseError("Only one anchor is allowed per node")
template initDocValues() {.dirty.} =
shorthands = initTable[string, string]()
@ -229,26 +216,9 @@ template initDocValues() {.dirty.} =
shorthands["!"] = "!"
shorthands["!!"] = "tag:yaml.org,2002:"
nextAnchorId = 0.AnchorId
level = FastParseLevel(kind: fplUnknown, indentation: 0)
level = FastParseLevel(kind: fplUnknown, indentation: -1)
tag = yTagQuestionmark
objectTag = yTagQuestionmark
anchor = yAnchorNone
objectAnchor = yAnchorNone
template applyObjectProperties() {.dirty.} =
if objectTag != yTagQuestionmark:
if tag != yTagQuestionmark:
debug("tag = " & $tag & ", object = " & $objectTag)
raiseError("Only one tag is allowed per node")
else:
tag = objectTag
objectTag = yTagQuestionmark
if objectAnchor != yAnchorNone:
if anchor != yAnchorNone:
raiseError("Only one anchor is allowed per node")
else:
anchor = objectAnchor
objectAnchor = yAnchorNone
template handleTagHandle() {.dirty.} =
if level.kind != fplUnknown:
@ -309,43 +279,47 @@ template leaveFlowLevel() {.dirty.} =
handleObjectEnd(fpFlowAfterObject)
template handlePossibleMapStart() {.dirty.} =
var flowDepth = 0
for p in countup(lexer.bufpos, lexer.bufpos + 1024):
case lexer.buf[p]
of ':':
if flowDepth == 0 and lexer.buf[p + 1] in spaceOrLineEnd:
handleObjectStart(yamlStartMap, false)
if level.indentation == -1:
var flowDepth = 0
for p in countup(lexer.bufpos, lexer.bufpos + 1024):
case lexer.buf[p]
of ':':
if flowDepth == 0 and lexer.buf[p + 1] in spaceOrLineEnd:
handleObjectStart(yamlStartMap)
break
of lineEnd:
break
of lineEnd:
break
of '[', '{':
flowDepth.inc()
of '}', ']':
flowDepth.inc(-1)
of '?':
if flowDepth == 0: break
of '#':
if lexer.buf[p - 1] in space: break
else:
discard
of '[', '{':
flowDepth.inc()
of '}', ']':
flowDepth.inc(-1)
of '?':
if flowDepth == 0: break
of '#':
if lexer.buf[p - 1] in space:
break
else:
discard
if level.indentation == -1:
level.indentation = indentation
template handleBlockItemStart() {.dirty.} =
case level.kind
of fplUnknown:
discard
handlePossibleMapStart()
of fplSequence:
raiseError("Unexpected token (expected block sequence indicator)",
lexer.bufpos)
of fplMapKey:
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: indentation)
of fplMapValue:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
level.kind = fplMapKey
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: indentation)
of fplScalar:
assert(false)
@ -815,10 +789,8 @@ template anchorName(lexer: FastLexer, content: var string) =
lexer.bufpos.inc()
let c = lexer.buf[lexer.bufpos]
case c
of spaceOrLineEnd:
of spaceOrLineEnd, '[', ']', '{', '}', ',':
break
of '[', ']', '{', '}', ',':
raiseError("Illegal character in anchor", lexer.bufpos)
else:
content.add(c)
@ -998,8 +970,8 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
anchors: Table[string, AnchorId]
nextAnchorId: AnchorId
content: string
tag, objectTag: TagId
anchor, objectAnchor: AnchorId
tag: TagId
anchor: AnchorId
ancestry = newSeq[FastParseLevel]()
level: FastParseLevel
indentation: int
@ -1040,12 +1012,30 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
lexer.finishLine()
handleLineEnd(false)
of ' ', '\t':
lexer.bufpos.inc()
while true:
lexer.bufpos.inc()
case lexer.buf[lexer.bufpos]
of ' ', '\t':
discard
of '\x0A':
lexer.bufpos = lexer.handleLF(lexer.bufpos)
break
of '\c':
lexer.bufpos = lexer.handleCR(lexer.bufpos)
break
of '#', EndOfFile:
lexer.lineEnding()
handleLineEnd(false)
break
else:
indentation = lexer.getColNumber(lexer.bufpos)
yield startDocEvent()
state = fpBlockObjectStart
break
of '\x0A':
lexer.bufpos = lexer.handleLF(lexer.bufpos)
of '\c':
lexer.bufpos = lexer.handleCR(lexer.bufpos)
lexer.bufpos.inc()
of EndOfFile:
return
of '#':
@ -1099,7 +1089,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
else:
ensureCorrectIndentation()
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
content = ""
lexer.plainScalar(content, cBlockOut)
state = fpBlockAfterPlainScalar
@ -1124,7 +1114,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
else:
ensureCorrectIndentation()
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
content = ""
lexer.plainScalar(content, cBlockOut)
state = fpBlockAfterPlainScalar
@ -1140,9 +1130,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of fplScalar:
state = fpBlockContinueScalar
of fplUnknown:
handlePossibleMapStart()
state = fpBlockObjectStart
level.indentation = indentation
else:
ensureCorrectIndentation()
state = fpBlockObjectStart
@ -1153,9 +1141,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of fplScalar:
state = fpBlockContinueScalar
of fplUnknown:
handlePossibleMapStart()
state = fpBlockObjectStart
level.indentation = indentation
else:
ensureCorrectIndentation()
state = fpBlockObjectStart
@ -1219,16 +1205,16 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of ':':
case level.kind
of fplUnknown:
handleObjectStart(yamlStartMap, false)
handleObjectStart(yamlStartMap)
of fplMapKey:
yield scalarEvent("", yTagQuestionMark, yAnchorNone)
level.kind = fplMapValue
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
of fplMapValue:
level.kind = fplMapValue
ancestry.add(level)
level.kind = fplUnknown
level = FastParseLevel(kind: fplUnknown, indentation: -1)
of fplSequence:
raiseError("Illegal token (expected sequence item)")
of fplScalar:
@ -1236,10 +1222,8 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
lexer.bufpos.inc()
lexer.skipWhitespace()
indentation = lexer.getColNumber(lexer.bufpos)
level.indentation = indentation
state = fpBlockObjectStart
of '#':
applyObjectProperties()
lexer.lineEnding()
handleLineEnd(true)
handleObjectEnd(fpBlockLineStart)
@ -1253,13 +1237,13 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
let objectStart = lexer.getColNumber(lexer.bufpos)
case lexer.buf[lexer.bufpos]
of '\x0A':
propsToObjectProps()
lexer.bufpos = lexer.handleLF(lexer.bufpos)
state = fpBlockLineStart
level.indentation = -1
of '\c':
propsToObjectProps()
lexer.bufpos = lexer.handleCR(lexer.bufpos)
state = fpBlockLineStart
level.indentation = -1
of EndOfFile:
closeEverything()
return
@ -1283,8 +1267,11 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
yield scalarEvent(content, tag, anchor)
handleObjectEnd(fpBlockAfterObject)
of '|', '>':
# TODO: this will scan for possible map start, which is not
# neccessary in this case
handleBlockItemStart()
var stateAfter: FastParseState
content = ""
lexer.blockScalar(content, stateAfter)
if tag == yTagQuestionmark:
tag = yTagExclamationmark
@ -1293,6 +1280,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of '-':
if lexer.isPlainSafe(lexer.bufpos + 1, cBlockOut):
handleBlockItemStart()
content = ""
lexer.tokenstart = lexer.getColNumber(lexer.bufpos)
lexer.plainScalar(content, cBlockOut)
state = fpBlockAfterPlainScalar
@ -1310,11 +1298,11 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
handleAlias()
of '[', '{':
handleBlockItemStart()
applyObjectProperties()
state = fpFlow
of '?':
if lexer.isPlainSafe(lexer.bufpos + 1, cBlockOut):
handleBlockItemStart()
content = ""
lexer.tokenstart = lexer.getColNumber(lexer.bufpos)
lexer.plainScalar(content, cBlockOut)
state = fpBlockAfterPlainScalar
@ -1324,12 +1312,16 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of ':':
if lexer.isPlainSafe(lexer.bufpos + 1, cBlockOut):
handleBlockItemStart()
content = ""
lexer.tokenstart = lexer.getColNumber(lexer.bufpos)
lexer.plainScalar(content, cBlockOut)
state = fpBlockAfterPlainScalar
else:
lexer.bufpos.inc()
handleMapValueIndicator()
of '@', '`':
raiseError("Reserved characters cannot start a plain scalar",
lexer.bufpos)
else:
handleBlockItemStart()
content = ""
@ -1376,12 +1368,12 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
lexer.skipWhitespaceAndNewlines()
case lexer.buf[lexer.bufpos]
of '{':
handleObjectStart(yamlStartMap, true)
handleObjectStart(yamlStartMap)
flowdepth.inc()
lexer.bufpos.inc()
explicitFlowKey = false
of '[':
handleObjectStart(yamlStartSequence, true)
handleObjectStart(yamlStartSequence)
flowdepth.inc()
lexer.bufpos.inc()
of '}':
@ -1444,7 +1436,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
level = FastParseLevel(kind: fplUnknown, indentation: -1)
lexer.bufpos.inc()
of ':':
assert(level.kind == fplUnknown)
@ -1461,7 +1453,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
level = FastParseLevel(kind: fplUnknown, indentation: -1)
lexer.bufpos.inc()
else:
handleFlowPlainScalar()
@ -1533,7 +1525,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
level = FastParseLevel(kind: fplUnknown, indentation: -1)
state = fpFlow
lexer.bufpos.inc()
of ':':
@ -1545,7 +1537,7 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
level = FastParseLevel(kind: fplUnknown, indentation: -1)
state = fpFlow
lexer.bufpos.inc()
of '#':

View File

@ -2,39 +2,6 @@ import "../yaml"
import unittest
proc startDoc(): YamlStreamEvent =
result.kind = yamlStartDocument
proc endDoc(): YamlStreamEvent =
result.kind = yamlEndDocument
proc scalar(content: string,
tag: TagId = yTagQuestionMark, anchor: AnchorId = yAnchorNone):
YamlStreamEvent = scalarEvent(content, tag, anchor)
proc startSequence(tag: TagId = yTagQuestionMark,
anchor: AnchorId = yAnchorNone):
YamlStreamEvent =
result.kind = yamlStartSequence
result.seqAnchor = anchor
result.seqTag = tag
proc endSequence(): YamlStreamEvent =
result.kind = yamlEndSequence
proc startMap(tag: TagId = yTagQuestionMark, anchor: AnchorId = yAnchorNone):
YamlStreamEvent =
result.kind = yamlStartMap
result.mapAnchor = anchor
result.mapTag = tag
proc endMap(): YamlStreamEvent =
result.kind = yamlEndMap
proc alias(target: AnchorId): YamlStreamEvent =
result.kind = yamlAlias
result.aliasTarget = target
proc printDifference(expected, actual: YamlStreamEvent) =
if expected.kind != actual.kind:
echo "expected " & $expected.kind & ", got " & $actual.kind
@ -45,10 +12,10 @@ proc printDifference(expected, actual: YamlStreamEvent) =
echo "[\"", actual.scalarContent, "\".tag] expected tag ",
expected.scalarTag, ", got ", actual.scalarTag
elif expected.scalarAnchor != actual.scalarAnchor:
echo "[scalar] expected anchor ", expected.scalarAnchor,
echo "[scalarEvent] expected anchor ", expected.scalarAnchor,
", got ", actual.scalarAnchor
elif expected.scalarContent != actual.scalarContent:
let msg = "[scalar] expected content \"" &
let msg = "[scalarEvent] expected content \"" &
expected.scalarContent & "\", got \"" &
actual.scalarContent & "\" "
if expected.scalarContent.len != actual.scalarContent.len:
@ -63,7 +30,7 @@ proc printDifference(expected, actual: YamlStreamEvent) =
cast[int](actual.scalarContent[i]), ")"
break
else:
echo "[scalar] Unknown difference"
echo "[scalarEvent] Unknown difference"
of yamlStartMap:
if expected.mapTag != actual.mapTag:
echo "[map.tag] expected ", expected.mapTag, ", got ",
@ -85,9 +52,8 @@ proc printDifference(expected, actual: YamlStreamEvent) =
template ensure(input: string, expected: varargs[YamlStreamEvent]) {.dirty.} =
var
parser = newParser(tagLib)
i = 0
events = parser.parse(newStringStream(input))
events = fastparse(tagLib, newStringStream(input))
try:
for token in events():
if i >= expected.len:
@ -113,183 +79,183 @@ suite "Parsing":
teardown:
discard
test "Parsing: Simple Scalar":
ensure("Scalar", startDoc(), scalar("Scalar"), endDoc())
test "Parsing: Simple scalarEvent":
ensure("scalarEvent", startDocEvent(), scalarEvent("scalarEvent"), endDocEvent())
test "Parsing: Simple Sequence":
ensure("- off", startDoc(), startSequence(),
scalar("off"), endSequence(), endDoc())
ensure("- off", startDocEvent(), startSeqEvent(),
scalarEvent("off"), endSeqEvent(), endDocEvent())
test "Parsing: Simple Map":
ensure("42: value\nkey2: -7.5", startDoc(), startMap(),
scalar("42"), scalar("value"), scalar("key2"),
scalar("-7.5"), endMap(), endDoc())
ensure("42: value\nkey2: -7.5", startDocEvent(), startMapEvent(),
scalarEvent("42"), scalarEvent("value"), scalarEvent("key2"),
scalarEvent("-7.5"), endMapEvent(), endDocEvent())
test "Parsing: Explicit Map":
ensure("? null\n: value\n? ON\n: value2", startDoc(), startMap(),
scalar("null"), scalar("value"),
scalar("ON"), scalar("value2"),
endMap(), endDoc())
ensure("? null\n: value\n? ON\n: value2", startDocEvent(), startMapEvent(),
scalarEvent("null"), scalarEvent("value"),
scalarEvent("ON"), scalarEvent("value2"),
endMapEvent(), endDocEvent())
test "Parsing: Mixed Map (explicit to implicit)":
ensure("? a\n: 13\n1.5: d", startDoc(), startMap(), scalar("a"),
scalar("13"), scalar("1.5"),
scalar("d"), endMap(), endDoc())
ensure("? a\n: 13\n1.5: d", startDocEvent(), startMapEvent(), scalarEvent("a"),
scalarEvent("13"), scalarEvent("1.5"),
scalarEvent("d"), endMapEvent(), endDocEvent())
test "Parsing: Mixed Map (implicit to explicit)":
ensure("a: 4.2\n? 23\n: d", startDoc(), startMap(), scalar("a"),
scalar("4.2"), scalar("23"),
scalar("d"), endMap(), endDoc())
ensure("a: 4.2\n? 23\n: d", startDocEvent(), startMapEvent(), scalarEvent("a"),
scalarEvent("4.2"), scalarEvent("23"),
scalarEvent("d"), endMapEvent(), endDocEvent())
test "Parsing: Missing values in map":
ensure("? a\n? b\nc:", startDoc(), startMap(), scalar("a"), scalar(""),
scalar("b"), scalar(""), scalar("c"), scalar(""), endMap(),
endDoc())
ensure("? a\n? b\nc:", startDocEvent(), startMapEvent(), scalarEvent("a"), scalarEvent(""),
scalarEvent("b"), scalarEvent(""), scalarEvent("c"), scalarEvent(""), endMapEvent(),
endDocEvent())
test "Parsing: Missing keys in map":
ensure(": a\n: b", startDoc(), startMap(), scalar(""), scalar("a"),
scalar(""), scalar("b"), endMap(), endDoc())
test "Parsing: Multiline scalars in explicit map":
ensure("? a\n true\n: null\n d\n? e\n 42", startDoc(), startMap(),
scalar("a true"), scalar("null d"), scalar("e 42"), scalar(""),
endMap(), endDoc())
ensure(": a\n: b", startDocEvent(), startMapEvent(), scalarEvent(""), scalarEvent("a"),
scalarEvent(""), scalarEvent("b"), endMapEvent(), endDocEvent())
test "Parsing: Multiline scalarEvents in explicit map":
ensure("? a\n true\n: null\n d\n? e\n 42", startDocEvent(), startMapEvent(),
scalarEvent("a true"), scalarEvent("null d"), scalarEvent("e 42"), scalarEvent(""),
endMapEvent(), endDocEvent())
test "Parsing: Map in Sequence":
ensure(" - key: value\n key2: value2\n -\n key3: value3",
startDoc(), startSequence(), startMap(), scalar("key"),
scalar("value"), scalar("key2"), scalar("value2"), endMap(),
startMap(), scalar("key3"), scalar("value3"), endMap(),
endSequence(), endDoc())
startDocEvent(), startSeqEvent(), startMapEvent(), scalarEvent("key"),
scalarEvent("value"), scalarEvent("key2"), scalarEvent("value2"), endMapEvent(),
startMapEvent(), scalarEvent("key3"), scalarEvent("value3"), endMapEvent(),
endSeqEvent(), endDocEvent())
test "Parsing: Sequence in Map":
ensure("key:\n - item1\n - item2", startDoc(), startMap(),
scalar("key"), startSequence(), scalar("item1"), scalar("item2"),
endSequence(), endMap(), endDoc())
ensure("key:\n - item1\n - item2", startDocEvent(), startMapEvent(),
scalarEvent("key"), startSeqEvent(), scalarEvent("item1"), scalarEvent("item2"),
endSeqEvent(), endMapEvent(), endDocEvent())
test "Parsing: Sequence in Sequence":
ensure("- - l1_i1\n - l1_i2\n- l2_i1", startDoc(), startSequence(),
startSequence(), scalar("l1_i1"), scalar("l1_i2"), endSequence(),
scalar("l2_i1"), endSequence(), endDoc())
ensure("- - l1_i1\n - l1_i2\n- l2_i1", startDocEvent(), startSeqEvent(),
startSeqEvent(), scalarEvent("l1_i1"), scalarEvent("l1_i2"), endSeqEvent(),
scalarEvent("l2_i1"), endSeqEvent(), endDocEvent())
test "Parsing: Flow Sequence":
ensure("[2, b]", startDoc(), startSequence(), scalar("2"),
scalar("b"), endSequence(), endDoc())
ensure("[2, b]", startDocEvent(), startSeqEvent(), scalarEvent("2"),
scalarEvent("b"), endSeqEvent(), endDocEvent())
test "Parsing: Flow Map":
ensure("{a: Y, 1.337: d}", startDoc(), startMap(), scalar("a"),
scalar("Y"), scalar("1.337"),
scalar("d"), endMap(), endDoc())
ensure("{a: Y, 1.337: d}", startDocEvent(), startMapEvent(), scalarEvent("a"),
scalarEvent("Y"), scalarEvent("1.337"),
scalarEvent("d"), endMapEvent(), endDocEvent())
test "Parsing: Flow Sequence in Flow Sequence":
ensure("[a, [b, c]]", startDoc(), startSequence(), scalar("a"),
startSequence(), scalar("b"), scalar("c"), endSequence(),
endSequence(), endDoc())
ensure("[a, [b, c]]", startDocEvent(), startSeqEvent(), scalarEvent("a"),
startSeqEvent(), scalarEvent("b"), scalarEvent("c"), endSeqEvent(),
endSeqEvent(), endDocEvent())
test "Parsing: Flow Sequence in Flow Map":
ensure("{a: [b, c], [d, e]: f}", startDoc(), startMap(), scalar("a"),
startSequence(), scalar("b"), scalar("c"), endSequence(),
startSequence(), scalar("d"), scalar("e"), endSequence(),
scalar("f"), endMap(), endDoc())
ensure("{a: [b, c], [d, e]: f}", startDocEvent(), startMapEvent(), scalarEvent("a"),
startSeqEvent(), scalarEvent("b"), scalarEvent("c"), endSeqEvent(),
startSeqEvent(), scalarEvent("d"), scalarEvent("e"), endSeqEvent(),
scalarEvent("f"), endMapEvent(), endDocEvent())
test "Parsing: Flow Sequence in Map":
ensure("a: [b, c]", startDoc(), startMap(), scalar("a"),
startSequence(), scalar("b"), scalar("c"), endSequence(),
endMap(), endDoc())
ensure("a: [b, c]", startDocEvent(), startMapEvent(), scalarEvent("a"),
startSeqEvent(), scalarEvent("b"), scalarEvent("c"), endSeqEvent(),
endMapEvent(), endDocEvent())
test "Parsing: Flow Map in Sequence":
ensure("- {a: b}", startDoc(), startSequence(), startMap(), scalar("a"),
scalar("b"), endMap(), endSequence(), endDoc())
ensure("- {a: b}", startDocEvent(), startSeqEvent(), startMapEvent(), scalarEvent("a"),
scalarEvent("b"), endMapEvent(), endSeqEvent(), endDocEvent())
test "Parsing: Multiline scalar (top level)":
ensure("a\nb \n c\nd", startDoc(), scalar("a b c d"), endDoc())
ensure("a\nb \n c\nd", startDocEvent(), scalarEvent("a b c d"), endDocEvent())
test "Parsing: Multiline scalar (in map)":
ensure("a: b\n c\nd:\n e\n f", startDoc(), startMap(), scalar("a"),
scalar("b c"), scalar("d"), scalar("e f"), endMap(), endDoc())
ensure("a: b\n c\nd:\n e\n f", startDocEvent(), startMapEvent(), scalarEvent("a"),
scalarEvent("b c"), scalarEvent("d"), scalarEvent("e f"), endMapEvent(), endDocEvent())
test "Parsing: Block scalar (literal)":
ensure("a: |\x0A ab\x0A \x0A cd\x0A ef\x0A \x0A", startDoc(),
startMap(), scalar("a"), scalar("ab\x0A\x0Acd\x0Aef\x0A"),
endMap(), endDoc())
ensure("a: |\x0A ab\x0A \x0A cd\x0A ef\x0A \x0A", startDocEvent(),
startMapEvent(), scalarEvent("a"), scalarEvent("ab\x0A\x0Acd\x0Aef\x0A", yTagExclamationmark),
endMapEvent(), endDocEvent())
test "Parsing: Block scalar (folded)":
ensure("a: >\x0A ab\x0A cd\x0A \x0Aef\x0A\x0A\x0Agh\x0A", startDoc(),
startMap(), scalar("a"), scalar("ab cd\x0Aef\x0Agh\x0A"),
endMap(), endDoc())
ensure("a: >\x0A ab\x0A cd\x0A \x0A ef\x0A\x0A\x0A gh\x0A", startDocEvent(),
startMapEvent(), scalarEvent("a"), scalarEvent("ab cd\x0Aef\x0A\x0Agh\x0A", yTagExclamationmark),
endMapEvent(), endDocEvent())
test "Parsing: Block scalar (keep)":
ensure("a: |+\x0A ab\x0A \x0A \x0A", startDoc(), startMap(),
scalar("a"), scalar("ab\x0A\x0A \x0A"), endMap(), endDoc())
ensure("a: |+\x0A ab\x0A \x0A \x0A", startDocEvent(), startMapEvent(),
scalarEvent("a"), scalarEvent("ab\x0A\x0A \x0A", yTagExclamationmark), endMapEvent(), endDocEvent())
test "Parsing: Block scalar (strip)":
ensure("a: |-\x0A ab\x0A \x0A \x0A", startDoc(), startMap(),
scalar("a"), scalar("ab"), endMap(), endDoc())
ensure("a: |-\x0A ab\x0A \x0A \x0A", startDocEvent(), startMapEvent(),
scalarEvent("a"), scalarEvent("ab", yTagExclamationmark), endMapEvent(), endDocEvent())
test "Parsing: non-specific tags of quoted strings":
ensure("\"a\"", startDoc(),
scalar("a", yTagExclamationMark), endDoc())
ensure("\"a\"", startDocEvent(),
scalarEvent("a", yTagExclamationMark), endDocEvent())
test "Parsing: explicit non-specific tag":
ensure("! a", startDoc(), scalar("a", yTagExclamationMark), endDoc())
ensure("! a", startDocEvent(), scalarEvent("a", yTagExclamationMark), endDocEvent())
test "Parsing: secondary tag handle resolution":
ensure("!!str a", startDoc(), scalar("a", yTagString), endDoc())
ensure("!!str a", startDocEvent(), scalarEvent("a", yTagString), endDocEvent())
test "Parsing: resolving custom tag handles":
let fooId = tagLib.registerUri("tag:example.com,2015:foo")
ensure("%TAG !t! tag:example.com,2015:\n---\n!t!foo a", startDoc(),
scalar("a", fooId), endDoc())
ensure("%TAG !t! tag:example.com,2015:\n---\n!t!foo a", startDocEvent(),
scalarEvent("a", fooId), endDocEvent())
test "Parsing: tags in sequence":
ensure(" - !!str a\n - b\n - !!int c\n - d", startDoc(),
startSequence(), scalar("a", yTagString), scalar("b"),
scalar("c", yTagInteger), scalar("d"), endSequence(), endDoc())
ensure(" - !!str a\n - b\n - !!int c\n - d", startDocEvent(),
startSeqEvent(), scalarEvent("a", yTagString), scalarEvent("b"),
scalarEvent("c", yTagInteger), scalarEvent("d"), endSeqEvent(), endDocEvent())
test "Parsing: tags in implicit map":
ensure("!!str a: b\nc: !!int d\ne: !!str f\ng: h", startDoc(), startMap(),
scalar("a", yTagString), scalar("b"), scalar("c"),
scalar("d", yTagInteger), scalar("e"), scalar("f", yTagString),
scalar("g"), scalar("h"), endMap(), endDoc())
ensure("!!str a: b\nc: !!int d\ne: !!str f\ng: h", startDocEvent(), startMapEvent(),
scalarEvent("a", yTagString), scalarEvent("b"), scalarEvent("c"),
scalarEvent("d", yTagInteger), scalarEvent("e"), scalarEvent("f", yTagString),
scalarEvent("g"), scalarEvent("h"), endMapEvent(), endDocEvent())
test "Parsing: tags in explicit map":
ensure("? !!str a\n: !!int b\n? c\n: !!str d", startDoc(), startMap(),
scalar("a", yTagString), scalar("b", yTagInteger), scalar("c"),
scalar("d", yTagString), endMap(), endDoc())
ensure("? !!str a\n: !!int b\n? c\n: !!str d", startDocEvent(), startMapEvent(),
scalarEvent("a", yTagString), scalarEvent("b", yTagInteger), scalarEvent("c"),
scalarEvent("d", yTagString), endMapEvent(), endDocEvent())
test "Parsing: tags for block objects":
ensure("--- !!map\nfoo: !!seq\n - a\n - !!str b\n!!str bar: !!str baz",
startDoc(), startMap(yTagMap), scalar("foo"),
startSequence(yTagSequence), scalar("a"), scalar("b", yTagString),
endSequence(), scalar("bar", yTagString),
scalar("baz", yTagString), endMap(), endDoc())
startDocEvent(), startMapEvent(yTagMap), scalarEvent("foo"),
startSeqEvent(yTagSequence), scalarEvent("a"), scalarEvent("b", yTagString),
endSeqEvent(), scalarEvent("bar", yTagString),
scalarEvent("baz", yTagString), endMapEvent(), endDocEvent())
test "Parsing: root tag for block sequence":
ensure("--- !!seq\n- a", startDoc(), startSequence(yTagSequence),
scalar("a"), endSequence(), endDoc())
ensure("--- !!seq\n- a", startDocEvent(), startSeqEvent(yTagSequence),
scalarEvent("a"), endSeqEvent(), endDocEvent())
test "Parsing: root tag for explicit block map":
ensure("--- !!map\n? a\n: b", startDoc(), startMap(yTagMap),
scalar("a"), scalar("b"), endMap(), endDoc())
ensure("--- !!map\n? a\n: b", startDocEvent(), startMapEvent(yTagMap),
scalarEvent("a"), scalarEvent("b"), endMapEvent(), endDocEvent())
test "Parsing: tags for flow objects":
ensure("!!map { k: !!seq [ a, !!str b] }", startDoc(), startMap(yTagMap),
scalar("k"), startSequence(yTagSequence), scalar("a"),
scalar("b", yTagString), endSequence(), endMap(), endDoc())
ensure("!!map { k: !!seq [ a, !!str b] }", startDocEvent(), startMapEvent(yTagMap),
scalarEvent("k"), startSeqEvent(yTagSequence), scalarEvent("a"),
scalarEvent("b", yTagString), endSeqEvent(), endMapEvent(), endDocEvent())
test "Parsing: Tag after directives end":
ensure("--- !!str\nfoo", startDoc(), scalar("foo", yTagString), endDoc())
ensure("--- !!str\nfoo", startDocEvent(), scalarEvent("foo", yTagString), endDocEvent())
test "Parsing: Simple Anchor":
ensure("&a str", startDoc(), scalar("str", yTagQuestionMark,
0.AnchorId), endDoc())
ensure("&a str", startDocEvent(), scalarEvent("str", yTagQuestionMark,
0.AnchorId), endDocEvent())
test "Parsing: Anchors in sequence":
ensure(" - &a a\n - b\n - &c c\n - &a d", startDoc(), startSequence(),
scalar("a", yTagQuestionMark, 0.AnchorId), scalar("b"),
scalar("c", yTagQuestionMark, 1.AnchorId),
scalar("d", yTagQuestionMark, 0.AnchorId), endSequence(),
endDoc())
ensure(" - &a a\n - b\n - &c c\n - &a d", startDocEvent(), startSeqEvent(),
scalarEvent("a", yTagQuestionMark, 0.AnchorId), scalarEvent("b"),
scalarEvent("c", yTagQuestionMark, 1.AnchorId),
scalarEvent("d", yTagQuestionMark, 2.AnchorId), endSeqEvent(),
endDocEvent())
test "Parsing: Anchors in map":
ensure("&a a: b\nc: &d d", startDoc(), startMap(),
scalar("a", yTagQuestionMark, 0.AnchorId),
scalar("b"), scalar("c"),
scalar("d", yTagQuestionMark, 1.AnchorId),
endMap(), endDoc())
ensure("&a a: b\nc: &d d", startDocEvent(), startMapEvent(),
scalarEvent("a", yTagQuestionMark, 0.AnchorId),
scalarEvent("b"), scalarEvent("c"),
scalarEvent("d", yTagQuestionMark, 1.AnchorId),
endMapEvent(), endDocEvent())
test "Parsing: Anchors and tags":
ensure(" - &a !!str a\n - !!int b\n - &c !!int c\n - &d d", startDoc(),
startSequence(), scalar("a", yTagString, 0.AnchorId),
scalar("b", yTagInteger), scalar("c", yTagInteger, 1.AnchorId),
scalar("d", yTagQuestionMark, 2.AnchorId), endSequence(),
endDoc())
ensure(" - &a !!str a\n - !!int b\n - &c !!int c\n - &d d", startDocEvent(),
startSeqEvent(), scalarEvent("a", yTagString, 0.AnchorId),
scalarEvent("b", yTagInteger), scalarEvent("c", yTagInteger, 1.AnchorId),
scalarEvent("d", yTagQuestionMark, 2.AnchorId), endSeqEvent(),
endDocEvent())
test "Parsing: Aliases in sequence":
ensure(" - &a a\n - &b b\n - *a\n - *b", startDoc(), startSequence(),
scalar("a", yTagQuestionMark, 0.AnchorId),
scalar("b", yTagQuestionMark, 1.AnchorId), alias(0.AnchorId),
alias(1.AnchorId), endSequence(), endDoc())
ensure(" - &a a\n - &b b\n - *a\n - *b", startDocEvent(), startSeqEvent(),
scalarEvent("a", yTagQuestionMark, 0.AnchorId),
scalarEvent("b", yTagQuestionMark, 1.AnchorId), aliasEvent(0.AnchorId),
aliasEvent(1.AnchorId), endSeqEvent(), endDocEvent())
test "Parsing: Aliases in map":
ensure("&a a: &b b\n*a: *b", startDoc(), startMap(),
scalar("a", yTagQuestionMark, 0.AnchorId),
scalar("b", yTagQuestionMark, 1.AnchorId), alias(0.AnchorId),
alias(1.AnchorId), endMap(), endDoc())
ensure("&a a: &b b\n*a : *b", startDocEvent(), startMapEvent(),
scalarEvent("a", yTagQuestionMark, 0.AnchorId),
scalarEvent("b", yTagQuestionMark, 1.AnchorId), aliasEvent(0.AnchorId),
aliasEvent(1.AnchorId), endMapEvent(), endDocEvent())
test "Parsing: Aliases in flow":
ensure("{ &a [a, &b b]: *b, *a: [c, *b, d]}", startDoc(), startMap(),
startSequence(yTagQuestionMark, 0.AnchorId), scalar("a"),
scalar("b", yTagQuestionMark, 1.AnchorId), endSequence(),
alias(1.AnchorId), alias(0.AnchorId), startSequence(),
scalar("c"), alias(1.AnchorId), scalar("d"), endSequence(),
endMap(), endDoc())
ensure("{ &a [a, &b b]: *b, *a : [c, *b, d]}", startDocEvent(), startMapEvent(),
startSeqEvent(yTagQuestionMark, 0.AnchorId), scalarEvent("a"),
scalarEvent("b", yTagQuestionMark, 1.AnchorId), endSeqEvent(),
aliasEvent(1.AnchorId), aliasEvent(0.AnchorId), startSeqEvent(),
scalarEvent("c"), aliasEvent(1.AnchorId), scalarEvent("d"), endSeqEvent(),
endMapEvent(), endDocEvent())
test "Parsing: Tags on empty scalars":
ensure("!!str : a\nb: !!int\n!!str : !!str", startDoc(), startMap(),
scalar("", yTagString), scalar("a"), scalar("b"),
scalar("", yTagInteger), scalar("", yTagString),
scalar("", yTagString), endMap(), endDoc())
ensure("!!str : a\nb: !!int\n!!str : !!str", startDocEvent(), startMapEvent(),
scalarEvent("", yTagString), scalarEvent("a"), scalarEvent("b"),
scalarEvent("", yTagInteger), scalarEvent("", yTagString),
scalarEvent("", yTagString), endMapEvent(), endDocEvent())
test "Parsing: Anchors on empty scalars":
ensure("&a : a\nb: &b\n&c : &a", startDoc(), startMap(),
scalar("", yTagQuestionMark, 0.AnchorId), scalar("a"),
scalar("b"), scalar("", yTagQuestionMark, 1.AnchorId),
scalar("", yTagQuestionMark, 2.AnchorId),
scalar("", yTagQuestionMark, 0.AnchorId), endMap(), endDoc())
ensure("&a : a\nb: &b\n&c : &a", startDocEvent(), startMapEvent(),
scalarEvent("", yTagQuestionMark, 0.AnchorId), scalarEvent("a"),
scalarEvent("b"), scalarEvent("", yTagQuestionMark, 1.AnchorId),
scalarEvent("", yTagQuestionMark, 2.AnchorId),
scalarEvent("", yTagQuestionMark, 3.AnchorId), endMapEvent(), endDocEvent())