NimYAML/private/sequential.nim

1004 lines
39 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,
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 newParser*(tagLib: YamlTagLibrary): YamlSequentialParser =
2015-12-23 11:35:07 +00:00
new(result)
result.tagLib = tagLib
result.anchors = initOrderedTable[string, AnchorId]()
proc anchor*(parser: YamlSequentialParser, id: AnchorId): string =
for pair in parser.anchors.pairs:
if pair[1] == id:
return pair[0]
return nil
proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool =
if left.kind != right.kind:
return false
case left.kind
of yamlStartDocument, yamlEndDocument, yamlEndMap, yamlEndSequence:
result = true
of yamlStartMap:
result = left.mapAnchor == right.mapAnchor and
left.mapTag == right.mapTag
of yamlStartSequence:
result = left.seqAnchor == right.seqAnchor and
left.seqTag == right.seqTag
of yamlScalar:
result = left.scalarAnchor == right.scalarAnchor and
left.scalarTag == right.scalarTag and
left.scalarContent == right.scalarContent and
left.scalarType == right.scalarType
of yamlAlias:
result = left.aliasTarget == right.aliasTarget
of yamlError, yamlWarning:
result = left.description == right.description and
left.line == right.line and left.column == right.column
proc `$`*(event: YamlStreamEvent): string =
result = $event.kind & '('
case event.kind
of yamlEndMap, yamlEndSequence, yamlStartDocument, yamlEndDocument:
discard
of yamlStartMap:
result &= "tag=" & $event.mapTag
2015-12-27 22:40:27 +00:00
if event.mapAnchor != yAnchorNone:
result &= ", anchor=" & $event.mapAnchor
of yamlStartSequence:
result &= "tag=" & $event.seqTag
2015-12-27 22:40:27 +00:00
if event.seqAnchor != yAnchorNone:
result &= ", anchor=" & $event.seqAnchor
of yamlScalar:
result &= "tag=" & $event.scalarTag
2015-12-27 22:40:27 +00:00
if event.scalarAnchor != yAnchorNone:
result &= ", anchor=" & $event.scalarAnchor
result &= ", typeHint=" & $event.scalarType
result &= ", content=\"" & event.scalarContent & '\"'
of yamlAlias:
result &= "aliasTarget=" & $event.aliasTarget
of yamlWarning, yamlError:
result &= "line=" & $event.line & ", column=" & $event.column
result &= ", description=\"" & event.description & '\"'
result &= ")"
2015-12-05 11:10:17 +00:00
template yieldWarning(d: string) {.dirty.} =
yield YamlStreamEvent(kind: yamlWarning, description: d,
2015-12-05 11:10:17 +00:00
line: lex.line, column: lex.column)
template yieldError(d: string) {.dirty.} =
yield YamlStreamEvent(kind: yamlError, description: d,
2015-12-05 11:10:17 +00:00
line: lex.line, column: lex.column)
2015-12-11 21:55:21 +00:00
break parserLoop
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)
yieldError(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, typeHint: YamlTypeHint,
quoted: bool = false) {.dirty.} =
when defined(yamlDebug):
echo "Parser token [mode=", level.mode, ", state=", state, "]: ",
"scalar[\"", content, "\", type=", typeHint, "]"
if objectTag.len > 0:
if tag.len > 0:
yieldError("Duplicate tag for scalar")
tag = objectTag
yield YamlStreamEvent(kind: yamlScalar,
scalarAnchor: resolveAnchor(parser, anchor),
scalarTag: resolveTag(parser, tag, quoted),
scalarContent: content,
scalarType: typeHint)
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["!!"] = "tag:yaml.org,2002:"
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("", yTypeUnknown)
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, "\", type=", scalarCacheType, "]"
yield YamlStreamEvent(kind: yamlScalar,
scalarAnchor: resolveAnchor(parser, anchor),
scalarTag: resolveTag(parser, tag),
scalarContent: scalarCache,
scalarType: scalarCacheType)
2015-12-11 21:55:21 +00:00
else:
yieldScalar("", yTypeUnknown)
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("", yTypeUnknown)
2015-12-22 21:28:27 +00:00
level.mode = next
ancestry.add(level)
level = DocumentLevel(mode: mUnknown, indicatorColumn: -1,
indentationColumn: -1)
else:
yieldError("Invalid token after " & $level.mode)
else:
yieldError("Invalid token after " & $level.mode)
2015-12-11 21:55:21 +00:00
elif level.mode != mUnknown:
yieldError("Invalid indentation")
elif entering == yamlError:
yieldUnexpectedToken()
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
yieldScalar("", yTypeUnknown)
2015-12-22 22:35:03 +00:00
else:
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
scalarCacheType = lex.typeHint
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):
yieldError("Missing tag suffix")
continue
2015-12-23 11:35:07 +00:00
if token != tTagSuffix:
yieldError("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:
yieldError("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
scalarCacheType: YamlTypeHint
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["!!"] = "tag:yaml.org,2002:"
2015-12-05 11:10:17 +00:00
var nextToken = tokens
2015-12-05 11:10:17 +00:00
var token = nextToken(lex)
2015-12-11 21:55:21 +00:00
block parserLoop:
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:
2015-12-11 21:55:21 +00:00
yieldError("Duplicate %YAML directive")
var
warn = false
actualVersion = ""
for version in [1, 2]:
token = nextToken(lex)
if finished(nextToken):
yieldError("Missing or badly formatted YAML version")
2015-12-23 11:35:07 +00:00
if token != tVersionPart:
2015-12-11 21:55:21 +00:00
yieldError("Missing or badly formatted YAML version")
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):
yieldError("Incomplete %TAG directive")
2015-12-23 11:35:07 +00:00
if token != tTagHandle:
2015-12-05 11:10:17 +00:00
yieldError("Invalid token (expected tag handle)")
let tagHandle = lex.content
token = nextToken(lex)
if finished(nextToken):
yieldError("Incomplete %TAG directive")
2015-12-23 11:35:07 +00:00
if token != tTagURI:
2015-12-05 11:10:17 +00:00
yieldError("Invalid token (expected tag URI)")
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:
if objectTag.len > 0:
yieldError("Duplicate tag for object")
else:
objectTag = tag
tag = ""
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
scalarCacheType = lex.typeHint
scalarCacheIsQuoted = false
scalarIndentation = lex.column
of mBlockMapValue:
scalarCache = lex.content
scalarCacheType = lex.typeHint
scalarCacheIsQuoted = false
scalarIndentation = lex.column
2015-12-22 21:28:27 +00:00
level.mode = mImplicitBlockMapKey
of mExplicitBlockMapKey:
yieldScalar("", yTypeUnknown)
2015-12-22 21:28:27 +00:00
level.mode = mBlockMapValue
continue
else:
2015-12-22 21:28:27 +00:00
yieldError("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
scalarCacheType = yTypeString
scalarCacheIsQuoted = true
scalarIndentation = lex.column
2015-12-23 11:35:07 +00:00
state = ypBlockAfterScalar
2015-12-11 21:55:21 +00:00
else:
yieldError("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:
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:
2015-12-23 11:35:07 +00:00
state = ypBlockAfterAlias
else:
yieldError("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
scalarCacheType = yTypeUnknown
2015-12-23 11:35:07 +00:00
state = ypBlockLineEnd
of tLineStart:
discard
2015-12-23 11:35:07 +00:00
of tColon, tDash, tQuestionmark:
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, scalarCacheType, 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:
yieldError("Missing colon after implicit map key")
if level.mode != mScalar:
yieldScalar(scalarCache, scalarCacheType,
scalarCacheIsQuoted)
scalarCache = nil
if ancestry.len > 0:
level = ancestry.pop()
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, scalarCacheType, 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:
yieldError("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:
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:
discard
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, yTypeUnknown, 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
yieldError("[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:
yieldError("Multiple chomping indicators!")
else:
lineStrip = lsKeep
2015-12-23 11:35:07 +00:00
of tDash:
2015-12-17 20:44:41 +00:00
if lineStrip != lsClip:
yieldError("Multiple chomping indicators!")
else:
lineStrip = lsStrip
2015-12-23 11:35:07 +00:00
of tBlockIndentationIndicator:
2015-12-17 20:44:41 +00:00
if blockScalarIndentation != -1:
yieldError("Multiple indentation indicators!")
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:
if blockScalar == bsFolded:
if blockScalarTrailing == " ":
blockScalarTrailing = "\x0A"
scalarCache &= blockScalarTrailing &
lex.content[level.indentationColumn..^1]
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, yTypeUnknown, true)
level = ancestry.pop()
state = ypFlowAfterObject
of tScalarPart:
yieldScalar(lex.content, lex.typeHint)
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
2015-12-23 11:35:07 +00:00
state = ypFlowAfterObject
of tColon:
yieldScalar("", yTypeUnknown)
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")
2015-12-23 11:35:07 +00:00
of tComma:
yieldScalar("", yTypeUnknown)
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("", yTypeUnknown)
2015-12-11 21:55:21 +00:00
else:
yieldError("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
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
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("", yTypeUnknown)
2015-12-11 21:55:21 +00:00
level = ancestry.pop()
if level.mode != mFlowMapValue:
yieldUnexpectedToken()
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("", yTypeUnknown)
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()
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:
2015-12-21 22:10:42 +00:00
yieldError("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:
2015-12-21 22:10:42 +00:00
yieldError("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:
2015-12-21 22:10:42 +00:00
yieldError("Multiple anchors on same node!")
2015-12-23 11:35:07 +00:00
of tTagHandle:
2015-12-21 22:10:42 +00:00
yieldError("Multiple tags on same node!")
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")
2015-12-05 11:10:17 +00:00
token = nextToken(lex)