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:
Felix Krause 2016-01-05 19:06:55 +01:00
parent d2baa1749e
commit 18753b1a4a
7 changed files with 95 additions and 66 deletions

View File

@ -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 =

View File

@ -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)

View File

@ -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)

View File

@ -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) =

View File

@ -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()

View File

@ -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

View File

@ -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