mirror of
https://github.com/status-im/NimYAML.git
synced 2025-01-26 19:19:24 +00:00
Added sequential parser
This commit is contained in:
parent
26c1c07567
commit
0544a8bba0
215
src/yaml/sequential.nim
Normal file
215
src/yaml/sequential.nim
Normal file
@ -0,0 +1,215 @@
|
||||
import streams, tables, strutils
|
||||
|
||||
import private.lexer
|
||||
|
||||
type
|
||||
YamlParserEventKind* = enum
|
||||
yamlStartDocument, yamlEndDocument, yamlStartMap, yamlEndMap,
|
||||
yamlStartSequence, yamlEndSequence, yamlScalar, yamlAlias,
|
||||
yamlError, yamlWarning
|
||||
|
||||
YamlParserEvent* = ref object
|
||||
case kind*: YamlParserEventKind
|
||||
of yamlStartMap, yamlStartSequence:
|
||||
objAnchor* : string # may be nil, may not be empty
|
||||
objTag* : string # may not be nil or empty, is a complete URI.
|
||||
of yamlScalar:
|
||||
scalarAnchor* : string # may be nil
|
||||
scalarTag* : string # may not be nil, is a complete URI.
|
||||
scalarContent*: string # may not be nil (but empty)
|
||||
of yamlEndMap, yamlEndSequence, yamlStartDocument, yamlEndDocument:
|
||||
discard
|
||||
of yamlAlias:
|
||||
aliasName* : string # may not be nil nor empty
|
||||
of yamlError, yamlWarning:
|
||||
description* : string
|
||||
line* : int
|
||||
column* : int
|
||||
|
||||
YamlParserState = enum
|
||||
ylInitial, ylSkipDirective, ylBlock, ylFlow
|
||||
|
||||
OutcomeEnum = enum
|
||||
oOkay, oWarn, oContinue
|
||||
|
||||
LevelKind = enum
|
||||
lUnknown, lSequence, lMap
|
||||
|
||||
DocumentLevel = object
|
||||
kind: LevelKind
|
||||
indicatorColumn: int
|
||||
readKey: bool
|
||||
anchor: string
|
||||
tag: string
|
||||
|
||||
template yieldWarning(d: string) {.dirty.} =
|
||||
yield YamlParserEvent(kind: yamlWarning, description: d,
|
||||
line: lex.line, column: lex.column)
|
||||
|
||||
template yieldError(d: string) {.dirty.} =
|
||||
yield YamlParserEvent(kind: yamlError, description: d,
|
||||
line: lex.line, column: lex.column)
|
||||
|
||||
template tag(): string {.dirty.} =
|
||||
if isNil(level.tag):
|
||||
case level.kind
|
||||
of lUnknown:
|
||||
result = "!!str"
|
||||
of lSequence:
|
||||
result = "!!seq"
|
||||
of lMap:
|
||||
result = "!!map"
|
||||
else:
|
||||
return level.tag
|
||||
|
||||
template closeLevel() {.dirty.} =
|
||||
case level.kind
|
||||
of lUnknown:
|
||||
yield YamlParserEvent(kind: yamlScalar, scalarAnchor: level.anchor,
|
||||
scalarTag: tag(), scalarContent: "")
|
||||
of lSequence:
|
||||
yield YamlParserEvent(kind: yamlEndSequence)
|
||||
of lMap:
|
||||
yield YamlParserEvent(kind: yamlEndMap)
|
||||
|
||||
template closeLevelsByIndicator() {.dirty.} =
|
||||
while levels.len > 0:
|
||||
let level = levels[levels.high]
|
||||
if level.indicatorColumn > lex.column:
|
||||
closeLevel()
|
||||
else:
|
||||
break
|
||||
levels.pop()
|
||||
|
||||
iterator events*(input: Stream): YamlParserEvent =
|
||||
var
|
||||
state = ylInitial
|
||||
lex : YamlLexer
|
||||
foundYamlDirective = false
|
||||
tagShorthands = initTable[string, string]()
|
||||
levels = initSeq[DocumentLevel]()
|
||||
curIndentation: int
|
||||
lex.open(input)
|
||||
|
||||
var nextToken = lexer.tokens
|
||||
var token = nextToken(lex)
|
||||
while not finished(nextToken):
|
||||
case state
|
||||
of ylInitial:
|
||||
case token.kind
|
||||
of yamlYamlDirective:
|
||||
if foundYamlDirective:
|
||||
yield YamlParserEvent(kind: yamlError,
|
||||
description: "Duplicate %YAML tag",
|
||||
line: lex.line,
|
||||
column: lex.column)
|
||||
state = ylSkipDirective
|
||||
else:
|
||||
var
|
||||
outcome = oOkay
|
||||
actualVersion = ""
|
||||
for version in [1, 2]:
|
||||
token = nextToken(lex)
|
||||
if finished(nextToken):
|
||||
yieldError("Missing or badly formatted YAML version")
|
||||
outcome = oContinue
|
||||
break
|
||||
if token.kind != yamlVersionPart:
|
||||
yieldError("Missing or badly formatted YAML version")
|
||||
outcome = oContinue
|
||||
break
|
||||
if parseInt(lex.content) != version:
|
||||
outcome = oWarn
|
||||
if actualVersion.len > 0: actualVersion &= "."
|
||||
actualVersion &= $version
|
||||
case outcome
|
||||
of oContinue:
|
||||
continue
|
||||
of oWarn:
|
||||
yieldWarning("Unsupported version: " & actualVersion &
|
||||
", trying to parse anyway")
|
||||
else:
|
||||
discard
|
||||
foundYamlDirective = true
|
||||
of yamlTagDirective:
|
||||
token = nextToken(lex)
|
||||
if finished(nextToken):
|
||||
yieldError("Incomplete %TAG directive")
|
||||
continue
|
||||
if token.kind != yamlTagHandle:
|
||||
yieldError("Invalid token (expected tag handle)")
|
||||
state = ylSkipDirective
|
||||
continue
|
||||
let tagHandle = lex.content
|
||||
token = nextToken(lex)
|
||||
if finished(nextToken):
|
||||
yieldError("Incomplete %TAG directive")
|
||||
continue
|
||||
if token.kind != yamlTagURI:
|
||||
yieldError("Invalid token (expected tag URI)")
|
||||
state = ylSkipDirective
|
||||
continue
|
||||
tagShorthands[tagHandle] = lex.content
|
||||
of yamlUnknownDirective:
|
||||
yieldWarning("Unknown directive: " & lex.content)
|
||||
state = ylSkipDirective
|
||||
of yamlComment:
|
||||
discard
|
||||
of yamlDirectivesEnd:
|
||||
yield YamlParserEvent(kind: yamlStartDocument)
|
||||
state = ylLineStart
|
||||
of yamlDocumentEnd:
|
||||
yield YamlParserEvent(kind: yamlStartDocument)
|
||||
yield YamlParserEvent(kind: yamlEndDocument)
|
||||
else:
|
||||
yield YamlParserEvent(kind: yamlStartDocument)
|
||||
state = ylLineStart
|
||||
continue
|
||||
of ylSkipDirective:
|
||||
if token.kind not in [yamlUnknownDirectiveParam, yamlTagHandle,
|
||||
yamlTagURI, yamlVersionPart, yamlComment]:
|
||||
state = ylInitial
|
||||
continue
|
||||
of ylBlock:
|
||||
case token.kind
|
||||
of yamlLineStart:
|
||||
discard
|
||||
of yamlDash:
|
||||
closeLevelsByIndicator()
|
||||
if levels.count > 0:
|
||||
let level = levels[levels.high]
|
||||
if level.kind == lUnknown:
|
||||
level.kind = lSequence
|
||||
level.indicatorColumn = lex.column
|
||||
levels.add(DocumentLevel(kind: lUnknown,
|
||||
indicatorColumn = -1,
|
||||
readKey: false,
|
||||
anchor: nil, tag: nil))
|
||||
elif level.indicatorColumn < lex.column:
|
||||
yieldError("Invalid indentation for '-'")
|
||||
elif level.kind == lSequence:
|
||||
levels.add(DocumentLevel(kind: lUnknown,
|
||||
indicatorColumn: -1,
|
||||
readKey: false,
|
||||
anchor: nil, tag: nil))
|
||||
else:
|
||||
yieldError("Unexpected token: '-'")
|
||||
else:
|
||||
levels.add(DocumentLevel(kind: lSequence,
|
||||
indicatorColumn: lex.column,
|
||||
readKey: false,
|
||||
anchor: nil, tag: nil))
|
||||
levels.add(DocumentLevel(kind: lUnknown,
|
||||
indicatorColmun: -1,
|
||||
readKey: false,
|
||||
anchor: nil, tag: nil))
|
||||
of yamlQuestionmark:
|
||||
|
||||
of yamlColon:
|
||||
|
||||
|
||||
else:
|
||||
discard
|
||||
|
||||
|
||||
token = nextToken(lex)
|
Loading…
x
Reference in New Issue
Block a user