From 2c470c8aa97457545082bf46441f640768efced2 Mon Sep 17 00:00:00 2001 From: GaveUp Date: Mon, 19 Sep 2016 13:51:50 -0500 Subject: [PATCH] Add octal/hex support. Add multi doc support. --- private/serialization.nim | 66 +++++++++++++++++++++++++++++++++++---- test/serializing.nim | 61 ++++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 6 deletions(-) diff --git a/private/serialization.nim b/private/serialization.nim index abbd06c..e14dadb 100644 --- a/private/serialization.nim +++ b/private/serialization.nim @@ -62,12 +62,40 @@ proc representObject*(value: string, ts: TagStyle, ## represents a string as YAML scalar c.put(scalarEvent(value, tag, yAnchorNone)) +proc parseHex[T: int8|int16|int32|int64|uint8|uint16|uint32|uint64](s: string): T = + result = 0 + var i = 2 + while true: + case s[i] + of '_': discard + of '0'..'9': result = result shl 4 or (T(ord(s[i]) - ord('0')) and 0xFF) + of 'a'..'f': result = result shl 4 or (T(ord(s[i]) - ord('a') + 10) and 0xFF) + of 'A'..'F': result = result shl 4 or (T(ord(s[i]) - ord('A') + 10) and 0xFF) + of '\0': break + else: raise newException(ValueError, "Invalid character in hex: " & s[i]) + inc(i) + +proc parseOctal[T: int8|int16|int32|int64|uint8|uint16|uint32|uint64](s: string): T = + var i = 2 + while true: + case s[i] + of '_': discard + of '0'..'8': result = result * 8 + T((ord(s[i]) - ord('0'))) + of '\0': break + else: raise newException(ValueError, "Invalid character in hex: " & s[i]) + inc(i) + proc constructObject*[T: int8|int16|int32|int64]( s: var YamlStream, c: ConstructionContext, result: var T) {.raises: [YamlConstructionError, YamlStreamError].} = ## constructs an integer value from a YAML scalar constructScalarItem(s, item, T): - result = T(parseBiggestInt(item.scalarContent)) + if item.scalarContent[0] == '0' and (item.scalarContent[1] == 'x' or item.scalarContent[1] == 'X'): + result = parseHex[T](item.scalarContent) + elif item.scalarContent[0] == '0' and (item.scalarContent[1] == 'o' or item.scalarContent[1] == 'O'): + result = parseOctal[T](item.scalarContent) + else: + result = T(parseBiggestInt(item.scalarContent)) proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var int) @@ -99,10 +127,15 @@ proc representObject*(value: int, tagStyle: TagStyle, {.push overflowChecks: on.} proc parseBiggestUInt(s: string): uint64 = result = 0 - for c in s: - if c in {'0'..'9'}: result *= 10.uint64 + (uint64(c) - uint64('0')) - elif c == '_': discard - else: raise newException(ValueError, "Invalid char in uint: " & c) + if s[0] == '0' and (s[1] == 'x' or s[1] == 'X'): + result = parseHex[uint64](s) + elif s[0] == '0' and (s[1] == 'o' or s[1] == 'O'): + result = parseOctal[uint64](s) + else: + for c in s: + if c in {'0'..'9'}: result = result * 10.uint64 + (uint64(c) - uint64('0')) + elif c == '_': discard + else: raise newException(ValueError, "Invalid char in uint: " & c) {.pop.} proc constructObject*[T: uint8|uint16|uint32|uint64]( @@ -110,7 +143,7 @@ proc constructObject*[T: uint8|uint16|uint32|uint64]( {.raises: [YamlConstructionError, YamlStreamError].} = ## construct an unsigned integer value from a YAML scalar constructScalarItem(s, item, T): - result = T(parseBiggestUInt(item.scalarContent)) + result = T(yaml.parseBiggestUInt(item.scalarContent)) proc constructObject*(s: var YamlStream, c: ConstructionContext, result: var uint) @@ -773,6 +806,27 @@ proc load*[K](input: Stream | string, target: var K) = elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent) else: internalError("Unexpected exception: " & e.parent.repr) +proc loadMultiDoc*[K](input: Stream, target: var seq[K]) = + if target.isNil: + target = newSeq[K]() + var + parser = newYamlParser(serializationTagLibrary) + events = parser.parse(input) + try: + while not events.finished(): + var item: K + construct(events, item) + target.add(item) + except YamlConstructionError: + var e = (ref YamlConstructionError)(getCurrentException()) + discard events.getLastTokenContext(e.line, e.column, e.lineContent) + raise e + except YamlStreamError: + let e = (ref YamlStreamError)(getCurrentException()) + if e.parent of IOError: raise (ref IOError)(e.parent) + elif e.parent of YamlParserError: raise (ref YamlParserError)(e.parent) + else: internalError("Unexpected exception: " & e.parent.repr) + proc setAnchor(a: var AnchorId, q: var Table[pointer, AnchorId]) {.inline.} = if a != yAnchorNone: a = q.getOrDefault(cast[pointer](a)) diff --git a/test/serializing.nim b/test/serializing.nim index d48f968..048dd2d 100644 --- a/test/serializing.nim +++ b/test/serializing.nim @@ -101,6 +101,54 @@ suite "Serialization": except: gotException = true assert gotException, "Expected exception, got none." + test "Serialization: Load Hex byte (0xFF)": + let input = newStringStream("0xFF") + var result: byte + load(input, result) + assert(result == 255) + + test "Serialization: Load Hex byte (0xC)": + let input = newStringStream("0xC") + var result: byte + load(input, result) + assert(result == 12) + + test "Serialization: Load Octal byte (0o14)": + let input = newStringStream("0o14") + var result: byte + load(input, result) + assert(result == 12) + + test "Serialization: Load byte (14)": + let input = newStringStream("14") + var result: byte + load(input, result) + assert(result == 14) + + test "Serialization: Load Hex int (0xFF)": + let input = newStringStream("0xFF") + var result: int + load(input, result) + assert(result == 255) + + test "Serialization: Load Hex int (0xC)": + let input = newStringStream("0xC") + var result: int + load(input, result) + assert(result == 12) + + test "Serialization: Load Octal int (0o14)": + let input = newStringStream("0o14") + var result: int + load(input, result) + assert(result == 12) + + test "Serialization: Load int (14)": + let input = newStringStream("14") + var result: int + load(input, result) + assert(result == 14) + test "Load nil string": let input = newStringStream("!nim:nil:string \"\"") var result: string @@ -253,6 +301,19 @@ suite "Serialization": let input = (str: "value", i: 42.int32, b: true) var output = dump(input, tsNone) assertStringEqual "%YAML 1.2\n--- \nstr: value\ni: 42\nb: y", output + + test "Serialization: Load Multiple Documents": + let input = newStringStream("1\n---\n2") + var result: seq[int] + loadMultiDoc(input, result) + assert result[0] == 1 + assert result[1] == 2 + + test "Serialization: Load Multiple Documents": + let input = newStringStream("1") + var result: seq[int] + loadMultiDoc(input, result) + assert result[0] == 1 test "Load custom object": let input = newStringStream("firstnamechar: P\nsurname: Pan\nage: 12")