mirror of https://github.com/status-im/NimYAML.git
262 lines
9.0 KiB
Nim
262 lines
9.0 KiB
Nim
import ../private/lex
|
|
|
|
import unittest, strutils
|
|
|
|
const tokensWithValue =
|
|
{ltScalarPart, ltQuotedScalar, ltYamlVersion, ltTagShorthand, ltTagUri,
|
|
ltUnknownDirective, ltUnknownDirectiveParams, ltLiteralTag, ltAnchor,
|
|
ltAlias, ltBlockScalar}
|
|
|
|
type
|
|
TokenWithValue = object
|
|
case kind: LexerToken
|
|
of tokensWithValue:
|
|
value: string
|
|
of ltIndentation:
|
|
indentation: int
|
|
of ltTagHandle:
|
|
handle, suffix: string
|
|
else: discard
|
|
|
|
proc actualRepr(lex: YamlLexer, t: LexerToken): string =
|
|
result = $t
|
|
case t
|
|
of tokensWithValue + {ltTagHandle}:
|
|
result.add("(" & escape(lex.buf) & ")")
|
|
of ltIndentation:
|
|
result.add("(" & $lex.indentation & ")")
|
|
else: discard
|
|
|
|
proc assertEquals(input: string, expected: varargs[TokenWithValue]) =
|
|
let lex = newYamlLexer(input)
|
|
var
|
|
i = 0
|
|
blockScalarEnd = -1
|
|
flowDepth = 0
|
|
for expectedToken in expected:
|
|
inc(i)
|
|
try:
|
|
lex.next()
|
|
doAssert lex.cur == expectedToken.kind, "Wrong token kind at #" & $i &
|
|
": Expected " & $expectedToken.kind & ", got " &
|
|
lex.actualRepr(lex.cur)
|
|
case expectedToken.kind
|
|
of tokensWithValue:
|
|
doAssert lex.buf == expectedToken.value, "Wrong token content at #" &
|
|
$i & ": Expected " & escape(expectedToken.value) &
|
|
", got " & escape(lex.buf)
|
|
lex.buf = ""
|
|
of ltIndentation:
|
|
doAssert lex.indentation == expectedToken.indentation,
|
|
"Wrong indentation length at #" & $i & ": Expected " &
|
|
$expectedToken.indentation & ", got " & $lex.indentation
|
|
if lex.indentation <= blockScalarEnd:
|
|
lex.endBlockScalar()
|
|
blockScalarEnd = -1
|
|
of ltBraceOpen, ltBracketOpen:
|
|
inc(flowDepth)
|
|
if flowDepth == 1: lex.setFlow(true)
|
|
of ltBraceClose, ltBracketClose:
|
|
dec(flowDepth)
|
|
if flowDepth == 0: lex.setFlow(false)
|
|
of ltTagHandle:
|
|
let
|
|
handle = lex.buf.substr(0, lex.shorthandEnd)
|
|
suffix = lex.buf.substr(lex.shorthandEnd + 1)
|
|
doAssert handle == expectedToken.handle,
|
|
"Wrong handle at #" & $i & ": Expected " & expectedToken.handle &
|
|
", got " & handle
|
|
doAssert suffix == expectedToken.suffix,
|
|
"Wrong suffix at #" & $i & ": Expected " & expectedToken.suffix &
|
|
", got " & suffix
|
|
lex.buf = ""
|
|
else: discard
|
|
except YamlLexerError:
|
|
let e = (ref YamlLexerError)(getCurrentException())
|
|
echo "Error at line " & $e.line & ", column " & $e.column & ":"
|
|
echo e.lineContent
|
|
assert false
|
|
|
|
proc assertLookahead(input: string, expected: bool, tokensBefore: int = 1) =
|
|
let lex = newYamlLexer(input)
|
|
var flowDepth = 0
|
|
for i in 0..tokensBefore:
|
|
lex.next()
|
|
case lex.cur
|
|
of ltBraceOpen, ltBracketOpen:
|
|
inc(flowDepth)
|
|
if flowDepth == 1: lex.setFlow(true)
|
|
of ltBraceClose, ltBracketClose:
|
|
dec(flowDepth)
|
|
if flowDepth == 0: lex.setFlow(false)
|
|
else: discard
|
|
doAssert lex.isImplicitKeyStart() == expected
|
|
|
|
proc i(indent: int): TokenWithValue =
|
|
TokenWithValue(kind: ltIndentation, indentation: indent)
|
|
proc sp(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltScalarPart, value: v)
|
|
proc qs(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltQuotedScalar, value: v)
|
|
proc se(): TokenWithValue = TokenWithValue(kind: ltStreamEnd)
|
|
proc mk(): TokenWithValue = TokenWithValue(kind: ltMapKeyInd)
|
|
proc mv(): TokenWithValue = TokenWithValue(kind: ltMapValInd)
|
|
proc si(): TokenWithValue = TokenWithValue(kind: ltSeqItemInd)
|
|
proc dy(): TokenWithValue = TokenWithValue(kind: ltYamlDirective)
|
|
proc dt(): TokenWithValue = TokenWithValue(kind: ltTagDirective)
|
|
proc du(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltUnknownDirective, value: v)
|
|
proc dp(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltUnknownDirectiveParams, value: v)
|
|
proc yv(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltYamlVersion, value: v)
|
|
proc ts(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltTagShorthand, value: v)
|
|
proc tu(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltTagUri, value: v)
|
|
proc dirE(): TokenWithValue = TokenWithValue(kind: ltDirectivesEnd)
|
|
proc docE(): TokenWithValue = TokenWithValue(kind: ltDocumentEnd)
|
|
proc bsh(): TokenWithValue = TokenWithValue(kind: ltBlockScalarHeader)
|
|
proc bs(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltBlockScalar, value: v)
|
|
proc el(): TokenWithValue = TokenWithValue(kind: ltEmptyLine)
|
|
proc ao(): TokenWithValue = TokenWithValue(kind: ltBracketOpen)
|
|
proc ac(): TokenWithValue = TokenWithValue(kind: ltBracketClose)
|
|
proc oo(): TokenWithValue = TokenWithValue(kind: ltBraceOpen)
|
|
proc oc(): TokenWithValue = TokenWithValue(kind: ltBraceClose)
|
|
proc c(): TokenWithValue = TokenWithValue(kind: ltComma)
|
|
proc th(handle, suffix: string): TokenWithValue =
|
|
TokenWithValue(kind: ltTagHandle, handle: handle, suffix: suffix)
|
|
proc lt(v: string): TokenWithValue =
|
|
TokenWithValue(kind: ltLiteralTag, value: v)
|
|
proc an(v: string): TokenWithValue = TokenWithValue(kind: ltAnchor, value: v)
|
|
proc al(v: string): TokenWithValue = TokenWithValue(kind: ltAlias, value: v)
|
|
|
|
suite "Lexer":
|
|
test "Empty document":
|
|
assertEquals("", se())
|
|
|
|
test "Single-line scalar":
|
|
assertEquals("scalar", i(0), sp("scalar"), se())
|
|
|
|
test "Multiline scalar":
|
|
assertEquals("scalar\l line two", i(0), sp("scalar"), i(2),
|
|
sp("line two"), se())
|
|
|
|
test "Single-line mapping":
|
|
assertEquals("key: value", i(0), sp("key"), mv(), sp("value"), se())
|
|
|
|
test "Multiline mapping":
|
|
assertEquals("key:\n value", i(0), sp("key"), mv(), i(2), sp("value"),
|
|
se())
|
|
|
|
test "Explicit mapping":
|
|
assertEquals("? key\n: value", i(0), mk(), sp("key"), i(0), mv(),
|
|
sp("value"), se())
|
|
|
|
test "Sequence":
|
|
assertEquals("- a\n- b", i(0), si(), sp("a"), i(0), si(), sp("b"), se())
|
|
|
|
test "Single-line single-quoted scalar":
|
|
assertEquals("'quoted scalar'", i(0), qs("quoted scalar"), se())
|
|
|
|
test "Multiline single-quoted scalar":
|
|
assertEquals("'quoted\l multi line \l\lscalar'", i(0),
|
|
qs("quoted multi line\lscalar"), se())
|
|
|
|
test "Single-line double-quoted scalar":
|
|
assertEquals("\"quoted scalar\"", i(0), qs("quoted scalar"), se())
|
|
|
|
test "Multiline double-quoted scalar":
|
|
assertEquals("\"quoted\l multi line \l\lscalar\"", i(0),
|
|
qs("quoted multi line\lscalar"), se())
|
|
|
|
test "Escape sequences":
|
|
assertEquals(""""\n\x31\u0032\U00000033"""", i(0), qs("\l123"), se())
|
|
|
|
test "Directives":
|
|
assertEquals("%YAML 1.2\n---\n%TAG\n...\n\n%TAG ! example.html",
|
|
dy(), yv("1.2"), dirE(), i(0), sp("%TAG"), i(0), docE(), dt(),
|
|
ts("!"), tu("example.html"), se())
|
|
|
|
test "Markers and Unknown Directive":
|
|
assertEquals("---\n---\n...\n%UNKNOWN warbl", dirE(), dirE(), i(0),
|
|
docE(), du("UNKNOWN"), dp("warbl"), se())
|
|
|
|
test "Block scalar":
|
|
assertEquals("|\l a\l\l b\l # comment", i(0), bsh(), bs("a\l\lb\l"), se())
|
|
|
|
test "Block Scalars":
|
|
assertEquals("one : >2-\l foo\l bar\ltwo: |+\l bar\l baz", i(0),
|
|
sp("one"), mv(), bsh(), bs(" foo\lbar"), i(0), sp("two"), mv(), bsh(),
|
|
bs("bar\l baz"), se())
|
|
|
|
test "Flow indicators":
|
|
assertEquals("bla]: {c: d, [e]: f}", i(0), sp("bla]"), mv(), oo(), sp("c"),
|
|
mv(), sp("d"), c(), ao(), sp("e"), ac(), mv(), sp("f"), oc(), se())
|
|
|
|
test "Adjacent map values in flow style":
|
|
assertEquals("{\"foo\":bar, [1]\l:egg}", i(0), oo(), qs("foo"), mv(),
|
|
sp("bar"), c(), ao(), sp("1"), ac(), mv(), sp("egg"), oc(), se())
|
|
|
|
test "Tag handles":
|
|
assertEquals("- !!str string\l- !local local\l- !e! e", i(0), si(),
|
|
th("!!", "str"), sp("string"), i(0), si(), th("!", "local"),
|
|
sp("local"), i(0), si(), th("!e!", ""), sp("e"), se())
|
|
|
|
test "Literal tag handle":
|
|
assertEquals("!<tag:yaml.org,2002:str> string", i(0),
|
|
lt("tag:yaml.org,2002:str"), sp("string"), se())
|
|
|
|
test "Anchors and aliases":
|
|
assertEquals("&a foo: {&b b: *a, *b : c}", i(0), an("a"), sp("foo"), mv(),
|
|
oo(), an("b"), sp("b"), mv(), al("a"), c(), al("b"), mv(), sp("c"),
|
|
oc(), se())
|
|
|
|
test "Empty lines":
|
|
assertEquals("""block: foo
|
|
|
|
bar
|
|
|
|
baz
|
|
flow: {
|
|
foo
|
|
|
|
bar: baz
|
|
|
|
|
|
mi
|
|
}""", i(0), sp("block"), mv(), sp("foo"), el(), i(2), sp("bar"), el(), i(4),
|
|
sp("baz"), i(0), sp("flow"), mv(), oo(), sp("foo"), el(), sp("bar"), mv(),
|
|
sp("baz"), el(), el(), sp("mi"), oc(), se())
|
|
|
|
suite "Lookahead":
|
|
test "Simple Scalar":
|
|
assertLookahead("abcde", false)
|
|
|
|
test "Simple Mapping":
|
|
assertLookahead("a: b", true)
|
|
|
|
test "Colon inside plain scalar":
|
|
assertLookahead("abc:de", false)
|
|
|
|
test "Colon inside quoted scalar":
|
|
assertLookahead("'abc: de'", false)
|
|
|
|
test "Quotes inside plain scalar":
|
|
assertLookahead("abc\'\"de: foo", true)
|
|
|
|
test "Flow indicator inside plain scalar":
|
|
assertLookahead("abc}]: de", true)
|
|
|
|
test "Complex key":
|
|
assertLookahead("[1, 2, \"3\"]: foo", true)
|
|
|
|
test "Flow value":
|
|
assertLookahead("{a: b}", false)
|
|
|
|
test "In flow context":
|
|
assertLookahead("[ abcde]: foo", false, 2)
|
|
|
|
test "Adjacent value":
|
|
assertLookahead("[\"abc\":de]", true, 2) |