NimYAML/private/parser.nim

1002 lines
38 KiB
Nim
Raw Normal View History

# NimYAML - YAML implementation in Nim
# (c) Copyright 2015 Felix Krause
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
2015-12-05 11:10:17 +00:00
type
YamlParserState = enum
2015-12-23 11:35:07 +00:00
ypInitial, ypSkipDirective, ypBlockLineStart, ypBlockAfterTag,
ypBlockAfterAnchor, ypBlockAfterAnchorAndTag, ypBlockAfterScalar,
ypBlockAfterAlias, ypBlockAfterColon, ypBlockMultilineScalar,
ypBlockLineEnd, ypBlockScalarHeader, ypBlockScalar, ypFlow,
ypFlowAfterObject, ypFlowAfterTag, ypFlowAfterAnchor,
ypFlowAfterQuestionMark, ypFlowAfterAnchorAndTag,
ypExpectingDocumentEnd, ypAfterDirectivesEnd
2015-12-05 11:10:17 +00:00
2015-12-11 21:55:21 +00:00
DocumentLevelMode = enum
mBlockSequenceItem, mFlowSequenceItem, mExplicitBlockMapKey,
mImplicitBlockMapKey, mBlockMapValue, mFlowMapKey, mFlowMapValue,
mScalar, mUnknown
2015-12-05 11:10:17 +00:00
DocumentLevel = object
2015-12-11 21:55:21 +00:00
mode: DocumentLevelMode
2015-12-05 11:10:17 +00:00
indicatorColumn: int
2015-12-11 21:55:21 +00:00
indentationColumn: int
2015-12-17 20:44:41 +00:00
LineStrippingMode = enum
lsStrip, lsClip, lsKeep
BlockScalarStyle = enum
bsLiteral, bsFolded
proc `$`*(id: TagId): string =
case id
of yTagQuestionMark: "?"
of yTagExclamationMark: "!"
of yTagString: "!!str"
of yTagSequence: "!!seq"
of yTagMap: "!!map"
of yTagNull: "!!null"
of yTagBoolean: "!!bool"
of yTagInteger: "!!int"
of yTagFloat: "!!float"
of yTagOrderedMap: "!!omap"
of yTagPairs: "!!pairs"
of yTagSet: "!!set"
of yTagBinary: "!!binary"
of yTagMerge: "!!merge"
of yTagTimestamp: "!!timestamp"
of yTagValue: "!!value"
of yTagYaml: "!!yaml"
else:
"<" & $cast[int](id) & ">"
proc newParser*(tagLib: YamlTagLibrary): YamlSequentialParser =
2015-12-23 11:35:07 +00:00
new(result)
result.tagLib = tagLib
result.anchors = initOrderedTable[string, AnchorId]()
proc setWarningCallback*(parser: YamlSequentialParser,
callback: YamlWarningCallback) =
parser.callback = callback
proc anchor*(parser: YamlSequentialParser, id: AnchorId): string =
for pair in parser.anchors.pairs:
if pair[1] == id:
return pair[0]
return nil
2015-12-05 11:10:17 +00:00
template yieldWarning(d: string) {.dirty.} =
if parser.callback != nil:
parser.callback(lex.line, lex.column, lex.getCurrentLine(), d)
2015-12-05 11:10:17 +00:00
template raiseError(message: string) {.dirty.} =
var e = newException(YamlParserError, message)
e.line = lex.line
e.column = lex.column
e.lineContent = lex.getCurrentLine()
raise e
2015-12-05 11:10:17 +00:00
template yieldUnexpectedToken(expected: string = "") {.dirty.} =
var msg = "[" & $state & "] Unexpected token"
if expected.len > 0:
msg.add(" (expected " & expected & ")")
msg.add(": " & $token)
raiseError(msg)
2015-12-23 11:35:07 +00:00
proc resolveAnchor(parser: YamlSequentialParser, anchor: var string):
AnchorId {.inline.} =
2015-12-27 22:40:27 +00:00
result = yAnchorNone
if anchor.len > 0:
result = cast[AnchorId](parser.anchors.len)
if parser.anchors.hasKeyOrPut(anchor, result):
result = parser.anchors[anchor]
anchor = ""
2015-12-23 11:35:07 +00:00
proc resolveAlias(parser: YamlSequentialParser, name: string): AnchorId =
try:
result = parser.anchors[name]
except KeyError:
2015-12-27 22:40:27 +00:00
result = yAnchorNone
2015-12-23 11:35:07 +00:00
proc resolveTag(parser: YamlSequentialParser, tag: var string,
quotedString: bool = false): TagId {.inline.} =
if tag.len == 0:
2015-12-27 22:40:27 +00:00
result = if quotedString: parser.tagLib.tags["!"] else:
parser.tagLib.tags["?"]
2015-12-21 20:58:28 +00:00
else:
try:
result = parser.tagLib.tags[tag]
2015-12-21 20:58:28 +00:00
except KeyError:
result = parser.tagLib.registerUri(tag)
tag = ""
template yieldScalar(content: string, quoted: bool = false) {.dirty.} =
when defined(yamlDebug):
echo "Parser token [mode=", level.mode, ", state=", state, "]: ",
"scalar[\"", content, "\"]"
if objectTag.len > 0:
if tag.len > 0:
raiseError("Duplicate tag for scalar (tag=" & tag & ", objectTag=" &
objectTag)
tag = objectTag
objectTag = ""
var e = scalarEvent(nil, resolveTag(parser, tag, quoted),
resolveAnchor(parser, anchor))
shallowCopy(e.scalarContent, content)
yield e
2015-12-05 11:10:17 +00:00
template yieldStartMap() {.dirty.} =
when defined(yamlDebug):
echo "Parser token [mode=", level.mode, ", state=", state, "]: yamlStartMap"
yield YamlStreamEvent(kind: yamlStartMap,
mapAnchor: resolveAnchor(parser, anchor),
mapTag: resolveTag(parser, tag))
template yieldStartSequence() {.dirty.} =
when defined(yamlDebug):
echo "Parser token [mode=", level.mode, ", state=", state, "]: yamlStartSequence"
yield YamlStreamEvent(kind: yamlStartSequence,
seqAnchor: resolveAnchor(parser, anchor),
seqTag: resolveTag(parser, tag))
template yieldStart(t: YamlStreamEventKind) {.dirty.} =
when t == yamlStartMap:
yieldStartMap()
else:
yieldStartSequence()
2015-12-05 11:10:17 +00:00
2015-12-21 22:10:42 +00:00
template yieldDocumentEnd() {.dirty.} =
yield YamlStreamEvent(kind: yamlEndDocument)
2015-12-21 22:10:42 +00:00
tagShorthands = initTable[string, string]()
tagShorthands["!"] = "!"
tagShorthands["!!"] = yamlTagRepositoryPrefix
parser.anchors = initOrderedTable[string, AnchorId]()
2015-12-21 22:10:42 +00:00
2015-12-11 21:55:21 +00:00
template closeLevel(lvl: DocumentLevel) {.dirty.} =
case lvl.mode
of mExplicitBlockMapKey, mFlowMapKey:
yieldScalar("")
yield YamlStreamEvent(kind: yamlEndMap)
of mImplicitBlockMapKey, mBlockMapValue, mFlowMapValue:
yield YamlStreamEvent(kind: yamlEndMap)
2015-12-11 21:55:21 +00:00
of mBlockSequenceItem, mFlowSequenceItem:
yield YamlStreamEvent(kind: yamlEndSequence)
of mScalar:
when defined(yamlDebug):
echo "Parser token [mode=", level.mode, ", state=", state, "]: ",
"scalar[\"", scalarCache, "\"]"
yieldScalar(scalarCache)
2015-12-11 21:55:21 +00:00
else:
yieldScalar("")
2015-12-11 21:55:21 +00:00
2015-12-22 22:35:03 +00:00
proc mustLeaveLevel(curCol: int, ancestry: seq[DocumentLevel]): bool =
if ancestry.len == 0:
result = false
else:
let parent = ancestry[ancestry.high]
result = parent.indicatorColumn >= curCol or
(parent.indicatorColumn == -1 and
parent.indentationColumn >= curCol)
2015-12-11 21:55:21 +00:00
template leaveMoreIndentedLevels() {.dirty.} =
while ancestry.len > 0:
let parent = ancestry[ancestry.high]
if parent.indicatorColumn >= lex.column or
(parent.indicatorColumn == -1 and
parent.indentationColumn >= lex.column):
closeLevel(level)
level = ancestry.pop()
if level.mode == mBlockMapValue:
level.mode = mImplicitBlockMapKey
else:
break
2015-12-11 21:55:21 +00:00
template closeAllLevels() {.dirty.} =
2015-12-11 21:55:21 +00:00
while true:
closeLevel(level)
if ancestry.len == 0: break
level = ancestry.pop()
2015-12-22 21:28:27 +00:00
template handleBlockIndicator(expected, possible: openarray[DocumentLevelMode],
next: DocumentLevelMode,
entering: YamlStreamEventKind,
2015-12-22 21:28:27 +00:00
emptyScalarOnOpening: bool = false) {.dirty.} =
2015-12-11 21:55:21 +00:00
leaveMoreIndentedLevels()
if level.indicatorColumn == lex.column or
level.indicatorColumn == -1 and level.indentationColumn == lex.column:
if level.mode in expected:
2015-12-11 21:55:21 +00:00
level.mode = next
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
else:
2015-12-22 21:28:27 +00:00
# `in` does not work if possible is [], so we have to check for that
when possible.len > 0:
if level.mode in possible:
yieldScalar("")
2015-12-22 21:28:27 +00:00
level.mode = next
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
else:
raiseError("Invalid token after " & $level.mode)
2015-12-22 21:28:27 +00:00
else:
raiseError("Invalid token after " & $level.mode)
2015-12-11 21:55:21 +00:00
elif level.mode != mUnknown:
raiseError("Invalid indentation")
2015-12-11 21:55:21 +00:00
else:
level.mode = next
level.indicatorColumn = lex.column
2015-12-22 21:28:27 +00:00
if emptyScalarOnOpening:
2015-12-22 22:35:03 +00:00
# do not consume anchor and tag; they are on the scalar
var
cachedAnchor = anchor
cachedTag = tag
anchor = ""
tag = objectTag
2015-12-22 22:35:03 +00:00
yieldStart(entering)
anchor = cachedAnchor
tag = cachedTag
objectTag = ""
yieldScalar("")
2015-12-22 22:35:03 +00:00
else:
tag = objectTag
objectTag = ""
2015-12-22 22:35:03 +00:00
yieldStart(entering)
2015-12-11 21:55:21 +00:00
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
template startPlainScalar() {.dirty.} =
level.mode = mScalar
2015-12-24 14:21:27 +00:00
level.indentationColumn = lex.column
scalarCache = lex.content
2015-12-24 14:21:27 +00:00
scalarIndentation = lex.column
2015-12-23 11:35:07 +00:00
state = ypBlockAfterScalar
template handleTagHandle() {.dirty.} =
let handle = lex.content
if tagShorthands.hasKey(handle):
token = nextToken(lex)
if finished(nextToken):
raiseError("Missing tag suffix")
continue
2015-12-23 11:35:07 +00:00
if token != tTagSuffix:
raiseError("Missing tag suffix")
continue
tag = tagShorthands[handle] & lex.content
2015-12-22 22:35:03 +00:00
if level.indentationColumn == -1 and level.indicatorColumn == -1:
level.indentationColumn = lex.column
else:
raiseError("Unknown tag shorthand: " & handle)
proc parse*(parser: YamlSequentialParser, s: Stream): YamlStream =
result = iterator(): YamlStreamEvent =
2015-12-05 11:10:17 +00:00
var
# parsing state
2015-12-11 21:55:21 +00:00
lex: YamlLexer
state: YamlParserState = ypInitial
# document state
2015-12-05 11:10:17 +00:00
foundYamlDirective = false
tagShorthands = initTable[string, string]()
# object tree state
2015-12-11 21:55:21 +00:00
ancestry = newSeq[DocumentLevel]()
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-17 20:44:41 +00:00
# block scalar state
lineStrip: LineStrippingMode
blockScalar: BlockScalarStyle
blockScalarIndentation: int
blockScalarTrailing: string = nil
# cached values
tag: string = ""
objectTag: string = ""
anchor: string = ""
scalarCache: string = nil
scalarIndentation: int
scalarCacheIsQuoted: bool = false
2015-12-27 22:40:27 +00:00
aliasCache = yAnchorNone
2015-12-23 11:35:07 +00:00
lex.open(s)
2015-12-21 22:10:42 +00:00
tagShorthands["!"] = "!"
tagShorthands["!!"] = yamlTagRepositoryPrefix
2015-12-05 11:10:17 +00:00
var nextToken = tokens
2015-12-05 11:10:17 +00:00
var token = nextToken(lex)
while not finished(nextToken):
2015-12-05 11:10:17 +00:00
case state
2015-12-23 11:35:07 +00:00
of ypInitial:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tYamlDirective:
2015-12-05 11:10:17 +00:00
if foundYamlDirective:
raiseError("Duplicate %YAML directive")
2015-12-11 21:55:21 +00:00
var
warn = false
actualVersion = ""
for version in [1, 2]:
token = nextToken(lex)
if finished(nextToken):
raiseError("Missing or badly formatted YAML version")
2015-12-23 11:35:07 +00:00
if token != tVersionPart:
raiseError("Missing or badly formatted YAML version")
2015-12-11 21:55:21 +00:00
if parseInt(lex.content) != version:
warn = true
if actualVersion.len > 0: actualVersion &= "."
actualVersion &= $version
if warn:
yieldWarning("Unsupported version: " & actualVersion &
", trying to parse anyway")
foundYamlDirective = true
2015-12-23 11:35:07 +00:00
of tTagDirective:
2015-12-05 11:10:17 +00:00
token = nextToken(lex)
if finished(nextToken):
raiseError("Incomplete %TAG directive")
2015-12-23 11:35:07 +00:00
if token != tTagHandle:
raiseError("Invalid token (expected tag handle)")
2015-12-05 11:10:17 +00:00
let tagHandle = lex.content
token = nextToken(lex)
if finished(nextToken):
raiseError("Incomplete %TAG directive")
2015-12-23 11:35:07 +00:00
if token != tTagURI:
raiseError("Invalid token (expected tag URI)")
2015-12-05 11:10:17 +00:00
tagShorthands[tagHandle] = lex.content
2015-12-23 11:35:07 +00:00
of tUnknownDirective:
2015-12-05 11:10:17 +00:00
yieldWarning("Unknown directive: " & lex.content)
2015-12-23 11:35:07 +00:00
state = ypSkipDirective
of tComment:
2015-12-05 11:10:17 +00:00
discard
2015-12-23 11:35:07 +00:00
of tDirectivesEnd:
yield YamlStreamEvent(kind: yamlStartDocument)
2015-12-11 21:55:21 +00:00
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-23 11:35:07 +00:00
state = ypAfterDirectivesEnd
of tDocumentEnd, tStreamEnd:
yield YamlStreamEvent(kind: yamlStartDocument)
2015-12-21 22:10:42 +00:00
yieldDocumentEnd()
2015-12-05 11:10:17 +00:00
else:
yield YamlStreamEvent(kind: yamlStartDocument)
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-05 11:10:17 +00:00
continue
2015-12-23 11:35:07 +00:00
of ypSkipDirective:
if token notin [tUnknownDirectiveParam, tTagHandle,
tTagURI, tVersionPart, tComment]:
state = ypInitial
2015-12-05 11:10:17 +00:00
continue
2015-12-23 11:35:07 +00:00
of ypAfterDirectivesEnd:
case token
2015-12-23 11:35:07 +00:00
of tTagHandle:
handleTagHandle()
objectTag = tag
tag = ""
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
of tComment:
state = ypBlockLineEnd
of tLineStart:
state = ypBlockLineStart
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockLineStart:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tLineStart:
discard
2015-12-23 11:35:07 +00:00
of tDash:
2015-12-22 21:28:27 +00:00
handleBlockIndicator([mBlockSequenceItem], [],
mBlockSequenceItem, yamlStartSequence)
2015-12-23 11:35:07 +00:00
of tQuestionmark:
handleBlockIndicator([mImplicitBlockMapKey, mBlockMapValue],
2015-12-22 21:28:27 +00:00
[mExplicitBlockMapKey],
2015-12-11 21:55:21 +00:00
mExplicitBlockMapKey, yamlStartMap)
2015-12-23 11:35:07 +00:00
of tColon:
handleBlockIndicator([mExplicitBlockMapKey],
2015-12-22 21:28:27 +00:00
[mBlockMapValue, mImplicitBlockMapKey],
mBlockMapValue, yamlStartMap, true)
2015-12-23 11:35:07 +00:00
of tPipe, tGreater:
blockScalar = if token == tPipe: bsLiteral else: bsFolded
2015-12-17 20:44:41 +00:00
blockScalarIndentation = -1
lineStrip = lsClip
2015-12-23 11:35:07 +00:00
state = ypBlockScalarHeader
2015-12-17 20:44:41 +00:00
scalarCache = ""
level.mode = mScalar
2015-12-23 11:35:07 +00:00
of tTagHandle:
2015-12-22 22:35:03 +00:00
leaveMoreIndentedLevels()
handleTagHandle()
2015-12-21 22:10:42 +00:00
level.indentationColumn = lex.column
2015-12-23 11:35:07 +00:00
state = ypBlockAfterTag
of tVerbatimTag:
2015-12-11 21:55:21 +00:00
tag = lex.content
2015-12-23 11:35:07 +00:00
state = ypBlockAfterTag
level.indentationColumn = lex.column
2015-12-23 11:35:07 +00:00
of tAnchor:
2015-12-22 22:35:03 +00:00
leaveMoreIndentedLevels()
2015-12-11 21:55:21 +00:00
anchor = lex.content
level.indentationColumn = lex.column
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAnchor
of tScalarPart:
leaveMoreIndentedLevels()
case level.mode
of mUnknown:
startPlainScalar()
level.indentationColumn = lex.column
of mImplicitBlockMapKey:
scalarCache = lex.content
scalarCacheIsQuoted = false
scalarIndentation = lex.column
of mBlockMapValue:
scalarCache = lex.content
scalarCacheIsQuoted = false
scalarIndentation = lex.column
2015-12-22 21:28:27 +00:00
level.mode = mImplicitBlockMapKey
of mExplicitBlockMapKey:
yieldScalar("")
2015-12-22 21:28:27 +00:00
level.mode = mBlockMapValue
continue
else:
raiseError("Unexpected scalar in " & $level.mode)
2015-12-23 11:35:07 +00:00
state = ypBlockAfterScalar
of tScalar:
2015-12-11 21:55:21 +00:00
leaveMoreIndentedLevels()
case level.mode
of mUnknown, mImplicitBlockMapKey:
scalarCache = lex.content
scalarCacheIsQuoted = true
scalarIndentation = lex.column
2015-12-23 11:35:07 +00:00
state = ypBlockAfterScalar
2015-12-11 21:55:21 +00:00
else:
raiseError("Unexpected scalar")
2015-12-23 11:35:07 +00:00
of tAlias:
aliasCache = resolveAlias(parser, lex.content)
2015-12-27 22:40:27 +00:00
if aliasCache == yAnchorNone:
raiseError("[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
2016-01-07 10:49:01 +00:00
of mUnknown, mImplicitBlockMapKey, mBlockMapValue,
mExplicitBlockMapKey, mBlockSequenceItem:
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAlias
else:
raiseError("Unexpected alias")
2015-12-23 11:35:07 +00:00
of tStreamEnd:
closeAllLevels()
yield YamlStreamEvent(kind: yamlEndDocument)
break
2015-12-23 11:35:07 +00:00
of tDocumentEnd:
closeAllLevels()
2015-12-21 22:10:42 +00:00
yieldDocumentEnd()
2015-12-23 11:35:07 +00:00
state = ypInitial
of tOpeningBrace:
state = ypFlow
continue
2015-12-23 11:35:07 +00:00
of tOpeningBracket:
state = ypFlow
continue
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockMultilineScalar:
case token
2015-12-23 11:35:07 +00:00
of tScalarPart:
leaveMoreIndentedLevels()
if level.mode != mScalar:
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
continue
scalarCache &= " " & lex.content
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
of tLineStart:
discard
2016-01-07 10:29:37 +00:00
of tColon, tDash, tQuestionmark, tOpeningBrace, tOpeningBracket:
leaveMoreIndentedLevels()
if level.mode != mScalar:
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
continue
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of tDocumentEnd, tStreamEnd:
state = ypBlockLineStart
continue
else:
leaveMoreIndentedLevels()
if level.mode == mScalar:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
continue
2015-12-23 11:35:07 +00:00
of ypBlockAfterScalar:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tColon:
assert level.mode in [mUnknown, mImplicitBlockMapKey, mScalar]
if level.mode in [mUnknown, mScalar]:
yield YamlStreamEvent(kind: yamlStartMap,
2015-12-27 22:40:27 +00:00
mapAnchor: yAnchorNone,
mapTag: parser.resolveTag(objectTag))
level.mode = mBlockMapValue
2015-12-11 21:55:21 +00:00
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
yieldScalar(scalarCache, scalarCacheIsQuoted)
scalarCache = nil
2015-12-23 11:35:07 +00:00
state = ypBlockAfterColon
of tLineStart:
2015-12-11 21:55:21 +00:00
if level.mode == mImplicitBlockMapKey:
raiseError("Missing colon after implicit map key")
if level.mode != mScalar:
yieldScalar(scalarCache, scalarCacheIsQuoted)
scalarCache = nil
if ancestry.len > 0:
level = ancestry.pop()
state = ypBlockLineStart
else:
2015-12-23 11:35:07 +00:00
state = ypExpectingDocumentEnd
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypBlockMultilineScalar
of tStreamEnd:
yieldScalar(scalarCache, scalarCacheIsQuoted)
scalarCache = nil
2015-12-11 21:55:21 +00:00
if ancestry.len > 0:
level = ancestry.pop()
closeAllLevels()
yield YamlStreamEvent(kind: yamlEndDocument)
break
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockAfterAlias:
case token
2015-12-23 11:35:07 +00:00
of tColon:
assert level.mode in [mUnknown, mImplicitBlockMapKey]
if level.mode == mUnknown:
yield YamlStreamEvent(kind: yamlStartMap,
2015-12-27 22:40:27 +00:00
mapAnchor: yAnchorNone,
mapTag: parser.resolveTag(objectTag))
level.mode = mBlockMapValue
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
yield YamlStreamEvent(kind: yamlAlias, aliasTarget: aliasCache)
2015-12-23 11:35:07 +00:00
state = ypBlockAfterColon
of tLineStart:
if level.mode == mImplicitBlockMapKey:
raiseError("Missing colon after implicit map key")
if level.mode == mUnknown:
assert ancestry.len > 0
level = ancestry.pop()
yield YamlStreamEvent(kind: yamlAlias, aliasTarget: aliasCache)
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
of tStreamEnd:
yield YamlStreamEvent(kind: yamlAlias, aliasTarget: aliasCache)
if level.mode == mUnknown:
assert ancestry.len > 0
level = ancestry.pop()
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
continue
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockAfterTag:
2015-12-22 22:35:03 +00:00
if mustLeaveLevel(lex.column, ancestry):
leaveMoreIndentedLevels()
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-22 22:35:03 +00:00
continue
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tAnchor:
2015-12-11 21:55:21 +00:00
anchor = lex.content
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAnchorAndTag
of tScalar, tColon, tStreamEnd, tScalarPart:
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-11 21:55:21 +00:00
continue
2015-12-23 11:35:07 +00:00
of tLineStart:
if objectTag.len > 0:
raiseError("Duplicate tag for object")
else:
objectTag = tag
tag = ""
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
of tOpeningBracket, tOpeningBrace:
state = ypFlow
2015-12-11 21:55:21 +00:00
continue
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockAfterAnchor:
2015-12-22 22:35:03 +00:00
if mustLeaveLevel(lex.column, ancestry):
leaveMoreIndentedLevels()
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-22 22:35:03 +00:00
continue
2015-12-11 21:55:21 +00:00
case token
of tScalar, tColon, tStreamEnd, tScalarPart:
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-11 21:55:21 +00:00
continue
2015-12-23 11:35:07 +00:00
of tLineStart:
discard
2015-12-23 11:35:07 +00:00
of tOpeningBracket, tOpeningBrace:
state = ypFlow
continue
2015-12-23 11:35:07 +00:00
of tTagHandle:
handleTagHandle()
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAnchorAndTag
of tVerbatimTag:
tag = lex.content
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAnchorAndTag
level.indentationColumn = lex.column
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockAfterAnchorAndTag:
2015-12-22 22:35:03 +00:00
if mustLeaveLevel(lex.column, ancestry):
leaveMoreIndentedLevels()
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-22 22:35:03 +00:00
continue
case token
of tScalar, tColon, tStreamEnd, tScalarPart:
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
continue
2015-12-23 11:35:07 +00:00
of tLineStart:
if objectTag.len > 0:
raiseError("Duplicate tag for object")
else:
objectTag = tag
tag = ""
state = ypBlockLineStart
2015-12-23 11:35:07 +00:00
of tOpeningBracket, tOpeningBrace:
state = ypFlow
2015-12-11 21:55:21 +00:00
continue
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockAfterColon:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tScalar:
yieldScalar(lex.content, true)
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
assert level.mode == mBlockMapValue
2015-12-11 21:55:21 +00:00
level.mode = mImplicitBlockMapKey
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
of tScalarPart:
startPlainScalar()
2015-12-23 11:35:07 +00:00
of tLineStart:
state = ypBlockLineStart
of tStreamEnd:
closeAllLevels()
yield YamlStreamEvent(kind: yamlEndDocument)
break
2015-12-23 11:35:07 +00:00
of tOpeningBracket, tOpeningBrace:
state = ypFlow
continue
2015-12-23 11:35:07 +00:00
of tPipe, tGreater:
blockScalar = if token == tPipe: bsLiteral else: bsFolded
2015-12-17 20:44:41 +00:00
blockScalarIndentation = -1
lineStrip = lsClip
2015-12-23 11:35:07 +00:00
state = ypBlockScalarHeader
2015-12-17 20:44:41 +00:00
scalarCache = ""
level.mode = mScalar
2015-12-23 11:35:07 +00:00
of tTagHandle:
2015-12-21 22:10:42 +00:00
handleTagHandle()
2015-12-23 11:35:07 +00:00
state = ypBlockAfterTag
of tAnchor:
2015-12-22 22:35:03 +00:00
level.indentationColumn = lex.column
2015-12-21 22:10:42 +00:00
anchor = lex.content
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAnchor
of tAlias:
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
raiseError("[alias] Unknown anchor: " & lex.content)
yield YamlStreamEvent(kind: yamlAlias, aliasTarget: aliasCache)
level = ancestry.pop()
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
else:
yieldUnexpectedToken("scalar or line end")
2015-12-23 11:35:07 +00:00
of ypBlockLineEnd:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tLineStart:
state = if level.mode == mScalar: ypBlockMultilineScalar else:
ypBlockLineStart
of tStreamEnd:
closeAllLevels()
yield YamlStreamEvent(kind: yamlEndDocument)
break
else:
yieldUnexpectedToken("line end")
2015-12-23 11:35:07 +00:00
of ypBlockScalarHeader:
2015-12-17 20:44:41 +00:00
case token
2015-12-23 11:35:07 +00:00
of tPlus:
2015-12-17 20:44:41 +00:00
if lineStrip != lsClip:
raiseError("Multiple chomping indicators!")
2015-12-17 20:44:41 +00:00
else:
lineStrip = lsKeep
2015-12-23 11:35:07 +00:00
of tDash:
2015-12-17 20:44:41 +00:00
if lineStrip != lsClip:
raiseError("Multiple chomping indicators!")
2015-12-17 20:44:41 +00:00
else:
lineStrip = lsStrip
2015-12-23 11:35:07 +00:00
of tBlockIndentationIndicator:
2015-12-17 20:44:41 +00:00
if blockScalarIndentation != -1:
raiseError("Multiple indentation indicators!")
2015-12-17 20:44:41 +00:00
else:
blockScalarIndentation = parseInt(lex.content)
2015-12-23 11:35:07 +00:00
of tLineStart:
2015-12-17 20:44:41 +00:00
blockScalarTrailing = ""
2015-12-23 11:35:07 +00:00
state = ypBlockScalar
2015-12-17 20:44:41 +00:00
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypBlockScalar:
2015-12-17 20:44:41 +00:00
case token
2015-12-23 11:35:07 +00:00
of tLineStart:
2015-12-17 20:44:41 +00:00
if level.indentationColumn == -1:
discard
else:
case blockScalar
of bsLiteral:
blockScalarTrailing &= "\x0A"
of bsFolded:
case blockScalarTrailing.len
of 0:
blockScalarTrailing = " "
of 1:
blockScalarTrailing = "\x0A"
else:
discard
if lex.content.len > level.indentationColumn - 1:
2015-12-17 20:44:41 +00:00
if blockScalar == bsFolded:
if blockScalarTrailing == " ":
blockScalarTrailing = "\x0A"
scalarCache &= blockScalarTrailing &
lex.content[level.indentationColumn - 1..^1]
2015-12-17 20:44:41 +00:00
blockScalarTrailing = ""
2015-12-23 11:35:07 +00:00
of tScalarPart:
2015-12-17 20:44:41 +00:00
if ancestry.high > 0:
if ancestry[ancestry.high].indicatorColumn >= lex.column or
ancestry[ancestry.high].indicatorColumn == -1 and
ancestry[ancestry.high].indentationColumn >= lex.column:
# todo: trailing chomping?
closeLevel(level)
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-17 20:44:41 +00:00
continue
if level.indentationColumn == -1:
level.indentationColumn = lex.column
else:
scalarCache &= blockScalarTrailing
blockScalarTrailing = ""
scalarCache &= lex.content
else:
case lineStrip
of lsStrip:
discard
of lsClip:
scalarCache &= "\x0A"
of lsKeep:
scalarCache &= blockScalarTrailing
closeLevel(level)
if ancestry.len == 0:
2015-12-23 11:35:07 +00:00
state = ypExpectingDocumentEnd
2015-12-17 20:44:41 +00:00
else:
level = ancestry.pop()
2015-12-23 11:35:07 +00:00
state = ypBlockLineStart
2015-12-17 20:44:41 +00:00
continue
2015-12-23 11:35:07 +00:00
of ypFlow:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tLineStart:
discard
of tScalar:
yieldScalar(lex.content, true)
level = ancestry.pop()
state = ypFlowAfterObject
of tScalarPart:
yieldScalar(lex.content)
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
of tColon:
yieldScalar("")
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
if level.mode == mFlowMapKey:
level.mode = mFlowMapValue
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
else:
yieldUnexpectedToken("scalar, comma or map end")
of tQuestionMark:
state = ypFlowAfterQuestionMark
2015-12-23 11:35:07 +00:00
of tComma:
yieldScalar("")
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
case level.mode
of mFlowMapValue:
level.mode = mFlowMapKey
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
of mFlowSequenceItem:
yieldScalar("")
2015-12-11 21:55:21 +00:00
else:
raiseError("Internal error! Please report this bug.")
2015-12-23 11:35:07 +00:00
of tOpeningBrace:
2015-12-11 21:55:21 +00:00
if level.mode != mUnknown:
yieldUnexpectedToken()
2015-12-11 21:55:21 +00:00
level.mode = mFlowMapKey
if objectTag.len > 0:
assert tag.len == 0
tag = objectTag
objectTag = ""
2015-12-11 21:55:21 +00:00
yieldStart(yamlStartMap)
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-23 11:35:07 +00:00
of tOpeningBracket:
2015-12-11 21:55:21 +00:00
if level.mode != mUnknown:
yieldUnexpectedToken()
2015-12-11 21:55:21 +00:00
level.mode = mFlowSequenceItem
if objectTag.len > 0:
assert tag.len == 0
tag = objectTag
objectTag = ""
2015-12-11 21:55:21 +00:00
yieldStart(yamlStartSequence)
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-23 11:35:07 +00:00
of tClosingBrace:
2015-12-11 21:55:21 +00:00
if level.mode == mUnknown:
yieldScalar("")
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
if level.mode != mFlowMapKey:
yieldUnexpectedToken($level.mode)
yield YamlStreamEvent(kind: yamlEndMap)
2015-12-11 21:55:21 +00:00
if ancestry.len > 0:
level = ancestry.pop()
case level.mode
of mFlowMapKey, mFlowMapValue, mFlowSequenceItem:
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypExpectingDocumentEnd
of tClosingBracket:
2015-12-11 21:55:21 +00:00
if level.mode == mUnknown:
yieldScalar("")
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
if level.mode != mFlowSequenceItem:
yieldUnexpectedToken()
else:
yield YamlStreamEvent(kind: yamlEndSequence)
2015-12-11 21:55:21 +00:00
if ancestry.len > 0:
level = ancestry.pop()
case level.mode
of mFlowMapKey, mFlowMapValue, mFlowSequenceItem:
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
else:
2015-12-23 11:35:07 +00:00
state = ypExpectingDocumentEnd
of tTagHandle:
2015-12-21 22:10:42 +00:00
handleTagHandle()
2015-12-23 11:35:07 +00:00
state = ypFlowAfterTag
of tAnchor:
2015-12-21 22:10:42 +00:00
anchor = lex.content
2015-12-23 11:35:07 +00:00
state = ypFlowAfterAnchor
of tAlias:
yield YamlStreamEvent(kind: yamlAlias,
aliasTarget: resolveAlias(parser, lex.content))
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
level = ancestry.pop()
else:
yieldUnexpectedToken()
of ypFlowAfterQuestionMark:
case token
of tScalar, tScalarPart, tColon, tComma, tOpeningBrace,
tOpeningBracket, tClosingBrace, tClosingBracket, tTagHandle,
tAnchor, tAlias:
state = ypFlow
continue
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypFlowAfterTag:
2015-12-21 22:10:42 +00:00
case token
2015-12-23 11:35:07 +00:00
of tTagHandle:
raiseError("Multiple tags on same node!")
2015-12-23 11:35:07 +00:00
of tAnchor:
2015-12-21 22:10:42 +00:00
anchor = lex.content
2015-12-23 11:35:07 +00:00
state = ypFlowAfterAnchorAndTag
2015-12-21 22:10:42 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypFlow
2015-12-21 22:10:42 +00:00
continue
2015-12-23 11:35:07 +00:00
of ypFlowAfterAnchor:
2015-12-21 22:10:42 +00:00
case token
2015-12-23 11:35:07 +00:00
of tAnchor:
raiseError("Multiple anchors on same node!")
2015-12-23 11:35:07 +00:00
of tTagHandle:
2015-12-21 22:10:42 +00:00
handleTagHandle()
2015-12-23 11:35:07 +00:00
state = ypFlowAfterAnchorAndTag
2015-12-21 22:10:42 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypFlow
2015-12-21 22:10:42 +00:00
continue
2015-12-23 11:35:07 +00:00
of ypFlowAfterAnchorAndTag:
2015-12-21 22:10:42 +00:00
case token
2015-12-23 11:35:07 +00:00
of tAnchor:
raiseError("Multiple anchors on same node!")
2015-12-23 11:35:07 +00:00
of tTagHandle:
raiseError("Multiple tags on same node!")
2015-12-21 22:10:42 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypFlow
2015-12-21 22:10:42 +00:00
continue
2015-12-23 11:35:07 +00:00
of ypFlowAfterObject:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tLineStart:
discard
2015-12-23 11:35:07 +00:00
of tColon:
2015-12-11 21:55:21 +00:00
if level.mode != mFlowMapKey:
yieldUnexpectedToken()
else:
2015-12-11 21:55:21 +00:00
level.mode = mFlowMapValue
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-23 11:35:07 +00:00
state = ypFlow
of tComma:
2015-12-11 21:55:21 +00:00
case level.mode
of mFlowSequenceItem:
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-23 11:35:07 +00:00
state = ypFlow
2015-12-11 21:55:21 +00:00
of mFlowMapValue:
level.mode = mFlowMapKey
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
2015-12-23 11:35:07 +00:00
state = ypFlow
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of tClosingBrace:
2015-12-11 21:55:21 +00:00
if level.mode != mFlowMapValue:
yieldUnexpectedToken()
else:
yield YamlStreamEvent(kind: yamlEndMap)
2015-12-11 21:55:21 +00:00
if ancestry.len > 0:
level = ancestry.pop()
case level.mode
of mFlowMapKey, mFlowMapValue, mFlowSequenceItem:
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypExpectingDocumentEnd
of tClosingBracket:
2015-12-11 21:55:21 +00:00
if level.mode != mFlowSequenceItem:
yieldUnexpectedToken()
else:
yield YamlStreamEvent(kind: yamlEndSequence)
2015-12-11 21:55:21 +00:00
if ancestry.len > 0:
level = ancestry.pop()
case level.mode
of mFlowMapKey, mFlowMapValue, mFlowSequenceItem:
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
2015-12-11 21:55:21 +00:00
else:
2015-12-23 11:35:07 +00:00
state = ypExpectingDocumentEnd
2015-12-11 21:55:21 +00:00
else:
yieldUnexpectedToken()
2015-12-23 11:35:07 +00:00
of ypExpectingDocumentEnd:
2015-12-11 21:55:21 +00:00
case token
2015-12-23 11:35:07 +00:00
of tComment, tLineStart:
2015-12-11 21:55:21 +00:00
discard
2015-12-23 11:35:07 +00:00
of tStreamEnd, tDocumentEnd:
2015-12-21 22:10:42 +00:00
yieldDocumentEnd()
2015-12-23 11:35:07 +00:00
state = ypInitial
of tDirectivesEnd:
2015-12-21 22:10:42 +00:00
yieldDocumentEnd()
2015-12-23 11:35:07 +00:00
state = ypAfterDirectivesEnd
2015-12-11 21:55:21 +00:00
continue
else:
yieldUnexpectedToken("document end")
token = nextToken(lex)
if token == tError:
2016-01-07 10:49:01 +00:00
raiseError("Lexer error: " & lex.content)