mirror of https://github.com/status-im/NimYAML.git
Got rid of yamlWarning, use YamlPresenterError
* Added YamlWarningCallback that may be used to capture parser warnings * Removed yamlWarning as event kind * Replaced checks for well-formedness in presenter with asserts * Added checks for JSON compatibility of YamlStream in presenter * Added proper type hints for special float values in serializer to be able to check for them in the presenter
This commit is contained in:
parent
d2baa1749e
commit
18753b1a4a
|
@ -23,9 +23,6 @@ proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool =
|
|||
left.scalarType == right.scalarType
|
||||
of yamlAlias:
|
||||
result = left.aliasTarget == right.aliasTarget
|
||||
of yamlWarning:
|
||||
result = left.description == right.description and
|
||||
left.line == right.line and left.column == right.column
|
||||
|
||||
proc `$`*(event: YamlStreamEvent): string =
|
||||
result = $event.kind & '('
|
||||
|
@ -48,9 +45,6 @@ proc `$`*(event: YamlStreamEvent): string =
|
|||
result &= ", content=\"" & event.scalarContent & '\"'
|
||||
of yamlAlias:
|
||||
result &= "aliasTarget=" & $event.aliasTarget
|
||||
of yamlWarning:
|
||||
result &= "line=" & $event.line & ", column=" & $event.column
|
||||
result &= ", description=\"" & event.description & '\"'
|
||||
result &= ")"
|
||||
|
||||
proc startDocEvent*(): YamlStreamEvent =
|
||||
|
|
|
@ -138,9 +138,6 @@ proc constructJson*(s: YamlStream): seq[JsonNode] =
|
|||
discard # will never happen
|
||||
else:
|
||||
discard # wait for yamlEndDocument
|
||||
of yamlWarning:
|
||||
echo "YAML warning at line ", event.line, ", column ", event.column,
|
||||
": ", event.description
|
||||
of yamlAlias:
|
||||
# we can savely assume that the alias exists in anchors
|
||||
# (else the parser would have already thrown an exception)
|
||||
|
|
|
@ -57,6 +57,10 @@ proc newParser*(tagLib: YamlTagLibrary): YamlSequentialParser =
|
|||
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:
|
||||
|
@ -64,8 +68,8 @@ proc anchor*(parser: YamlSequentialParser, id: AnchorId): string =
|
|||
return nil
|
||||
|
||||
template yieldWarning(d: string) {.dirty.} =
|
||||
yield YamlStreamEvent(kind: yamlWarning, description: d,
|
||||
line: lex.line, column: lex.column)
|
||||
if parser.callback != nil:
|
||||
parser.callback(lex.line, lex.column, lex.getCurrentLine(), d)
|
||||
|
||||
template raiseError(message: string) {.dirty.} =
|
||||
var e = newException(YamlParserError, message)
|
||||
|
|
|
@ -155,13 +155,22 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
writeTagAndAnchor(target,
|
||||
item.scalarTag, tagLib, item.scalarAnchor)
|
||||
|
||||
if (style == ypsJson and item.scalarTag in [yTagQuestionMark,
|
||||
yTagBoolean] and
|
||||
item.scalarType in [yTypeBoolTrue, yTypeBoolFalse]):
|
||||
if item.scalarType == yTypeBoolTrue:
|
||||
target.write("true")
|
||||
if style == ypsJson:
|
||||
if item.scalarTag in [yTagQuestionMark, yTagBoolean] and
|
||||
item.scalarType in [yTypeBoolTrue, yTypeBoolFalse]:
|
||||
if item.scalarType == yTypeBoolTrue:
|
||||
target.write("true")
|
||||
else:
|
||||
target.write("false")
|
||||
elif item.scalarTag in [yTagQuestionMark, yTagNull] and
|
||||
item.scalarType == yTypeNull:
|
||||
target.write("null")
|
||||
elif item.scalarTag in [yTagQuestionMark, yTagFloat] and
|
||||
item.scalarType in [yTypeFloatInf, yTypeFloatNaN]:
|
||||
raise newException(YamlPresenterError,
|
||||
"Infinity and not-a-number values cannot be presented as JSON!")
|
||||
else:
|
||||
target.write("false")
|
||||
target.write(item.scalarContent)
|
||||
elif style == ypsCanonical or item.scalarContent.needsEscaping or
|
||||
(style == ypsJson and
|
||||
(item.scalarTag notin [yTagQuestionMark, yTagInteger, yTagFloat,
|
||||
|
@ -173,10 +182,8 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
else:
|
||||
target.write(item.scalarContent)
|
||||
of yamlAlias:
|
||||
if levels.len == 0:
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
else:
|
||||
startItem(target, style, indentation, levels[levels.high])
|
||||
assert levels.len > 0
|
||||
startItem(target, style, indentation, levels[levels.high])
|
||||
target.write('*')
|
||||
target.write(cast[byte]('a') + cast[byte](item.aliasTarget))
|
||||
of yamlStartSequence:
|
||||
|
@ -186,8 +193,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
var length = 0
|
||||
while true:
|
||||
let next = s()
|
||||
if finished(s):
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
assert (not finished(s))
|
||||
cached.enqueue(next)
|
||||
case next.kind
|
||||
of yamlScalar:
|
||||
|
@ -201,7 +207,13 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
break
|
||||
nextState = if length <= 60: dFlowSequenceStart else:
|
||||
dBlockSequenceItem
|
||||
of ypsMinimal, ypsJson, ypsCanonical:
|
||||
of ypsJson:
|
||||
if levels[levels.high] in
|
||||
[dFlowImplicitMapStart, dFlowImplicitMapValue]:
|
||||
raise newException(YamlPresenterError,
|
||||
"Cannot have sequence as map key in JSON output!")
|
||||
nextState = dFlowSequenceStart
|
||||
of ypsMinimal, ypsCanonical:
|
||||
nextState = dFlowSequenceStart
|
||||
of ypsBlockOnly:
|
||||
nextState = dBlockSequenceItem
|
||||
|
@ -240,8 +252,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
var length = 0
|
||||
while true:
|
||||
let next = s()
|
||||
if finished(s):
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
assert (not finished(s))
|
||||
cached.enqueue(next)
|
||||
case next.kind
|
||||
of yamlScalar:
|
||||
|
@ -262,6 +273,10 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
of ypsCanonical:
|
||||
nextState = dFlowExplicitMapStart
|
||||
of ypsJson:
|
||||
if levels[levels.high] in
|
||||
[dFlowImplicitMapStart, dFlowImplicitMapValue]:
|
||||
raise newException(YamlPresenterError,
|
||||
"Cannot have map as map key in JSON output!")
|
||||
nextState = dFlowImplicitMapStart
|
||||
of ypsBlockOnly:
|
||||
nextState = if item.mapMayHaveKeyObjects:
|
||||
|
@ -305,8 +320,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
levels.add(nextState)
|
||||
|
||||
of yamlEndSequence:
|
||||
if levels.len == 0:
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
assert levels.len > 0
|
||||
case levels.pop()
|
||||
of dFlowSequenceItem:
|
||||
case style
|
||||
|
@ -333,11 +347,10 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
of dBlockSequenceItem:
|
||||
discard
|
||||
else:
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
assert false
|
||||
indentation -= indentationStep
|
||||
of yamlEndMap:
|
||||
if levels.len == 0:
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
assert levels.len > 0
|
||||
case levels.pop()
|
||||
of dFlowImplicitMapValue, dFlowExplicitMapValue:
|
||||
case style
|
||||
|
@ -364,7 +377,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
of dBlockImplicitMapValue, dBlockExplicitMapValue:
|
||||
discard
|
||||
else:
|
||||
raise newException(ValueError, "Malformed YamlStream")
|
||||
assert false
|
||||
indentation -= indentationStep
|
||||
of yamlEndDocument:
|
||||
let next = s()
|
||||
|
@ -372,8 +385,6 @@ proc present*(s: YamlStream, target: Stream, tagLib: YamlTagLibrary,
|
|||
break
|
||||
target.write("...\x0A")
|
||||
cached.enqueue(next)
|
||||
of yamlWarning:
|
||||
discard
|
||||
|
||||
proc transform*(input: Stream, output: Stream, style: YamlPresentationStyle,
|
||||
indentationStep: int = 2) =
|
||||
|
|
|
@ -48,8 +48,6 @@ proc alias(target: AnchorId): YamlStreamEvent =
|
|||
proc printDifference(expected, actual: YamlStreamEvent) =
|
||||
if expected.kind != actual.kind:
|
||||
echo "expected " & $expected.kind & ", got " & $actual.kind
|
||||
if actual.kind == yamlWarning:
|
||||
echo "Warning message: " & actual.description
|
||||
else:
|
||||
case expected.kind
|
||||
of yamlScalar:
|
||||
|
@ -117,7 +115,7 @@ template ensure(input: string, expected: varargs[YamlStreamEvent]) {.dirty.} =
|
|||
break
|
||||
i.inc()
|
||||
except YamlParserError:
|
||||
let e = cast[YamlParserError](getCurrentException())
|
||||
let e = cast[ref YamlParserError](getCurrentException())
|
||||
echo "Parser error:", getCurrentExceptionMsg()
|
||||
echo e.lineContent
|
||||
fail()
|
||||
|
|
57
yaml.nim
57
yaml.nim
|
@ -50,7 +50,7 @@ type
|
|||
## Kinds of YAML events that may occur in an ``YamlStream``. Event kinds
|
||||
## are discussed in ``YamlStreamEvent``.
|
||||
yamlStartDocument, yamlEndDocument, yamlStartMap, yamlEndMap,
|
||||
yamlStartSequence, yamlEndSequence, yamlScalar, yamlAlias, yamlWarning
|
||||
yamlStartSequence, yamlEndSequence, yamlScalar, yamlAlias
|
||||
|
||||
TagId* = distinct int ## \
|
||||
## A ``TagId`` identifies a tag URI, like for example
|
||||
|
@ -107,24 +107,13 @@ type
|
|||
discard
|
||||
of yamlAlias:
|
||||
aliasTarget* : AnchorId
|
||||
of yamlWarning:
|
||||
description* : string
|
||||
line* : int
|
||||
column* : int
|
||||
|
||||
YamlStream* = iterator(): YamlStreamEvent ## \
|
||||
## A ``YamlStream`` is an iterator that yields a well-formed stream of
|
||||
## ``YamlStreamEvents``. Well-formed means that every ``yamlStartMap``
|
||||
## is terminated by a ``yamlEndMap``, every ``yamlStartSequence`` is
|
||||
## terminated by a ``yamlEndSequence`` and every ``yamlStartDocument``
|
||||
## is terminated by a ``yamlEndDocument``. The only exception to this
|
||||
## rule is a ``yamlError``, which may occur anywhere in the stream and
|
||||
## must be the last element in the stream, which may leave any number of
|
||||
## objects open.
|
||||
##
|
||||
## A ``yamlWarning`` may also occur anywhere in the stream, but will not
|
||||
## invalidate the structure of the event stream, and may not abruptly
|
||||
## end the stream as ``yamlError`` does.
|
||||
## is terminated by a ``yamlEndDocument``.
|
||||
##
|
||||
## The creator of a ``YamlStream`` is responsible for it being
|
||||
## well-formed. A user of the stream may assume that it is well-formed
|
||||
|
@ -157,6 +146,15 @@ type
|
|||
nextCustomTagId*: TagId
|
||||
secondaryPrefix*: string
|
||||
|
||||
YamlWarningCallback* = proc(line, column: int, lineContent: string,
|
||||
message: string)
|
||||
## Callback for parser warnings. Currently, this callback may be called
|
||||
## on two occasions while parsing a YAML document stream:
|
||||
##
|
||||
## - If the version number in the ``%YAML`` directive does not match
|
||||
## ``1.2``.
|
||||
## - If there is an unknown directive encountered.
|
||||
|
||||
YamlSequentialParser* = ref object
|
||||
## A parser object. Retains its ``YamlTagLibrary`` across calls to
|
||||
## `parse <#parse,YamlSequentialParser,Stream,YamlStream>`_. Can be used
|
||||
|
@ -165,6 +163,7 @@ type
|
|||
## ``yamlEndDocument`` is yielded).
|
||||
tagLib: YamlTagLibrary
|
||||
anchors: OrderedTable[string, AnchorId]
|
||||
callback: YamlWarningCallback
|
||||
|
||||
YamlPresentationStyle* = enum
|
||||
## Different styles for YAML character stream output.
|
||||
|
@ -186,7 +185,18 @@ type
|
|||
## flow style at all.
|
||||
ypsMinimal, ypsCanonical, ypsDefault, ypsJson, ypsBlockOnly
|
||||
|
||||
YamlParserError* = object of Exception
|
||||
YamlLoadingError* = object of Exception
|
||||
## Base class for all exceptions that may be raised during the process
|
||||
## of loading a YAML character stream.
|
||||
line*: int ## line number (1-based) where the error was encountered
|
||||
column*: int ## \
|
||||
## column number (1-based) where the error was encountered
|
||||
lineContent*: string ## \
|
||||
## content of the line where the error was encountered. Includes a
|
||||
## second line with a marker ``^`` at the position where the error
|
||||
## was encountered, as returned by ``lexbase.getCurrentLine``.
|
||||
|
||||
YamlParserError* = object of YamlLoadingError
|
||||
## A parser error is raised if the character stream that is parsed is
|
||||
## not a valid YAML character stream. This stream cannot and will not be
|
||||
## parsed wholly nor partially and all events that have been emitted by
|
||||
|
@ -216,16 +226,16 @@ type
|
|||
##
|
||||
## Some elements in this list are vague. For a detailed description of a
|
||||
## valid YAML character stream, see the YAML specification.
|
||||
line*: int ## line number (1-based) where the error was encountered
|
||||
column*: int ## \
|
||||
## column number (1-based) where the error was encountered
|
||||
lineContent*: string ## \
|
||||
## content of the line where the error was encountered. Includes a
|
||||
## second line with a marker ``^`` at the position where the error
|
||||
## was encountered, as returned by ``lexbase.getCurrentLine``.
|
||||
|
||||
YamlPresenterError* = object of Exception
|
||||
## Exception that may be raised by the YAML presenter.
|
||||
## Exception that may be raised by the YAML presenter. Currently, the
|
||||
## only ocassion this exception may be raised is when the presenter is
|
||||
## instructed to output JSON, but is unable to do so. This may occur if:
|
||||
##
|
||||
## - The given `YamlStream <#YamlStream>`_ contains a map which has any
|
||||
## non-scalar type as key.
|
||||
## - Any float scalar bears a ``NaN`` or positive/negative infinity
|
||||
## value
|
||||
const
|
||||
# failsafe schema
|
||||
|
||||
|
@ -350,6 +360,9 @@ proc extendedTagLibrary*(): YamlTagLibrary
|
|||
proc newParser*(tagLib: YamlTagLibrary): YamlSequentialParser
|
||||
## Instanciates a parser
|
||||
|
||||
proc setWarningCallback*(parser: YamlSequentialParser,
|
||||
callback: YamlWarningCallback)
|
||||
|
||||
proc anchor*(parser: YamlSequentialParser, id: AnchorId): string
|
||||
## Get the anchor name which an ``AnchorId`` maps to
|
||||
|
||||
|
|
|
@ -233,7 +233,7 @@ proc safeTagUri*(id: TagId): string =
|
|||
proc construct*(s: YamlStream, result: var string) =
|
||||
let item = s()
|
||||
if finished(s) or item.kind != yamlScalar:
|
||||
raise newException(ValueError, "Construction error!" & $item.description)
|
||||
raise newException(ValueError, "Construction error!")
|
||||
if item.scalarTag notin [yTagQuestionMark, yTagExclamationMark, yTagString]:
|
||||
raise newException(ValueError, "Wrong tag for string.")
|
||||
result = item.scalarContent
|
||||
|
@ -306,15 +306,27 @@ proc construct*(s: YamlStream, result: var float) =
|
|||
proc serialize*(value: float,
|
||||
tagStyle: YamlTagStyle = ytsNone): YamlStream =
|
||||
result = iterator(): YamlStreamEvent =
|
||||
var asString = case value
|
||||
of Inf: ".inf"
|
||||
of NegInf: "-.inf"
|
||||
of NaN: ".nan"
|
||||
else: $value
|
||||
var
|
||||
asString: string
|
||||
hint: YamlTypeHint
|
||||
case value
|
||||
of Inf:
|
||||
asString = ".inf"
|
||||
hint = yTypeFloatInf
|
||||
of NegInf:
|
||||
asString = "-.inf"
|
||||
hint = yTypeFloatInf
|
||||
of NaN:
|
||||
asString = ".nan"
|
||||
hint = yTypeFloatNaN
|
||||
else:
|
||||
asString = $value
|
||||
hint = yTypeFloat
|
||||
|
||||
yield YamlStreamEvent(kind: yamlScalar,
|
||||
scalarTag: presentTag(float, tagStyle),
|
||||
scalarAnchor: yAnchorNone, scalarContent: asString)
|
||||
scalarAnchor: yAnchorNone, scalarContent: asString,
|
||||
scalarType: hint)
|
||||
|
||||
proc yamlTag*(T: typedesc[bool]): TagId {.inline.} = yTagBoolean
|
||||
|
||||
|
|
Loading…
Reference in New Issue