FastParser: Implemented flow style

* still missing: block scalars and explicit map keys
This commit is contained in:
Felix Krause 2016-01-20 23:38:39 +01:00
parent 55d5cfcbf9
commit bb5804cd15
1 changed files with 314 additions and 40 deletions

View File

@ -1,7 +1,8 @@
type type
FastParseState = enum FastParseState = enum
fpInitial, fpBlockLineStart, fpBlockAfterScalar, fpBlockAfterPlainScalar, fpInitial, fpBlockLineStart, fpBlockAfterScalar, fpBlockAfterPlainScalar,
fpBlockObjectStart, fpBlockContinueScalar, fpExpectDocEnd fpBlockObjectStart, fpBlockContinueScalar, fpExpectDocEnd, fpFlow,
fpFlowAfterObject
FastParseLevelKind = enum FastParseLevelKind = enum
fplUnknown, fplSequence, fplMapKey, fplMapValue, fplScalar fplUnknown, fplSequence, fplMapKey, fplMapValue, fplScalar
@ -166,10 +167,15 @@ template initDocValues() {.dirty.} =
shorthands["!!"] = "tag:yaml.org,2002:" shorthands["!!"] = "tag:yaml.org,2002:"
nextAnchorId = 0.AnchorId nextAnchorId = 0.AnchorId
level = FastParseLevel(kind: fplUnknown, indentation: -1) level = FastParseLevel(kind: fplUnknown, indentation: -1)
tag = yTagQuestionmark
objectTag = yTagQuestionmark
anchor = yAnchorNone
objectAnchor = yAnchorNone
template applyObjectProperties() {.dirty.} = template applyObjectProperties() {.dirty.} =
if objectTag != yTagQuestionmark: if objectTag != yTagQuestionmark:
if cachedScalar.scalarTag != yTagQuestionmark: if cachedScalar.scalarTag != yTagQuestionmark:
debug("cached = " & $cachedScalar.scalarTag & ", object = " & $objectTag)
raiseError("Only one tag is allowed per node") raiseError("Only one tag is allowed per node")
else: else:
cachedScalar.scalarTag = objectTag cachedScalar.scalarTag = objectTag
@ -181,6 +187,60 @@ template applyObjectProperties() {.dirty.} =
cachedScalar.scalarAnchor = objectAnchor cachedScalar.scalarAnchor = objectAnchor
objectAnchor = yAnchorNone objectAnchor = yAnchorNone
template handleTagHandle() {.dirty.} =
if tag != yTagQuestionmark:
raiseError("Only one tag handle is allowed per node")
content = ""
var
shorthandEnd: int
tagUri: string
lexer.tagHandle(content, shorthandEnd)
if shorthandEnd != -1:
try:
let prefix = shorthands[content[0..shorthandEnd]]
tagUri = prefix & content[shorthandEnd + 1 .. ^1]
except KeyError:
raiseError("Undefined tag shorthand: " & content[0..shorthandEnd])
else:
shallowCopy(tagUri, content)
try:
tag = tagLib.tags[tagUri]
except KeyError:
tag = tagLib.registerUri(tagUri)
template handleAnchor() {.dirty.} =
if anchor != yAnchorNone:
raiseError("Only one anchor is allowed per node", lexer.bufpos)
content = ""
lexer.anchorName(content)
anchor = nextAnchorId
anchors[content] = anchor
nextAnchorId = cast[AnchorId](cast[int](nextAnchorId) + 1)
template handleAlias() {.dirty.} =
if anchor != yAnchorNone or tag != yTagQuestionmark:
raiseError("Alias may not have anchor or tag")
content = ""
lexer.anchorName(content)
try:
cachedScalar = aliasEvent(anchors[content])
except KeyError:
raiseError("Unknown anchor")
state = fpBlockAfterScalar
template leaveFlowLevel() {.dirty.} =
flowdepth.inc(-1)
if ancestry.len == 0:
state = fpExpectDocEnd
else:
level = ancestry.pop()
if flowdepth == 0:
lexer.lineEnding()
handleLineEnd(true)
state = fpBlockLineStart
else:
state = fpFlowAfterObject
template finishLine(lexer: FastLexer) = template finishLine(lexer: FastLexer) =
debug("lex: finishLine") debug("lex: finishLine")
while lexer.buf[lexer.bufpos] notin lineEnd: while lexer.buf[lexer.bufpos] notin lineEnd:
@ -190,6 +250,19 @@ template skipWhitespace(lexer: FastLexer) =
debug("lex: skipWhitespace") debug("lex: skipWhitespace")
while lexer.buf[lexer.bufpos] in space: lexer.bufpos.inc() while lexer.buf[lexer.bufpos] in space: lexer.bufpos.inc()
template skipWhitespaceAndNewlines(lexer: FastLexer) =
debug("lex: skipWhitespaceAndNewLines")
while true:
case lexer.buf[lexer.bufpos]
of space:
lexer.bufpos.inc()
of '\x0A':
lexer.bufpos = lexer.handleLF(lexer.bufpos)
of '\c':
lexer.bufpos = lexer.handleCR(lexer.bufpos)
else:
break
template skipIndentation(lexer: FastLexer) = template skipIndentation(lexer: FastLexer) =
debug("lex: skipIndentation") debug("lex: skipIndentation")
while lexer.buf[lexer.bufpos] == ' ': lexer.bufpos.inc() while lexer.buf[lexer.bufpos] == ' ': lexer.bufpos.inc()
@ -475,7 +548,7 @@ template plainScalar(lexer: FastLexer, content: var string,
content.add(c2) content.add(c2)
break break
of flowIndicators: of flowIndicators:
if context in [cFlowOut, cBlockKey]: if context in [cBlockOut, cBlockIn, cBlockKey]:
content.add(c) content.add(c)
else: else:
break break
@ -495,6 +568,43 @@ template continueMultilineScalar() {.dirty.} =
lexer.plainScalar(cachedScalar.scalarContent, cBlockOut) lexer.plainScalar(cachedScalar.scalarContent, cBlockOut)
state = fpBlockAfterPlainScalar state = fpBlockAfterPlainScalar
template handleFlowPlainScalar() {.dirty.} =
content = ""
lexer.plainScalar(content, cFlowOut)
if lexer.buf[lexer.bufpos] in ['{', '}', '[', ']', ',', ':', '#']:
discard
else:
var newlines = 0
while true:
case lexer.buf[lexer.bufpos]
of ':':
raiseError("Multiline scalar is not allowed as implicit key")
of '#', EndOfFile:
break
of '\x0A':
lexer.bufpos = lexer.handleLF(lexer.bufpos)
newlines.inc()
of '\c':
lexer.bufpos = lexer.handleCR(lexer.bufpos)
newlines.inc()
of flowIndicators:
break
of ' ', '\t':
lexer.skipWhitespace()
else:
if newlines == 1:
content.add(' ')
newlines = 0
elif newlines > 1:
content.add(repeat(' ', newlines - 1))
newlines = 0
lexer.plainScalar(content, cFlowOut)
yield scalarEvent(content, tag, anchor)
tag = yTagQuestionMark
anchor = yAnchorNone
level = ancestry.pop()
state = fpFlowAfterObject
template tagHandle(lexer: var FastLexer, content: var string, template tagHandle(lexer: var FastLexer, content: var string,
shorthandEnd: var int) = shorthandEnd: var int) =
debug("lex: tagHandle") debug("lex: tagHandle")
@ -561,13 +671,14 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
anchors: Table[string, AnchorId] anchors: Table[string, AnchorId]
nextAnchorId: AnchorId nextAnchorId: AnchorId
content: string content: string
tag, objectTag: TagId = yTagQuestionMark tag, objectTag: TagId
anchor, objectAnchor: AnchorId = yAnchorNone anchor, objectAnchor: AnchorId
ancestry = newSeq[FastParseLevel]() ancestry = newSeq[FastParseLevel]()
level: FastParseLevel level: FastParseLevel
cachedScalar: YamlStreamEvent cachedScalar: YamlStreamEvent
indentation: int indentation: int
newlines: int newlines: int
flowdepth: int = 0
lexer.open(s) lexer.open(s)
initDocValues() initDocValues()
@ -854,43 +965,14 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
lexer.bufpos.inc() lexer.bufpos.inc()
handleStartBlockSequence() handleStartBlockSequence()
of '!': of '!':
if tag != yTagQuestionmark: handleTagHandle()
raiseError("Only one tag handle is allowed per node")
content = ""
var
shorthandEnd: int
tagUri: string
lexer.tagHandle(content, shorthandEnd)
if shorthandEnd != -1:
try:
let prefix = shorthands[content[0..shorthandEnd]]
tagUri = prefix & content[shorthandEnd + 1 .. ^1]
except KeyError:
raiseError("Undefined tag shorthand: " & content[0..shorthandEnd])
else:
shallowCopy(tagUri, content)
try:
tag = tagLib.tags[tagUri]
except KeyError:
tag = tagLib.registerUri(tagUri)
of '&': of '&':
if anchor != yAnchorNone: handleAnchor()
raiseError("Only one anchor is allowed per node", lexer.bufpos)
content = ""
lexer.anchorName(content)
anchor = nextAnchorId
anchors[content] = anchor
nextAnchorId = cast[AnchorId](cast[int](nextAnchorId) + 1)
of '*': of '*':
if anchor != yAnchorNone or tag != yTagQuestionmark: handleAlias()
raiseError("Alias may not have anchor or tag") of '[', '{':
content = "" applyObjectProperties()
lexer.anchorName(content) state = fpFlow
try:
cachedScalar = aliasEvent(anchors[content])
except KeyError:
raiseError("Unknown anchor")
state = fpBlockAfterScalar
else: else:
handleStartBlockScalar() handleStartBlockScalar()
content = "" content = ""
@ -898,4 +980,196 @@ proc fastparse*(tagLib: TagLibrary, s: Stream): YamlStream =
cachedScalar = scalarEvent(content, tag, anchor) cachedScalar = scalarEvent(content, tag, anchor)
state = fpBlockAfterPlainScalar state = fpBlockAfterPlainScalar
of fpExpectDocEnd: of fpExpectDocEnd:
discard # TODO case lexer.buf[lexer.bufpos]
of '-':
var token: LexedPossibleDirectivesEnd
content = ""
lexer.directivesEnd(content, token)
case token
of lpdeDirectivesEnd:
yield endDocEvent()
initDocValues()
yield startDocEvent()
state = fpBlockObjectStart
else:
raiseError("Unexpected content (expected document end)")
of '.':
var isDocumentEnd: bool
content = ""
lexer.documentEnd(content, isDocumentEnd)
if isDocumentEnd:
yield endDocEvent()
initDocValues()
state = fpInitial
else:
raiseError("Unexpected content (expected document end)")
of ' ', '\t', '#':
lexer.lineEnding()
handleLineEnd(true)
of '\x0A':
lexer.bufpos = lexer.handleLF(lexer.bufpos)
of '\c':
lexer.bufpos = lexer.handleCR(lexer.bufpos)
of EndOfFile:
yield endDocEvent()
break
else:
raiseError("Unexpected content (expected document end)")
of fpFlow:
lexer.skipWhitespaceAndNewlines()
case lexer.buf[lexer.bufpos]
of '{':
assert(level.kind == fplUnknown)
yield startMapEvent(tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
flowdepth.inc()
level.kind = fplMapKey
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
lexer.bufpos.inc()
of '[':
assert(level.kind == fplUnknown)
yield startSeqEvent(tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
flowdepth.inc()
level.kind = fplSequence
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
lexer.bufpos.inc()
of '}':
assert(level.kind == fplUnknown)
level = ancestry.pop()
case level.kind
of fplMapValue:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
of fplMapKey:
discard
of fplSequence:
raiseError("Unexpected token (expected ']')", lexer.bufpos)
of fplUnknown, fplScalar:
assert(false)
yield endMapEvent()
leaveFlowLevel()
lexer.bufpos.inc()
of ']':
assert(level.kind == fplUnknown)
level = ancestry.pop()
case level.kind
of fplSequence:
yield endSeqEvent()
of fplMapKey, fplMapValue:
raiseError("Unexpected token (expected '}')", lexer.bufpos)
of fplUnknown, fplScalar:
assert(false)
leaveFlowLevel()
lexer.bufpos.inc()
of ',':
assert(level.kind == fplUnknown)
level = ancestry.pop()
case level.kind
of fplSequence:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
of fplMapValue:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
level.kind = fplMapKey
of fplMapKey:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
yield scalarEvent("", tag, anchor)
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
lexer.bufpos.inc()
of ':':
assert(level.kind == fplUnknown)
if lexer.isPlainSafe(lexer.bufpos + 1, cFlowIn):
level = ancestry.pop()
case level.kind
of fplSequence, fplMapValue:
raiseError("Unexpected token (expected ',')", lexer.bufpos)
of fplMapKey:
yield scalarEvent("", tag, anchor)
tag = yTagQuestionmark
anchor = yAnchorNone
level.kind = fplMapValue
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
lexer.bufpos.inc()
else:
handleFlowPlainScalar()
of '!':
handleTagHandle()
of '&':
handleAnchor()
of '*':
handleAlias()
else:
handleFlowPlainScalar()
of fpFlowAfterObject:
lexer.skipWhitespaceAndNewlines()
case lexer.buf[lexer.bufpos]
of ']':
case level.kind
of fplSequence:
yield endSeqEvent()
of fplMapKey, fplMapValue:
raiseError("Unexpected token (expected '}')", lexer.bufpos)
of fplScalar, fplUnknown:
assert(false)
leaveFlowLevel()
of '}':
case level.kind
of fplSequence:
raiseError("Unexpected token (expected ']')", lexer.bufpos)
of fplMapKey:
yield scalarEvent("", yTagQuestionmark, yAnchorNone)
of fplMapValue:
discard
of fplUnknown, fplScalar:
assert(false)
yield endMapEvent()
leaveFlowLevel()
of ',':
case level.kind
of fplSequence:
discard
of fplMapKey:
yield scalarEvent("", yTagQuestionmark, yAnchorNone)
of fplMapValue:
level.kind = fplMapKey
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
state = fpFlow
of ':':
case level.kind
of fplSequence, fplMapValue:
raiseError("Unexpected token (expected ',')", lexer.bufpos)
of fplMapKey:
level.kind = fplMapValue
of fplUnknown, fplScalar:
assert(false)
ancestry.add(level)
level = FastParseLevel(kind: fplUnknown)
state = fpFlow
of '#':
lexer.lineEnding()
of EndOfFile:
raiseError("Unclosed flow content", lexer.bufpos)
else:
raiseError("Unexpected content (expected flow indicator)",
lexer.bufpos)
lexer.bufpos.inc()