From c83d488886d3c2b11a773cad56cece9bde82e846 Mon Sep 17 00:00:00 2001 From: Felix Krause Date: Thu, 24 Dec 2015 15:21:49 +0100 Subject: [PATCH] Added JSON compositor. --- src/private/json.nim | 94 ++++++++++++++++++++++++++++++++++++++++++++ src/yaml.nim | 8 +++- test/parsing.nim | 8 ++-- 3 files changed, 105 insertions(+), 5 deletions(-) create mode 100644 src/private/json.nim diff --git a/src/private/json.nim b/src/private/json.nim new file mode 100644 index 0000000..4439be6 --- /dev/null +++ b/src/private/json.nim @@ -0,0 +1,94 @@ +type + Level = tuple[node: JsonNode, key: string] + +proc initLevel(node: JsonNode): Level = (node: node, key: nil) + +proc jsonFromScalar(content: string, typeHint: YamlTypeHint): JsonNode = + new(result) + case typeHint + of yTypeInteger: + result.kind = JInt + result.num = parseBiggestInt(content) + of yTypeFloat: + result.kind = JFloat + result.fnum = parseFloat(content) + of yTypeBoolean: + result.kind = JBool + result.bval = parseBool(content) + of yTypeNull: + result.kind = JNull + else: + result.kind = JString + result.str = content + +proc parseToJson*(s: string): seq[JsonNode] = + result = parseToJson(newStringStream(s)) + +proc parseToJson*(s: Stream): seq[JsonNode] = + newSeq(result, 0) + + var + levels = newSeq[Level]() + parser = newParser() + tagStr = parser.registerUri("tag:yaml.org,2002:str") + tagBool = parser.registerUri("tag:yaml.org,2002:bool") + tagNull = parser.registerUri("tag:yaml.org,2002:null") + tagInt = parser.registerUri("tag:yaml.org,2002:int") + tagFloat = parser.registerUri("tag:yaml.org,2002:float") + events = parser.parse(s) + + for event in events(): + case event.kind + of yamlStartDocument: + # we don't need to do anything here; root node will be created + # by first scalar, sequence or map event + discard + of yamlEndDocument: + # we can savely assume that levels has e length of exactly 1. + result.add(levels.pop().node) + of yamlStartSequence: + levels.add((node: newJArray(), key: cast[string](nil))) + of yamlStartMap: + levels.add((node: newJObject(), key: cast[string](nil))) + of yamlScalar: + case levels[levels.high].node.kind + of JArray: + levels[levels.high].node.elems.add( + jsonFromScalar(event.scalarContent, event.scalarType)) + of JObject: + if isNil(levels[levels.high].key): + # JSON only allows strings as keys + levels[levels.high].key = event.scalarContent + else: + levels[levels.high].node.fields.add( + (key: levels[levels.high].key, val: jsonFromScalar( + event.scalarContent, event.scalarType))) + levels[levels.high].key = nil + else: + discard # will never happen + of yamlEndSequence, yamlEndMap: + if levels.len > 1: + let level = levels.pop() + case levels[levels.high].node.kind + of JArray: + levels[levels.high].node.elems.add(level.node) + of JObject: + if isNil(levels[levels.high].key): + raise newException(ValueError, + "non-scalar as key not allowed in JSON") + else: + levels[levels.high].node.fields.add( + (key: levels[levels.high].key, val: level.node)) + levels[levels.high].key = nil + else: + discard # will never happen + else: + discard # wait for yamlEndDocument + of yamlWarning: + echo "YAML warning at line ", event.line, ", column ", event.column, + ": ", event.description + of yamlError: + echo "YAML error at line ", event.line, ", column ", event.column, + ": ", event.description + of yamlAlias: + discard # todo \ No newline at end of file diff --git a/src/yaml.nim b/src/yaml.nim index 3bd1a32..45baeaa 100644 --- a/src/yaml.nim +++ b/src/yaml.nim @@ -1,4 +1,4 @@ -import streams, unicode, lexbase, tables, strutils +import streams, unicode, lexbase, tables, strutils, json type YamlTypeHint* = enum @@ -41,7 +41,11 @@ type proc parse*(parser: YamlSequentialParser, s: Stream): iterator(): YamlParserEvent +proc parseToJson*(s: Stream): seq[JsonNode] +proc parseToJson*(s: string): seq[JsonNode] + # implementation include private.lexer -include private.sequential \ No newline at end of file +include private.sequential +include private.json \ No newline at end of file diff --git a/test/parsing.nim b/test/parsing.nim index b318eaa..99f0e9a 100644 --- a/test/parsing.nim +++ b/test/parsing.nim @@ -162,9 +162,11 @@ suite "Parsing": scalar("a true"), scalar("null d"), scalar("e 42"), scalar(""), endMap(), endDoc()) test "Parsing: Map in Sequence": - ensure(" - key: value", startDoc(), startSequence(), startMap(), - scalar("key"), scalar("value"), endMap(), endSequence(), - endDoc()) + ensure(" - key: value\n key2: value2\n -\n key3: value3", + startDoc(), startSequence(), startMap(), scalar("key"), + scalar("value"), scalar("key2"), scalar("value2"), endMap(), + startMap(), scalar("key3"), scalar("value3"), endMap(), + endSequence(), endDoc()) test "Parsing: Sequence in Map": ensure("key:\n - item1\n - item2", startDoc(), startMap(), scalar("key"), startSequence(), scalar("item1"), scalar("item2"),