From a08f4c1e4e38cecdd3c7e75d8ba0a20ab50ee1a5 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Sun, 24 Jan 2016 18:24:09 +0100 Subject: [PATCH] Sanitized exceptions and raises pragmas * Added raises pragma to every proc in yaml.nim * Properly encapsulate exceptions from YamlStreams * Discovered a Nim compiler bug and added a workaround * Added possibility to query line, column and line content from YamlParser * Updated rst documentation * Actually call callback in fastparse if available * Fixed parsing YAML version --- README.md | 3 - private/fastparse.nim | 36 ++++++++--- private/hints.nim | 4 +- private/json.nim | 138 ++++++++++++++++++++++++++++++------------ private/presenter.nim | 105 ++++++++++++++++++-------------- test/tests.nim | 2 +- yaml.nim | 99 +++++++++++++++++++----------- 7 files changed, 251 insertions(+), 136 deletions(-) diff --git a/README.md b/README.md index da185b0..d42b5ea 100644 --- a/README.md +++ b/README.md @@ -208,11 +208,8 @@ Output: * Documentation: - Document yaml.serialization - - Setup github pages site with proper API documentation * Lexer: - Add type hints for more scalar types - * Parser: - - Properly handle leading spaces in block scalars * Serialization: - Support for more standard library types - Support for ref and ptr types diff --git a/private/fastparse.nim b/private/fastparse.nim index ca20815..3f8d059 100644 --- a/private/fastparse.nim +++ b/private/fastparse.nim @@ -44,6 +44,15 @@ proc newYamlParser*(tagLib: TagLibrary = initExtendedTagLibrary(), result.tagLib = tagLib result.callback = callback +proc getLineNumber*(p: YamlParser): int = p.lexer.lineNumber + +proc getColNumber*(p: YamlParser): int = p.tokenstart + 1 # column is 1-based + +proc getLineContent*(p: YamlParser, marker: bool = true): string = + result = p.lexer.getCurrentLine(false) + if marker: + result.add(repeat(' ', p.tokenstart) & "^\n") + template debug(message: string) {.dirty.} = when defined(yamlDebug): try: styledWriteLine(stdout, fgBlue, message) @@ -52,15 +61,14 @@ template debug(message: string) {.dirty.} = template parserError(message: string) {.dirty.} = var e = newException(YamlParserError, message) e.line = p.lexer.lineNumber - e.column = p.tokenstart - e.lineContent = p.lexer.getCurrentLine(false) & - repeat(' ', p.tokenstart) & "^\n" + e.column = p.tokenstart + 1 + e.lineContent = p.getLineContent(true) raise e template lexerError(lx: BaseLexer, message: string) {.dirty.} = var e = newException(YamlParserError, message) e.line = lx.lineNumber - e.column = lx.bufpos + e.column = lx.bufpos + 1 e.lineContent = lx.getCurrentLine(false) & repeat(' ', lx.getColNumber(lx.bufpos)) & "^\n" raise e @@ -408,12 +416,18 @@ template yamlVersion(lexer: BaseLexer, o: var string) = c = lexer.buf[lexer.bufpos] if lexer.buf[lexer.bufpos] != '.': lexerError(lexer, "Invalid YAML version number") + o.add('.') lexer.bufpos.inc() - if lexer.buf[lexer.bufpos] notin digits: + c = lexer.buf[lexer.bufpos] + if c notin digits: lexerError(lexer, "Invalid YAML version number") + o.add(c) lexer.bufpos.inc() - while lexer.buf[lexer.bufpos] in digits: + c = lexer.buf[lexer.bufpos] + while c in digits: + o.add(c) lexer.bufpos.inc() + c = lexer.buf[lexer.bufpos] if lexer.buf[lexer.bufpos] notin spaceOrLineEnd: lexerError(lexer, "Invalid YAML version number") @@ -1006,8 +1020,10 @@ proc parse*(p: YamlParser, s: Stream): YamlStream = startToken() p.lexer.yamlVersion(version) if version != "1.2": - echo "version is not 1.2!" - # TODO: warning (unknown version) + if p.callback != nil: + p.callback(p.lexer.lineNumber, p.getColNumber(), + p.getLineContent(), + "Version is not 1.2, but " & version) discard p.lexer.lineEnding() handleLineEnd(false) @@ -1020,7 +1036,9 @@ proc parse*(p: YamlParser, s: Stream): YamlStream = p.lexer.lineEnding() handleLineEnd(false) of ldUnknown: - # TODO: warning (unknown directive) + if p.callback != nil: + p.callback(p.lexer.lineNumber, p.getColNumber(), + p.getLineContent(), "Unknown directive") p.lexer.finishLine() handleLineEnd(false) of ' ', '\t': diff --git a/private/hints.nim b/private/hints.nim index 24ce1c6..9fbf80d 100644 --- a/private/hints.nim +++ b/private/hints.nim @@ -62,11 +62,11 @@ macro typeHintStateMachine(c: untyped, content: untyped): stmt = newIdentNode("typeHintState"), copyNimTree(rule[2])))) stateBranches.add(stateBranch) stateBranches.add(newNimNode(nnkElse).add(newStmtList( - newNimNode(nnkReturnStmt).add(newIdentNode("yTypeString"))))) + newNimNode(nnkReturnStmt).add(newIdentNode("yTypeUnknown"))))) charBranch.add(newStmtList(stateBranches)) result.add(charBranch) result.add(newNimNode(nnkElse).add(newStmtList( - newNimNode(nnkReturnStmt).add(newIdentNode("yTypeString"))))) + newNimNode(nnkReturnStmt).add(newIdentNode("yTypeUnknown"))))) template advanceTypeHint(ch: char) {.dirty.} = typeHintStateMachine ch: diff --git a/private/json.nim b/private/json.nim index d856f06..1a5bfd5 100644 --- a/private/json.nim +++ b/private/json.nim @@ -7,9 +7,11 @@ type Level = tuple[node: JsonNode, key: string] -proc initLevel(node: JsonNode): Level = (node: node, key: cast[string](nil)) +proc initLevel(node: JsonNode): Level {.raises: [].} = + (node: node, key: cast[string](nil)) -proc jsonFromScalar(content: string, tag: TagId): JsonNode = +proc jsonFromScalar(content: string, tag: TagId): JsonNode + {.raises: [YamlConstructionError].}= new(result) var mappedType: TypeHint @@ -17,7 +19,7 @@ proc jsonFromScalar(content: string, tag: TagId): JsonNode = of yTagQuestionMark: mappedType = guessType(content) of yTagExclamationMark, yTagString: - mappedType = yTypeString + mappedType = yTypeUnknown of yTagBoolean: case guessType(content) of yTypeBoolTrue: @@ -25,41 +27,56 @@ proc jsonFromScalar(content: string, tag: TagId): JsonNode = of yTypeBoolFalse: mappedType = yTypeBoolFalse else: - raise newException(ValueError, "Invalid boolean value: " & content) + raise newException(YamlConstructionError, + "Invalid boolean value: " & content) of yTagInteger: mappedType = yTypeInteger of yTagNull: mappedType = yTypeNull of yTagFloat: - mappedType = yTypeFloat - ## TODO: NaN, inf + case guessType(content) + of yTypeFloat: + mappedType = yTypeFloat + of yTypeFloatInf: + mappedType = yTypeFloatInf + of yTypeFloatNaN: + mappedType = yTypeFloatNaN + else: + raise newException(YamlConstructionError, + "Invalid float value: " & content) else: mappedType = yTypeUnknown - case mappedType - of yTypeInteger: - result.kind = JInt - result.num = parseBiggestInt(content) - of yTypeFloat: - result.kind = JFloat - result.fnum = parseFloat(content) - of yTypeFloatInf: - result.kind = JFloat - result.fnum = if content[0] == '-': NegInf else: Inf - of yTypeFloatNaN: - result.kind = JFloat - result.fnum = NaN - of yTypeBoolTrue: - result.kind = JBool - result.bval = true - of yTypeBoolFalse: - result.kind = JBool - result.bval = false - of yTypeNull: - result.kind = JNull - else: - result.kind = JString - result.str = content + try: + case mappedType + of yTypeInteger: + result.kind = JInt + result.num = parseBiggestInt(content) + of yTypeFloat: + result.kind = JFloat + result.fnum = parseFloat(content) + of yTypeFloatInf: + result.kind = JFloat + result.fnum = if content[0] == '-': NegInf else: Inf + of yTypeFloatNaN: + result.kind = JFloat + result.fnum = NaN + of yTypeBoolTrue: + result.kind = JBool + result.bval = true + of yTypeBoolFalse: + result.kind = JBool + result.bval = false + of yTypeNull: + result.kind = JNull + else: + result.kind = JString + result.str = content + except ValueError: + var e = newException(YamlConstructionError, + "Cannot parse numeric value") + e.parent = getCurrentException() + raise e proc constructJson*(s: YamlStream): seq[JsonNode] = newSeq(result, 0) @@ -67,8 +84,19 @@ proc constructJson*(s: YamlStream): seq[JsonNode] = var levels = newSeq[Level]() anchors = initTable[AnchorId, JsonNode]() - - for event in s(): + safeIter = iterator(): YamlStreamEvent + {.raises: [YamlConstructionStreamError].} = + while true: + var item: YamlStreamEvent + try: + item = s() + if finished(s): break + except Exception: + var e = newException(YamlConstructionStreamError, "") + e.parent = getCurrentException() + raise e + yield item + for event in safeIter(): case event.kind of yamlStartDocument: # we don't need to do anything here; root node will be created @@ -104,7 +132,7 @@ proc constructJson*(s: YamlStream): seq[JsonNode] = # JSON only allows strings as keys levels[levels.high].key = event.scalarContent if event.scalarAnchor != yAnchorNone: - raise newException(ValueError, + raise newException(YamlConstructionError, "scalar keys may not have anchors in JSON") else: let jsonScalar = jsonFromScalar(event.scalarContent, @@ -125,7 +153,7 @@ proc constructJson*(s: YamlStream): seq[JsonNode] = of JObject: if isNil(levels[levels.high].key): echo level.node.pretty() - raise newException(ValueError, + raise newException(YamlConstructionError, "non-scalar as key not allowed in JSON") else: levels[levels.high].node.fields.add( @@ -140,15 +168,26 @@ proc constructJson*(s: YamlStream): seq[JsonNode] = # (else the parser would have already thrown an exception) case levels[levels.high].node.kind of JArray: - levels[levels.high].node.elems.add(anchors[event.aliasTarget]) + try: + levels[levels.high].node.elems.add( + anchors[event.aliasTarget]) + except KeyError: + # we can safely assume that this doesn't happen. It would + # have resulted in a parser error earlier. + assert(false) of JObject: if isNil(levels[levels.high].key): - raise newException(ValueError, + raise newException(YamlConstructionError, "cannot use alias node as key in JSON") else: - levels[levels.high].node.fields.add( - (key: levels[levels.high].key, - val: anchors[event.aliasTarget])) + try: + levels[levels.high].node.fields.add( + (key: levels[levels.high].key, + val: anchors[event.aliasTarget])) + except KeyError: + # we can safely assume that this doesn't happen. It would + # have resulted in a parser error earlier. + assert(false) levels[levels.high].key = nil else: discard # will never happen @@ -157,4 +196,23 @@ proc loadToJson*(s: Stream): seq[JsonNode] = var parser = newYamlParser(initCoreTagLibrary()) events = parser.parse(s) - return constructJson(events) \ No newline at end of file + try: + return constructJson(events) + except YamlConstructionError: + var e = cast[ref YamlConstructionError](getCurrentException()) + e.line = parser.getLineNumber() + e.column = parser.getColNumber() + e.lineContent = parser.getLineContent() + raise e + except YamlConstructionStreamError: + let e = getCurrentException() + if e.parent is IOError: + raise cast[ref IOError](e.parent) + elif e.parent is YamlParserError: + raise cast[ref YamlParserError](e.parent) + else: + # can never happen + assert(false) + except Exception: + # compiler bug: https://github.com/nim-lang/Nim/issues/3772 + assert(false) \ No newline at end of file diff --git a/private/presenter.nim b/private/presenter.nim index 6519054..51d4351 100644 --- a/private/presenter.nim +++ b/private/presenter.nim @@ -27,7 +27,7 @@ proc writeDoubleQuoted(scalar: string, s: Stream) s.write('"') except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e template safeWrite(s: string or char) {.dirty.} = @@ -35,7 +35,7 @@ template safeWrite(s: string or char) {.dirty.} = target.write(s) except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e proc startItem(target: Stream, style: PresentationStyle, indentation: int, @@ -121,7 +121,7 @@ proc startItem(target: Stream, style: PresentationStyle, indentation: int, discard # can never happen except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e proc writeTagAndAnchor(target: Stream, tag: TagId, tagLib: TagLibrary, @@ -147,7 +147,7 @@ proc writeTagAndAnchor(target: Stream, tag: TagId, tagLib: TagLibrary, target.write(' ') except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, @@ -166,7 +166,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, cached.enqueue(item) except: var e = newException(YamlPresenterStreamError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e indentation = 0 levels = newSeq[DumperState]() @@ -184,7 +184,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, target.write("--- ") except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e of yamlScalar: if levels.len == 0: @@ -229,7 +229,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, target.write(cast[byte]('a') + cast[byte](item.aliasTarget)) except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e of yamlStartSequence: var nextState: DumperState @@ -253,7 +253,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, break except: var e = newException(YamlPresenterStreamError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e nextState = if length <= 60: dFlowSequenceStart else: dBlockSequenceItem @@ -318,7 +318,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, mps = mpNeedBlock except: var e = newException(YamlPresenterStreamError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e nextState = if mps == mpNeedBlock: dBlockMapValue else: dBlockInlineMap @@ -384,7 +384,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, target.write(']') except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e if levels.len == 0 or levels[levels.high] notin [dBlockExplicitMapKey, dBlockMapValue, @@ -418,7 +418,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, target.write('}') except: var e = newException(YamlPresenterOutputError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e if levels.len == 0 or levels[levels.high] notin [dBlockExplicitMapKey, dBlockMapValue, @@ -444,7 +444,7 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, cached.enqueue(next) except: var e = newException(YamlPresenterStreamError, "") - e.cause = getCurrentException() + e.parent = getCurrentException() raise e safeWrite("...\x0A") @@ -454,37 +454,52 @@ proc transform*(input: Stream, output: Stream, style: PresentationStyle, taglib = initExtendedTagLibrary() parser = newYamlParser(tagLib) events = parser.parse(input) - if style == psCanonical: - var specificTagEvents = iterator(): YamlStreamEvent = - for e in events(): - var event = e - case event.kind - of yamlStartDocument, yamlEndDocument, yamlEndMap, yamlAlias, - yamlEndSequence: - discard - of yamlStartMap: - if event.mapTag in [yTagQuestionMark, yTagExclamationMark]: - event.mapTag = yTagMap - of yamlStartSequence: - if event.seqTag in [yTagQuestionMark, yTagExclamationMark]: - event.seqTag = yTagSequence - of yamlScalar: - if event.scalarTag == yTagQuestionMark: - case guessType(event.scalarContent) - of yTypeInteger: - event.scalarTag = yTagInteger - of yTypeFloat, yTypeFloatInf, yTypeFloatNaN: - event.scalarTag = yTagFloat - of yTypeBoolTrue, yTypeBoolFalse: - event.scalarTag = yTagBoolean - of yTypeNull: - event.scalarTag = yTagNull - of yTypeString, yTypeUnknown: + try: + if style == psCanonical: + var specificTagEvents = iterator(): YamlStreamEvent = + for e in events(): + var event = e + case event.kind + of yamlStartDocument, yamlEndDocument, yamlEndMap, + yamlAlias, yamlEndSequence: + discard + of yamlStartMap: + if event.mapTag in [yTagQuestionMark, + yTagExclamationMark]: + event.mapTag = yTagMap + of yamlStartSequence: + if event.seqTag in [yTagQuestionMark, + yTagExclamationMark]: + event.seqTag = yTagSequence + of yamlScalar: + if event.scalarTag == yTagQuestionMark: + case guessType(event.scalarContent) + of yTypeInteger: + event.scalarTag = yTagInteger + of yTypeFloat, yTypeFloatInf, yTypeFloatNaN: + event.scalarTag = yTagFloat + of yTypeBoolTrue, yTypeBoolFalse: + event.scalarTag = yTagBoolean + of yTypeNull: + event.scalarTag = yTagNull + of yTypeUnknown: + event.scalarTag = yTagString + elif event.scalarTag == yTagExclamationMark: event.scalarTag = yTagString - elif event.scalarTag == yTagExclamationMark: - event.scalarTag = yTagString - yield event - present(specificTagEvents, output, tagLib, style, - indentationStep) - else: - present(events, output, tagLib, style, indentationStep) + yield event + present(specificTagEvents, output, tagLib, style, + indentationStep) + else: + present(events, output, tagLib, style, indentationStep) + except YamlPresenterStreamError: + let e = getCurrentException() + if e.parent is IOError: + raise cast[ref IOError](e.parent) + elif e.parent is YamlParserError: + raise cast[ref YamlParserError](e.parent) + else: + # never happens + assert(false) + except Exception: + # compiler bug: https://github.com/nim-lang/Nim/issues/3772 + assert(false) \ No newline at end of file diff --git a/test/tests.nim b/test/tests.nim index dd447a7..f2134d4 100644 --- a/test/tests.nim +++ b/test/tests.nim @@ -1 +1 @@ -import parsing, serializing \ No newline at end of file +import parsing, constructingJson, serializing \ No newline at end of file diff --git a/yaml.nim b/yaml.nim index 2ad9221..3a2b37b 100644 --- a/yaml.nim +++ b/yaml.nim @@ -25,10 +25,13 @@ when defined(yamlDebug): type TypeHint* = enum - ## A type hint is a friendly message from the YAML lexer, telling you - ## it thinks a scalar string probably is of a certain type. You are not - ## required to adhere to this information. The first matching RegEx will - ## be the type hint of a scalar string. + ## A type hint can be computed from scalar content and tells you what + ## NimYAML thinks the scalar's type is. It is generated by + ## `guessType <#guessType,string,TypeHint>`_ The first matching RegEx + ## in the following table will be the type hint of a scalar string. + ## + ## You can use it to determine the type of YAML scalars that have a '?' + ## non-specific tag, but using this feature is completely optional. ## ## ================== ========================= ## Name RegEx @@ -40,14 +43,10 @@ type ## ``yTypeBoolTrue`` ``y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON`` ## ``yTypeBoolFalse`` ``n|N|no|No|NO|false|False|FALSE|off|Off|OFF`` ## ``yTypeNull`` ``~ | null | Null | NULL`` - ## ``yTypeString`` *none* ## ``yTypeUnknown`` ``*`` ## ================== ========================= - ## - ## The value `yTypeString` is not returned based on RegExes, but for - ## scalars that are quoted within the YAML input character stream. yTypeInteger, yTypeFloat, yTypeFloatInf, yTypeFloatNaN, yTypeBoolTrue, - yTypeBoolFalse, yTypeNull, yTypeString, yTypeUnknown + yTypeBoolFalse, yTypeNull, yTypeUnknown YamlStreamEventKind* = enum ## Kinds of YAML events that may occur in an ``YamlStream``. Event kinds @@ -59,11 +58,13 @@ type ## A ``TagId`` identifies a tag URI, like for example ## ``"tag:yaml.org,2002:str"``. The URI corresponding to a ``TagId`` can ## be queried from the `TagLibrary <#TagLibrary>`_ which was - ## used to create this ``TagId`` with - ## `uri <#uri,TagLibrary,TagId>`_. URI strings are - ## mapped to ``TagId`` s for efficiency reasons (you do not need to - ## compare strings every time) and to be able to discover unknown tag - ## URIs early in the parsing process. + ## used to create this ``TagId``; e.g. when you parse a YAML character + ## stream, the ``TagLibrary`` of the parser is the one which generates + ## the resulting ``TagId`` s. + ## + ## URI strings are mapped to ``TagId`` s for efficiency reasons (you + ## do not need to compare strings every time) and to be able to + ## discover unknown tag URIs early in the parsing process. AnchorId* = distinct int ## \ ## An ``AnchorId`` identifies an anchor in the current document. It @@ -105,7 +106,8 @@ type ## ``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``. + ## is terminated by a ``yamlEndDocument``. Moreover, every emitted map + ## has an even number of children. ## ## The creator of a ``YamlStream`` is responsible for it being ## well-formed. A user of the stream may assume that it is well-formed @@ -122,8 +124,14 @@ type ## `extendedTagLibrary <#extendedTagLibrary>`_. ## ## When `YamlParser <#YamlParser>`_ encounters tags not existing in the - ## tag library, it will assign ``nextCustomTagId`` to the URI, add it - ## to the tag library and increase ``nextCustomTagId``. + ## tag library, it will use + ## `registerTagUri <#registerTagUri,TagLibrary,string,TagId>`_ to add + ## the tag to the library. + ## + ## You can base your tag library on common tag libraries by initializing + ## them with `initFailsafeTagLibrary <#initFailsafeTagLibrary>`_, + ## `initCoreTagLibrary <#initCoreTagLibrary>`_ or + ## `initExtendedTagLibrary <#initExtendedTagLibrary>`_. tags*: Table[string, TagId] nextCustomTagId*: TagId secondaryPrefix*: string @@ -179,7 +187,7 @@ type 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``. + ## was encountered. YamlParserError* = object of YamlLoadingError ## A parser error is raised if the character stream that is parsed is @@ -223,16 +231,26 @@ type YamlPresenterOutputError* = object of Exception ## Exception that may be raised by the YAML presenter. This occurs if - ## writing character data to the output stream raises any exception. The - ## exception that has been catched is retrievable from ``cause``. - cause*: ref Exception + ## writing character data to the output stream raises any exception. + ## The error that has occurred is available from ``parent``. YamlPresenterStreamError* = object of Exception ## Exception that may be raised by the YAML presenter. This occurs if ## an exception is raised while retrieving the next item from a - ## `YamlStream <#YamlStream>`_. The exception that has been catched is - ## retrievable from ``cause``. - cause*: ref Exception + ## `YamlStream <#YamlStream>`_. The error that has occurred is + ## available from ``parent``. + + YamlConstructionError* = object of YamlLoadingError + ## Exception that may be raised when constructing data objects from a + ## `YamlStream <#YamlStream>`_. The fields ``line``, ``column`` and + ## ``lineContent`` are only available if the costructing proc also does + ## parsing, because otherwise this information is not available to the + ## costruction proc. + + YamlConstructionStreamError* = object of YamlLoadingError + ## Exception that may be raised by a constructor if the input + ## `YamlStream <#YamlStream>`_ raises an error. The error that has + ## occurred is available from ``parent``. const # failsafe schema @@ -286,10 +304,10 @@ const # interface -proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool +proc `==`*(left: YamlStreamEvent, right: YamlStreamEvent): bool {.raises: [].} ## compares all existing fields of the given items -proc `$`*(event: YamlStreamEvent): string +proc `$`*(event: YamlStreamEvent): string {.raises: [].} ## outputs a human-readable string describing the given event proc startDocEvent*(): YamlStreamEvent {.inline, raises: [].} @@ -315,34 +333,34 @@ proc `==`*(left, right: AnchorId): bool {.borrow.} proc `$`*(id: AnchorId): string {.borrow.} proc hash*(id: AnchorId): Hash {.borrow.} -proc initTagLibrary*(): TagLibrary +proc initTagLibrary*(): TagLibrary {.raises: [].} ## initializes the ``tags`` table and sets ``nextCustomTagId`` to ## ``yFirstCustomTagId``. -proc registerUri*(tagLib: TagLibrary, uri: string): TagId +proc registerUri*(tagLib: TagLibrary, uri: string): TagId {.raises: [].} ## registers a custom tag URI with a ``TagLibrary``. The URI will get ## the ``TagId`` ``nextCustomTagId``, which will be incremented. -proc uri*(tagLib: TagLibrary, id: TagId): string +proc uri*(tagLib: TagLibrary, id: TagId): string {.raises: [KeyError].} ## retrieve the URI a ``TagId`` maps to. # these should be consts, but the Nim VM still has problems handling tables # properly, so we use let instead. -proc initFailsafeTagLibrary*(): TagLibrary +proc initFailsafeTagLibrary*(): TagLibrary {.raises: [].} ## Contains only: ## - ``!`` ## - ``?`` ## - ``!!str`` ## - ``!!map`` ## - ``!!seq`` -proc initCoreTagLibrary*(): TagLibrary +proc initCoreTagLibrary*(): TagLibrary {.raises: [].} ## Contains everything in ``initFailsafeTagLibrary`` plus: ## - ``!!null`` ## - ``!!bool`` ## - ``!!int`` ## - ``!!float`` -proc initExtendedTagLibrary*(): TagLibrary +proc initExtendedTagLibrary*(): TagLibrary {.raises: [].} ## Contains everything from ``initCoreTagLibrary`` plus: ## - ``!!omap`` ## - ``!!pairs`` @@ -356,12 +374,17 @@ proc initExtendedTagLibrary*(): TagLibrary proc guessType*(scalar: string): TypeHint {.raises: [].} proc newYamlParser*(tagLib: TagLibrary = initExtendedTagLibrary(), - callback: WarningCallback = nil): YamlParser + callback: WarningCallback = nil): YamlParser {.raises: [].} + +proc getLineNumber*(p: YamlParser): int {.raises: [].} +proc getColNumber*(p: YamlParser): int {.raises: [].} +proc getLineContent*(p: YamlParser, marker: bool = true): string {.raises: [].} proc parse*(p: YamlParser, s: Stream): YamlStream {.raises: [IOError, YamlParserError].} proc constructJson*(s: YamlStream): seq[JsonNode] + {.raises: [YamlConstructionError, YamlConstructionStreamError].} ## Construct an in-memory JSON tree from a YAML event stream. The stream may ## not contain any tags apart from those in ``coreTagLibrary``. Anchors and ## aliases will be resolved. Maps in the input must not contain @@ -376,7 +399,8 @@ proc constructJson*(s: YamlStream): seq[JsonNode] ## of these values into a JSON character stream. proc loadToJson*(s: Stream): seq[JsonNode] - ## Uses `YamlSequentialParser <#YamlSequentialParser>`_ and + {.raises: [IOError, YamlParserError, YamlConstructionError].} + ## Uses `YamlParser <#YamlParser>`_ and ## `constructJson <#constructJson>`_ to construct an in-memory JSON tree ## from a YAML character stream. @@ -388,9 +412,12 @@ proc present*(s: YamlStream, target: Stream, tagLib: TagLibrary, ## Convert ``s`` to a YAML character stream and write it to ``target``. proc transform*(input: Stream, output: Stream, style: PresentationStyle, - indentationStep: int = 2) + indentationStep: int = 2) {.raises: [IOError, YamlParserError, + YamlPresenterJsonError, + YamlPresenterOutputError].} ## Parser ``input`` as YAML character stream and then dump it to ``output`` - ## without resolving any tags, anchors and aliases. + ## while resolving non-specific tags to the ones in the YAML core tag + ## library. # implementation