From: Jean Privat Date: Wed, 19 Nov 2014 15:37:17 +0000 (-0500) Subject: Merge: Markdown clean X-Git-Tag: v0.6.11~26 X-Git-Url: http://nitlanguage.org?hp=d2b622af14ba1a5cff300c95838e458bcba7d8e6 Merge: Markdown clean Some cleaning for the markdown library. The moved method `token_at` will be used in a further PR. Pull-Request: #920 Reviewed-by: Jean Privat Reviewed-by: Lucas Bajolet --- diff --git a/contrib/github_merge.nit b/contrib/github_merge.nit index c30732f..0d5e052 100644 --- a/contrib/github_merge.nit +++ b/contrib/github_merge.nit @@ -20,14 +20,14 @@ import template redef class Object # Factorize cast - fun json_as_a: Array[nullable Object] do return self.as(Array[nullable Object]) + fun json_as_a: JsonArray do return self.as(JsonArray) # Factorize cast - fun json_as_map: Map[String, nullable Object] do return self.as(Map[String, nullable Object]) + fun json_as_map: JsonObject do return self.as(JsonObject) end redef class GithubCurl # Get a given pull request (PR) - fun getpr(number: Int): Map[String, nullable Object] + fun getpr(number: Int): JsonObject do var pr = get_and_check("https://api.github.com/repos/privat/nit/pulls/{number}") var prm = pr.json_as_map @@ -46,7 +46,7 @@ redef class GithubCurl end # Get reviewers of a PR - fun getrev(pr: Map[String, nullable Object]): Array[String] + fun getrev(pr: JsonObject): Array[String] do var number = pr["number"].as(Int) var user = pr["user"].json_as_map["login"].as(String) diff --git a/contrib/github_search_for_jni/src/github_search_for_jni.nit b/contrib/github_search_for_jni/src/github_search_for_jni.nit index d12f288..35b19b2 100644 --- a/contrib/github_search_for_jni/src/github_search_for_jni.nit +++ b/contrib/github_search_for_jni/src/github_search_for_jni.nit @@ -19,13 +19,12 @@ module github_search_for_jni import github_api -# The proprieties introduced by this redef are to be used only on HashMap +# The proprieties introduced by this redef are to be used only on a JSON object # representing a Github repository. -redef class HashMap[K, V] +redef class JsonObject # The repository has at least 50% Java code fun has_lots_of_java: Bool do - assert self isa HashMap[String, nullable Object] var java_count = 0 if keys.has("Java") then java_count = self["Java"].as(Int) @@ -43,7 +42,6 @@ redef class HashMap[K, V] # The repository has at least 100 lines of C code fun has_some_c: Bool do - assert self isa HashMap[String, nullable Object] var c_count = 0 if keys.has("C") then c_count = self["C"].as(Int) return c_count > 100 @@ -64,14 +62,14 @@ var per_page = 100 loop # Get a page of the main query var uri = "https://api.github.com/search/repositories?q={main_query}&page={page}&per_page={per_page}&sort=stars" - var obj = curl.get_and_check(uri).as(HashMap[String, nullable Object]) + var obj = curl.get_and_check(uri).as(JsonObject) # Main object has "total_count" and "items" - var items = obj["items"].as(Array[nullable Object]) + var items = obj["items"].as(JsonArray) # "items" is an array of Json objects for item in items do - assert item isa HashMap[String, nullable Object] + assert item isa JsonObject # Each item has "name" and "languages_url" assert item.keys.has("name") @@ -79,7 +77,7 @@ loop # Download the language list var lang_url = item["languages_url"].as(String) - var langs = curl.get_and_check(lang_url).as(HashMap[String, nullable Object]) + var langs = curl.get_and_check(lang_url).as(JsonObject) # The project is of interest if it has lots of Java and at least some C var may_be_of_interest = langs.has_lots_of_java and langs.has_some_c diff --git a/contrib/nitcc/examples/json.sablecc b/contrib/nitcc/examples/json.sablecc index d1e9c6b..0de8914 100644 --- a/contrib/nitcc/examples/json.sablecc +++ b/contrib/nitcc/examples/json.sablecc @@ -9,7 +9,18 @@ frac = '.' d+; exp = e d+; e = ('e'|'E') ('+'|'-')?; -string = '"' (Any-'\\'-'"' | '\\'Any)* '"'; +hexdig = '0'..'9' | 'a'..'z' | 'A'..'Z'; +string = '"' (Any - '\\' - '"' | '\\' ( + '\\' | + '"' | + '/' | + 'b' | + 'f' | + 'n' | + 'r' | + 't' | + 'u' hexdig hexdig hexdig hexdig + ))* '"'; blank = (' '|'\n'|'\t')+; diff --git a/lib/counter.nit b/lib/counter.nit index 670fc59..3d1555b 100644 --- a/lib/counter.nit +++ b/lib/counter.nit @@ -19,10 +19,33 @@ import poset # A counter counts occurrences of things # Use this instead of a `HashMap[E, Int]` +# +# ~~~ +# var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"]) +# assert c["a"] == 2 +# assert c["b"] == 3 +# assert c["c"] == 1 +# assert c["d"] == 0 +# ~~~ +# +# The counter class can also be used to gather statistical informations. +# +# ~~~~ +# assert c.length == 3 # because 3 distinct values +# assert c.max == "b" # because "b" has the most count (3) +# assert c.avg == 2.0 # because it is the mean of the counts +# ~~~~ class Counter[E: Object] super Map[E, Int] # Total number of counted occurrences + # + # ~~~ + # var c = new Counter[String] + # assert c.sum == 0 + # c.inc_all(["a", "a", "b", "b", "b", "c"]) + # assert c.sum == 6 + # ~~~ var sum: Int = 0 private var map = new HashMap[E, Int] @@ -64,7 +87,24 @@ class Counter[E: Object] sum += 1 end + # Count one more for each element of `es` + fun inc_all(es: Collection[E]) + do + for e in es do inc(e) + end + + # A new Counter initialized with `inc_all`. + init from(es: Collection[E]) + do + inc_all(es) + end + # Return an array of elements sorted by occurrences + # + # ~~~ + # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"]) + # assert c.sort == ["c", "a", "b"] + # ~~~ fun sort: Array[E] do var res = map.keys.to_a @@ -77,7 +117,7 @@ class Counter[E: Object] # @toimplement by default just call `to_s` on the element protected fun element_to_s(e: E): String do - do return e.to_s + return e.to_s end # Display statistical information @@ -130,7 +170,14 @@ class Counter[E: Object] end end - # Return the element with the highest value + # Return the element with the highest value (aka. the mode) + # + # ~~~ + # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"]) + # assert c.max == "b" + # ~~~ + # + # If more than one max exists, the first one is returned. fun max: nullable E do var max: nullable Int = null var elem: nullable E = null @@ -144,6 +191,13 @@ class Counter[E: Object] end # Return the couple with the lowest value + # + # ~~~ + # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"]) + # assert c.min == "c" + # ~~~ + # + # If more than one min exists, the first one is returned. fun min: nullable E do var min: nullable Int = null var elem: nullable E = null @@ -156,13 +210,24 @@ class Counter[E: Object] return elem end - # Values average + # Values average (aka. arithmetic mean) + # + # ~~~ + # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"]) + # assert c.avg == 2.0 + # ~~~ fun avg: Float do if values.is_empty then return 0.0 return (sum / values.length).to_f end # The standard derivation of the counter values + # + # ~~~ + # var c = new Counter[String].from(["a", "a", "b", "b", "b", "c"]) + # assert c.std_dev > 0.81 + # assert c.std_dev < 0.82 + # ~~~ fun std_dev: Float do var avg = self.avg var sum = 0.0 diff --git a/lib/github_api.nit b/lib/github_api.nit index 2394592..39fca84 100644 --- a/lib/github_api.nit +++ b/lib/github_api.nit @@ -45,7 +45,7 @@ class GithubCurl # Get the requested URI, and check the HTTP response. Then convert to JSON # and check for Github errors. - fun get_and_check(uri: String): nullable Object + fun get_and_check(uri: String): nullable Jsonable do var request = new CurlHTTPRequest(uri, self) request.user_agent = user_agent @@ -53,8 +53,8 @@ class GithubCurl var response = request.execute if response isa CurlResponseSuccess then - var obj = response.body_str.json_to_nit_object - if obj isa HashMap[String, nullable Object] then + var obj = response.body_str.parse_json + if obj isa JsonObject then if obj.keys.has("message") then print "Message from Github API: {obj["message"] or else ""}" print "Requested URI: {uri}" diff --git a/lib/json/dynamic.nit b/lib/json/dynamic.nit index ec3ba25..d15ceb7 100644 --- a/lib/json/dynamic.nit +++ b/lib/json/dynamic.nit @@ -20,8 +20,8 @@ # to get the underlying Json data. It can also be used as any Json types. module dynamic +import error private import static -import standard class JsonValue var value: nullable Object @@ -123,14 +123,14 @@ class JsonValue # # assert """{"a": 123}""".to_json_value.is_map # assert not "123".to_json_value.is_map - fun is_map: Bool do return value isa HashMap[String, nullable Object] + fun is_map: Bool do return value isa MapRead[String, nullable Object] # Get this value as a `Map[String, JsonValue]` # # require: `self.is_map` fun to_map: Map[String, JsonValue] do var value = value - assert value isa HashMap[String, nullable Object] + assert value isa MapRead[String, nullable Object] var map = new HashMap[String, JsonValue] for k, v in value do map[k] = new JsonValue(v) @@ -145,7 +145,7 @@ class JsonValue # assert "[1, 2, 3, 4, 5]".to_json_value.is_array # assert "[null, true, false, 0.0, 1, \"str\"]".to_json_value.is_array # assert """["a", "b", "c"]""".to_json_value.is_array - fun is_array: Bool do return value isa Array[nullable Object] + fun is_array: Bool do return value isa SequenceRead[nullable Object] # Get this value as an `Array[JsonValue]` # @@ -155,13 +155,42 @@ class JsonValue fun to_a: Array[JsonValue] do var value = value - assert value isa Array[nullable Object] + assert value isa SequenceRead[nullable Object] var a = new Array[JsonValue] for e in value do a.add(new JsonValue(e)) return a end + ### Error + + # Is this value an error? + # + # assert "[]".to_json_value[0].is_error + # assert "[".to_json_value.is_error + # assert not "[]".to_json_value.is_error + fun is_error: Bool do return value isa Error + + # Get this value as a `Error`. + # + # require: `self.is_error` + fun to_error: Error do return value.as(Error) + + ### JsonParseError + + # Is this value a parse error? + # + # assert "[".to_json_value.is_parse_error + # assert not "[]".to_json_value.is_parse_error + fun is_parse_error: Bool do return value isa JsonParseError + + # Get this value as a `JsonParseError`. + # + # require: `self.is_parse_error` + fun to_parse_error: JsonParseError do return value.as(JsonParseError) + + ### Children access + # Iterator over the values of the array `self` # # require: `self.is_array` @@ -181,17 +210,39 @@ class JsonValue # assert """{"a": 123}""".to_json_value["a"].to_i == 123 # assert """{"123": "a"}""".to_json_value[123].to_s == "a" # assert """{"John Smith": 1980}""".to_json_value[["John ", "Smith"]].to_i == 1980 + # assert """{"a": 123}""".to_json_value["b"].is_error # # assert """["a", "b", "c"]""".to_json_value[0].to_s == "a" - fun [](key: Object): JsonValue - do + # assert """["a", "b", "c"]""".to_json_value[3].is_error + fun [](key: Object): JsonValue do var value = value - if value isa HashMap[String, nullable Object] then - return new JsonValue(value[key.to_s]) - else if value isa Array[nullable Object] then - assert key isa Int - return new JsonValue(value[key]) - else abort + var result: nullable Object + if is_error then + return self + else if value isa MapRead[String, nullable Object] then + key = key.to_s + if value.has_key(key) then + result = value[key] + else + result = new JsonKeyError("Key `{key}` not found.", self, key) + end + else if value isa SequenceRead[nullable Object] then + if key isa Int then + if key < value.length and key >= 0 then + result = value[key] + else + result = new JsonKeyError("Index `{key}` out of bounds.", + self, key) + end + else + result = new JsonKeyError("Invalid key type. Expecting `Int`. Got `{key.class_name}`.", + self, key) + end + else + result = new JsonKeyError("Invalid `[]` access on a `{json_type}` JsonValue.", + self, key) + end + return new JsonValue(result) end # Advanced query to get a value within the map `self` or it's children. @@ -203,24 +254,78 @@ class JsonValue # assert """{"a": {"t": true, "f": false}}""".to_json_value.get("a").is_map # assert """{"a": {"t": true, "f": false}}""".to_json_value.get("a.t").to_bool # assert not """{"a": {"t": true, "f": false}}""".to_json_value.get("a.f").to_bool + # assert """{"a": {"t": true, "f": false}}""".to_json_value.get("a.t.t").is_error # assert """{"a": {"b": {"c": {"d": 123}}}}""".to_json_value.get("a.b.c.d").to_i == 123 - fun get(query: String): JsonValue - do + # assert """{"a": {"b": {"c": {"d": 123}}}}""".to_json_value.get("a.z.c.d").is_error + fun get(query: String): JsonValue do var keys = query.split(".") var value = value - for key in keys do - assert value isa HashMap[String, nullable Object] - value = value[key] + if is_error then return self + for i in [0..keys.length[ do + var key = keys[i] + if value isa MapRead[String, nullable Object] then + if value.has_key(key) then + value = value[key] + else + var sub_query = sub_query_to_s(keys, i) + var e = new JsonKeyError("Key `{key}` not found.", + self, sub_query) + return new JsonValue(e) + end + else + var sub_query = sub_query_to_s(keys, i) + var val_type = (new JsonValue(value)).json_type + var e = new JsonKeyError("Value at `{sub_query}` is not a map. Got type `{val_type}`", + self, sub_query) + return new JsonValue(e) + end end return new JsonValue(value) end + + # Concatenate all keys up to `last` for debugging purposes. + # + # Note: This method deletes elements in `keys`. + private fun sub_query_to_s(keys: Array[String], last: Int): String do + last += 1 + for j in [last..keys.length[ do keys.pop + return keys.join(".") + end + + # Return a human-readable description of the type. + # + # For debugging purpose only. + fun json_type: String do + if is_array then return "array" + if is_bool then return "bool" + if is_float then return "float" + if is_int then return "int" + if is_null then return "null" + if is_map then return "map" + if is_string then return "string" + if is_parse_error then return "parse_error" + if is_error then return "error" + return "undefined" + end end -redef class String +# Keyed access failed. +class JsonKeyError + super Error + + # The value on which the access was requested. + var json_value: JsonValue + + # The requested key. + # + # In the case of `JsonValue.get`, the sub-query that failed. + var key: Object +end + +redef class Text # Parse `self` to obtain a `JsonValue` - fun to_json_value: JsonValue - do - var value = json_to_nit_object + fun to_json_value: JsonValue do + var value = parse_json return new JsonValue(value) end end diff --git a/lib/json/error.nit b/lib/json/error.nit new file mode 100644 index 0000000..dab1689 --- /dev/null +++ b/lib/json/error.nit @@ -0,0 +1,31 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Errors related to JSON parsing. +module json::error + +import nitcc_runtime + +# Ill-formed JSON. +class JsonParseError + super Error + + # The location of the error in the original text. + var position: nullable Position + + redef fun to_s do + var p = position + if p isa Position then + return "[{p}] {super}" + else + return super + end + end +end diff --git a/lib/json/json.nit b/lib/json/json.nit index 9353350..b458d01 100644 --- a/lib/json/json.nit +++ b/lib/json/json.nit @@ -1,6 +1,7 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Alexis Laferrière +# Copyright 2014 Jean-Christophe Beaupré # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,14 +15,19 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Offers two APIs to manipulate read Json strings. +# Provides two APIs to manipulate JSON strings. +# +# Both `dynamic` and `static` modules provide at least a method to parse a +# value written in JSON, but only `static` provide a method to translate a +# value into JSON. # # The `dynamic` module provides a simple interface to get information -# from a Json document. You must be careful as all services are provided on +# from a JSON document. You must be careful as all services are provided on # each nodes and a wrongful use can `abort`. # -# The `static` module converts a Json string to a nullable Nit object. The object -# must then be type checked before it can be used. +# The `static` module provides a common interface for anything that can be +# translated into a JSON document. The provided parsing method return a +# nullable Nit object that must then be type-checked before it can be used. module json import static diff --git a/lib/json/json_lexer.nit b/lib/json/json_lexer.nit index f597c82..3273525 100644 --- a/lib/json/json_lexer.nit +++ b/lib/json/json_lexer.nit @@ -36,6 +36,10 @@ redef class Object private fun dfastate_28: DFAState28 do return once new DFAState28 private fun dfastate_29: DFAState29 do return once new DFAState29 private fun dfastate_30: DFAState30 do return once new DFAState30 + private fun dfastate_31: DFAState31 do return once new DFAState31 + private fun dfastate_32: DFAState32 do return once new DFAState32 + private fun dfastate_33: DFAState33 do return once new DFAState33 + private fun dfastate_34: DFAState34 do return once new DFAState34 end class MyNToken super NToken @@ -391,6 +395,75 @@ private class DFAState30 super DFAState redef fun trans(char) do var c = char.ascii - return dfastate_2 + if c <= 33 then return null + if c <= 34 then return dfastate_2 + if c <= 46 then return null + if c <= 47 then return dfastate_2 + if c <= 91 then return null + if c <= 92 then return dfastate_2 + if c <= 97 then return null + if c <= 98 then return dfastate_2 + if c <= 101 then return null + if c <= 102 then return dfastate_2 + if c <= 109 then return null + if c <= 110 then return dfastate_2 + if c <= 113 then return null + if c <= 114 then return dfastate_2 + if c <= 115 then return null + if c <= 116 then return dfastate_2 + if c <= 117 then return dfastate_31 + return null + end +end +private class DFAState31 + super DFAState + redef fun trans(char) do + var c = char.ascii + if c <= 47 then return null + if c <= 57 then return dfastate_32 + if c <= 64 then return null + if c <= 90 then return dfastate_32 + if c <= 96 then return null + if c <= 122 then return dfastate_32 + return null + end +end +private class DFAState32 + super DFAState + redef fun trans(char) do + var c = char.ascii + if c <= 47 then return null + if c <= 57 then return dfastate_33 + if c <= 64 then return null + if c <= 90 then return dfastate_33 + if c <= 96 then return null + if c <= 122 then return dfastate_33 + return null + end +end +private class DFAState33 + super DFAState + redef fun trans(char) do + var c = char.ascii + if c <= 47 then return null + if c <= 57 then return dfastate_34 + if c <= 64 then return null + if c <= 90 then return dfastate_34 + if c <= 96 then return null + if c <= 122 then return dfastate_34 + return null + end +end +private class DFAState34 + super DFAState + redef fun trans(char) do + var c = char.ascii + if c <= 47 then return null + if c <= 57 then return dfastate_2 + if c <= 64 then return null + if c <= 90 then return dfastate_2 + if c <= 96 then return null + if c <= 122 then return dfastate_2 + return null end end diff --git a/lib/json/static.nit b/lib/json/static.nit index 51800fc..29e5c22 100644 --- a/lib/json/static.nit +++ b/lib/json/static.nit @@ -1,6 +1,8 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Alexis Laferrière +# Copyright 2014 Alexandre Terrasa +# Copyright 2014 Jean-Christophe Beaupré # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -16,17 +18,308 @@ # Static interface to get Nit objects from a Json string. # -# `String::json_to_nit_object` returns an equivalent Nit object from +# `Text::parse_json` returns an equivalent Nit object from # the Json source. This object can then be type checked by the usual # languages features (`isa` and `as`). module static -import standard +import error private import json_parser private import json_lexer +# Something that can be translated to JSON. +interface Jsonable + # Encode `self` in JSON. + # + # SEE: `append_json` + fun to_json: String is abstract + + # Append the JSON representation of `self` to the specified buffer. + # + # SEE: `to_json` + fun append_json(buffer: Buffer) do + buffer.append(to_json) + end +end + +redef class Text + super Jsonable + + redef fun append_json(buffer) do + buffer.add '\"' + for i in [0..self.length[ do + var char = self[i] + if char == '\\' then + buffer.append "\\\\" + else if char == '\"' then + buffer.append "\\\"" + else if char == '\/' then + buffer.append "\\/" + else if char < 16.ascii then + if char == '\n' then + buffer.append "\\n" + else if char == '\r' then + buffer.append "\\r" + else if char == '\t' then + buffer.append "\\t" + else if char == 0x0C.ascii then + buffer.append "\\f" + else if char == 0x08.ascii then + buffer.append "\\b" + else + buffer.append "\\u000{char.ascii.to_hex}" + end + else if char < ' ' then + buffer.append "\\u00{char.ascii.to_hex}" + else + buffer.add char + end + end + buffer.add '\"' + end + + # Encode `self` in JSON. + # + # assert "\t\"http://example.com\"\r\n\0\\".to_json == + # "\"\\t\\\"http:\\/\\/example.com\\\"\\r\\n\\u0000\\\\\"" + redef fun to_json do + var buffer = new FlatBuffer + append_json(buffer) + return buffer.write_to_string + end + + # Parse `self` as JSON. + # + # If `self` is not a valid JSON document or contains an unsupported escape + # sequence, return a `JSONParseError`. + # + # Example with `JsonObject`: + # + # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".parse_json + # assert obj isa JsonObject + # assert obj["foo"] isa JsonObject + # assert obj["foo"].as(JsonObject)["bar"] == true + # + # Example with `JsonArray`: + # + # var arr = "[1, 2, 3]".parse_json + # assert arr isa JsonArray + # assert arr.length == 3 + # assert arr.first == 1 + # assert arr.last == 3 + # + # Example with `String`: + # + # var str = "\"foo, bar, baz\"".parse_json + # assert str isa String + # assert str == "foo, bar, baz" + # + # Example of a syntaxic error: + # + # var bad = "\{foo: \"bar\"\}".parse_json + # assert bad isa JsonParseError + # assert bad.position.col_start == 2 + fun parse_json: nullable Jsonable do + var lexer = new Lexer_json(to_s) + var parser = new Parser_json + var tokens = lexer.lex + parser.tokens.add_all(tokens) + var root_node = parser.parse + if root_node isa NStart then + return root_node.n_0.to_nit_object + else if root_node isa NError then + return new JsonParseError(root_node.message, root_node.position) + else abort + end +end + +redef class Buffer + + # Append the JSON representation of `jsonable` to `self`. + # + # Append `"null"` for `null`. + private fun append_json_of(jsonable: nullable Jsonable) do + if jsonable isa Jsonable then + append jsonable.to_json + else + append "null" + end + end +end + +redef class Int + super Jsonable + + # Encode `self` in JSON. + # + # assert 0.to_json == "0" + # assert (-42).to_json == "-42" + redef fun to_json do return self.to_s +end + +redef class Float + super Jsonable + + # Encode `self` in JSON. + # + # Note: Because this method use `to_s`, it may lose precision. + # + # ~~~ + # # Will not work as expected. + # # assert (-0.0).to_json == "-0.0" + # + # assert (.5).to_json == "0.5" + # assert (0.0).to_json == "0.0" + # ~~~ + redef fun to_json do return self.to_s +end + +redef class Bool + super Jsonable + + # Encode `self` in JSON. + # + # assert true.to_json == "true" + # assert false.to_json == "false" + redef fun to_json do return self.to_s +end + +# A map that can be translated into a JSON object. +interface JsonMapRead[K: String, V: nullable Jsonable] + super MapRead[K, V] + super Jsonable + + redef fun append_json(buffer) do + buffer.append "\{" + var it = iterator + if it.is_ok then + append_json_entry(it, buffer) + while it.is_ok do + buffer.append "," + append_json_entry(it, buffer) + end + end + it.finish + buffer.append "\}" + end + + # Encode `self` in JSON. + # + # var obj = new JsonObject + # obj["foo"] = "bar" + # assert obj.to_json == "\{\"foo\":\"bar\"\}" + # obj = new JsonObject + # obj["baz"] = null + # assert obj.to_json == "\{\"baz\":null\}" + redef fun to_json do + var buffer = new FlatBuffer + append_json(buffer) + return buffer.write_to_string + end + + private fun append_json_entry(iterator: MapIterator[String, nullable Jsonable], + buffer: Buffer) do + buffer.append iterator.key.to_json + buffer.append ":" + buffer.append_json_of(iterator.item) + iterator.next + end +end + +# A JSON Object. +class JsonObject + super JsonMapRead[String, nullable Jsonable] + super HashMap[String, nullable Jsonable] +end + +# A sequence that can be translated into a JSON array. +class JsonSequenceRead[E: nullable Jsonable] + super Jsonable + super SequenceRead[E] + + redef fun append_json(buffer) do + buffer.append "[" + var it = iterator + if it.is_ok then + append_json_entry(it, buffer) + while it.is_ok do + buffer.append "," + append_json_entry(it, buffer) + end + end + it.finish + buffer.append "]" + end + + # Encode `self` in JSON. + # + # var arr = new JsonArray.with_items("foo", null) + # assert arr.to_json == "[\"foo\",null]" + # arr.pop + # assert arr.to_json =="[\"foo\"]" + # arr.pop + # assert arr.to_json =="[]" + redef fun to_json do + var buffer = new FlatBuffer + append_json(buffer) + return buffer.write_to_string + end + + private fun append_json_entry(iterator: Iterator[nullable Jsonable], + buffer: Buffer) do + buffer.append_json_of(iterator.item) + iterator.next + end +end + +# A JSON array. +class JsonArray + super JsonSequenceRead[nullable Jsonable] + super Array[nullable Jsonable] +end + +redef class JsonParseError + super Jsonable + + # Get the JSON representation of `self`. + # + # var err = new JsonParseError("foo", new Position(1, 2, 3, 4, 5, 6)) + # assert err.to_json == "\{\"error\":\"JsonParseError\"," + + # "\"position\":\{" + + # "\"pos_start\":1,\"pos_end\":2," + + # "\"line_start\":3,\"line_end\":4," + + # "\"col_start\":5,\"col_end\":6" + + # "\},\"message\":\"foo\"\}" + redef fun to_json do + return "\{\"error\":\"JsonParseError\"," + + "\"position\":{position.to_json}," + + "\"message\":{message.to_json}\}" + end +end + +redef class Position + super Jsonable + + # Get the JSON representation of `self`. + # + # var pos = new Position(1, 2, 3, 4, 5, 6) + # assert pos.to_json == "\{" + + # "\"pos_start\":1,\"pos_end\":2," + + # "\"line_start\":3,\"line_end\":4," + + # "\"col_start\":5,\"col_end\":6" + + # "\}" + redef fun to_json do + return "\{\"pos_start\":{pos_start},\"pos_end\":{pos_end}," + + "\"line_start\":{line_start},\"line_end\":{line_end}," + + "\"col_start\":{col_start},\"col_end\":{col_end}\}" + end +end + +################################################################################ +# Redef parser + redef class Nvalue - fun to_nit_object: nullable Object is abstract + fun to_nit_object: nullable Jsonable is abstract end redef class Nvalue_number @@ -55,17 +348,46 @@ redef class Nvalue_null end redef class Nstring - # FIXME support \n, etc. - fun to_nit_string: String do return text.substring(1, text.length-2). - replace("\\\\", "\\").replace("\\\"", "\"").replace("\\b", "\b"). - replace("\\/", "/").replace("\\n", "\n").replace("\\r", "\r"). - replace("\\t", "\t") + fun to_nit_string: String do + var res = new FlatBuffer + var i = 1 + while i < text.length - 1 do + var char = text[i] + if char == '\\' then + i += 1 + char = text[i] + if char == 'b' then + char = 0x08.ascii + else if char == 'f' then + char = 0x0C.ascii + else if char == 'n' then + char = '\n' + else if char == 'r' then + char = '\r' + else if char == 't' then + char = '\t' + else if char == 'u' then + var code = text.substring(i + 1, 4).to_hex + # TODO UTF-16 escaping is not supported yet. + if code >= 128 then + char = '?' + else + char = code.ascii + end + i += 4 + end + # `"`, `/` or `\` => Keep `char` as-is. + end + res.add char + i += 1 + end + return res.write_to_string + end end redef class Nvalue_object - redef fun to_nit_object - do - var obj = new HashMap[String, nullable Object] + redef fun to_nit_object do + var obj = new JsonObject var members = n_members if members != null then var pairs = members.pairs @@ -94,13 +416,13 @@ end redef class Npair fun name: String do return n_string.to_nit_string - fun value: nullable Object do return n_value.to_nit_object + fun value: nullable Jsonable do return n_value.to_nit_object end redef class Nvalue_array redef fun to_nit_object do - var arr = new Array[nullable Object] + var arr = new JsonArray var elements = n_elements if elements != null then var items = elements.items @@ -126,25 +448,3 @@ end redef class Nelements_head redef fun items do return [n_value] end - -redef class Text - fun json_to_nit_object: nullable Object - do - var lexer = new Lexer_json(to_s) - var parser = new Parser_json - var tokens = lexer.lex - parser.tokens.add_all(tokens) - var root_node = parser.parse - if root_node isa NStart then - return root_node.n_0.to_nit_object - else if root_node isa NLexerError then - var pos = root_node.position - print "Json lexer error: {root_node.message} at {pos or else ""} for {root_node}" - return null - else if root_node isa NParserError then - var pos = root_node.position - print "Json parsing error: {root_node.message} at {pos or else ""} for {root_node}" - return null - else abort - end -end diff --git a/lib/json_serialization.nit b/lib/json_serialization.nit index 6a2d82a..f090b24 100644 --- a/lib/json_serialization.nit +++ b/lib/json_serialization.nit @@ -71,16 +71,16 @@ end class JsonDeserializer super Deserializer - var root: nullable Object - var path = new Array[HashMap[String, nullable Object]] + var root: nullable Jsonable + var path = new Array[JsonObject] var id_to_object = new HashMap[Int, Object] var just_opened_id: nullable Int = null init(text: Text) do - var root = text.json_to_nit_object - if root isa HashMap[String, nullable Object] then path.add(root) + var root = text.parse_json + if root isa JsonObject then path.add(root) self.root = root end @@ -107,7 +107,7 @@ class JsonDeserializer # Convert from simple Json object to Nit object private fun convert_object(object: nullable Object): nullable Object do - if object isa HashMap[String, nullable Object] then + if object isa JsonObject then assert object.keys.has("__kind") var kind = object["__kind"] @@ -197,18 +197,11 @@ redef class Bool end redef class Char - redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": \"{to_s.to_json_s}\"\}" + redef fun serialize_to_json(v) do v.stream.write "\{\"__kind\": \"char\", \"__val\": {to_s.to_json}\}" end redef class String - redef fun serialize_to_json(v) do v.stream.write("\"{to_json_s}\"") - - private fun to_json_s: String do return self.replace("\\", "\\\\"). - replace("\"", "\\\"").replace("/", "\\/"). - replace("\n", "\\n").replace("\r", "\\r").replace("\t", "\\t") - # FIXME add support for unicode char when supported by Nit strings - # FIXME add support for \f! # .replace("\f", "\\f") - # FIXME add support for \b .replace("\b", "\\b") + redef fun serialize_to_json(v) do v.stream.write(to_json) end redef class NativeString @@ -259,7 +252,7 @@ redef class Array[E] v.notify_of_creation self var length = v.deserialize_attribute("__length").as(Int) - var arr = v.path.last["__items"].as(Array[nullable Object]) + var arr = v.path.last["__items"].as(SequenceRead[nullable Object]) for i in length.times do var obj = v.convert_object(arr[i]) self.add obj diff --git a/lib/neo4j/curl_json.nit b/lib/neo4j/curl_json.nit index dc5c8ac..e458eb1 100644 --- a/lib/neo4j/curl_json.nit +++ b/lib/neo4j/curl_json.nit @@ -15,7 +15,7 @@ # cURL requests compatible with the JSON REST APIs. module curl_json -import jsonable +import json::static intrude import curl # An abstract request that defines most of the standard options for Neo4j REST API diff --git a/lib/neo4j/error.nit b/lib/neo4j/error.nit new file mode 100644 index 0000000..f9df000 --- /dev/null +++ b/lib/neo4j/error.nit @@ -0,0 +1,34 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# This file is free software, which comes along with NIT. This software is +# distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; +# without even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. You can modify it is you want, provided this header +# is kept unaltered, and a notification of the changes is added. +# You are allowed to redistribute it and sell it, alone or is a part of +# another product. + +# Errors thrown by the `neo4j` library. +module neo4j::error + +import json::static + +# An error thrown by the `neo4j` API. +# +# var error = new NeoError("ErrorMessage", "ErrorName") +# assert error.to_json == "\{\"error\":\"ErrorName\",\"message\":\"ErrorMessage\"\}" +class NeoError + super Error + super Jsonable + + # The name of the error. + # + # Used to programmatically distinguish this kind of error from others. + var name: String + + redef fun to_json do + return "\{\"error\":{name.to_json},\"message\":{message.to_json}\}" + end + + redef fun to_s do return "[{name}] {super}" +end diff --git a/lib/neo4j/json_store.nit b/lib/neo4j/json_store.nit index 49e055c..cf9f441 100644 --- a/lib/neo4j/json_store.nit +++ b/lib/neo4j/json_store.nit @@ -70,7 +70,7 @@ class JsonGraph # end # assert 2 == graph.nodes.length init from_json(t: Text) do - from_json_object(t.to_jsonable.as(JsonObject)) + from_json_object(t.parse_json.as(JsonObject)) end # Retrieve the graph from the specified JSON object. @@ -138,7 +138,7 @@ redef class NeoNode # assert ["foo", "Bar"] == node.labels # assert 42 == node["baz"] init from_json(t: Text) do - from_json_object(t.to_jsonable.as(JsonObject)) + from_json_object(t.parse_json.as(JsonObject)) end # Retrieve the node from the specified JSON value. diff --git a/lib/neo4j/jsonable.nit b/lib/neo4j/jsonable.nit deleted file mode 100644 index c82a240..0000000 --- a/lib/neo4j/jsonable.nit +++ /dev/null @@ -1,430 +0,0 @@ -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -# Introduce base classes and services for JSON handling. -module jsonable - -import standard -private import json::json_parser -private import json::json_lexer - -# Something that can be translated to JSON -interface Jsonable - # Get the JSON representation of `self` - fun to_json: String is abstract -end - -redef class String - super Jsonable - - redef fun to_json do - var res = new FlatBuffer - res.add '\"' - for i in [0..self.length[ do - var char = self[i] - if char == '\\' then - res.append("\\\\") - continue - else if char == '\"' then - res.append("\\\"") - continue - else if char == '\/' then - res.append("\\/") - continue - else if char == '\n' then - res.append("\\n") - continue - else if char == '\r' then - res.append("\\r") - continue - else if char == '\t' then - res.append("\\t") - continue - end - res.add char - end - res.add '\"' - return res.write_to_string - end -end - -redef class Int - super Jsonable - - redef fun to_json do return self.to_s -end - -redef class Float - super Jsonable - - redef fun to_json do return self.to_s -end - -redef class Bool - super Jsonable - - redef fun to_json do return self.to_s -end - -# A JSON Object representation that behaves like a `Map` -class JsonObject - super Jsonable - super Map[String, nullable Jsonable] - - private var map = new HashMap[String, nullable Jsonable] - - # Create an empty `JsonObject` - # - # var obj = new JsonObject - # assert obj.is_empty - init do end - - # Init the JSON Object from a Nit `Map` - # - # var map = new HashMap[String, String] - # map["foo"] = "bar" - # map["goo"] = "baz" - # var obj = new JsonObject.from(map) - # assert obj.length == 2 - # assert obj["foo"] == "bar" - # assert obj["goo"] == "baz" - init from(items: Map[String, nullable Jsonable]) do - for k, v in items do map[k] = v - end - - redef fun [](key) do return map[key] - redef fun []=(key, value) do map[key] = value - redef fun clear do map.clear - redef fun has_key(key) do return map.has_key(key) - redef fun is_empty do return map.is_empty - redef fun iterator do return map.iterator - redef fun keys do return map.keys - redef fun values do return map.values - redef fun length do return map.length - - # Advanced query to get a value within `self` or its children. - # - # A query is composed of the keys to each object seperated by '.'. - # - # REQUIRE `self.has_key(query)` - # - # var obj1 = new JsonObject - # obj1["baz"] = "foobarbaz" - # var obj2 = new JsonObject - # obj2["bar"] = obj1 - # var obj3 = new JsonObject - # obj3["foo"] = obj2 - # assert obj3.get("foo.bar.baz") == "foobarbaz" - fun get(query: String): nullable Jsonable do - var keys = query.split(".").reversed - var key = keys.pop - - assert has_key(key) - var node = self[key] - - while not keys.is_empty do - key = keys.pop - assert node isa JsonObject and node.has_key(key) - node = node[key] - end - return node - end - - # Create an empty `JsonObject` - # - # var obj = new JsonObject - # obj["foo"] = "bar" - # assert obj.to_json == "\{\"foo\": \"bar\"\}" - redef fun to_json do - var tpl = new Array[String] - tpl.add "\{" - var vals = new Array[String] - for k, v in self do - if v == null then - vals.add "{k.to_json}: null" - else - vals.add "{k.to_json}: {v.to_json}" - end - end - tpl.add vals.join(",") - tpl.add "\}" - return tpl.join("") - end - - redef fun to_s do return to_json -end - -# A JSON Array representation that behaves like a `Sequence` -class JsonArray - super Jsonable - super Sequence[nullable Jsonable] - - private var array = new Array[nullable Jsonable] - - init do end - - # init the JSON Array from a Nit `Collection` - init from(items: Collection[nullable Jsonable]) do - array.add_all(items) - end - - redef fun [](key) do return array[key] - redef fun []=(key, value) do array[key] = value - redef fun clear do array.clear - redef fun insert(item, index) do array.insert(item, index) - redef fun is_empty do return array.is_empty - redef fun iterator do return array.iterator - redef fun length do return array.length - redef fun pop do return array.pop - redef fun push(value) do array.push(value) - redef fun remove_at(index) do array.remove_at(index) - redef fun shift do return array.shift - redef fun unshift(e) do array.unshift(e) - - redef fun to_json do - var tpl = new Array[String] - tpl.add "[" - var vals = new Array[String] - for v in self do - if v == null then - vals.add "null" - else - vals.add v.to_json - end - end - tpl.add vals.join(",") - tpl.add "]" - return tpl.join("") - end - - redef fun to_s do return to_json -end - -# An error in JSON format that can be returned by tools using JSON like parsers. -# -# var error = new JsonError("ErrorCode", "ErrorMessage") -# assert error.to_s == "ErrorCode: ErrorMessage" -# assert error.to_json == "\{\"error\": \"ErrorCode\", \"message\": \"ErrorMessage\"\}" -class JsonError - super Jsonable - - # The error code - var error: String - - # The error message - var message: String - - redef fun to_json do - var tpl = new Array[String] - tpl.add "\{" - tpl.add "\"error\": {error.to_json}, " - tpl.add "\"message\": {message.to_json}" - tpl.add "\}" - return tpl.join("") - end - - redef fun to_s do return "{error}: {message}" -end - -# Redef parser - -redef class Nvalue - private fun to_nit_object: nullable Jsonable is abstract -end - -redef class Nvalue_number - redef fun to_nit_object - do - var text = n_number.text - if text.chars.has('.') or text.chars.has('e') or text.chars.has('E') then return text.to_f - return text.to_i - end -end - -redef class Nvalue_string - redef fun to_nit_object do return n_string.to_nit_string -end - -redef class Nvalue_true - redef fun to_nit_object do return true -end - -redef class Nvalue_false - redef fun to_nit_object do return false -end - -redef class Nvalue_null - redef fun to_nit_object do return null -end - -redef class Nstring - # FIXME support \n, etc. - fun to_nit_string: String do - var res = new FlatBuffer - var skip = false - for i in [1..text.length-2] do - if skip then - skip = false - continue - end - var char = text[i] - if char == '\\' and i < text.length - 2 then - if text[i + 1] == '\\' then - res.add('\\') - skip = true - continue - end - if text[i + 1] == '\"' then - res.add('\"') - skip = true - continue - end - if text[i + 1] == '/' then - res.add('\/') - skip = true - continue - end - if text[i + 1] == 'n' then - res.add('\n') - skip = true - continue - end - if text[i + 1] == 'r' then - res.add('\r') - skip = true - continue - end - if text[i + 1] == 't' then - res.add('\t') - skip = true - continue - end - end - res.add char - end - return res.write_to_string - end -end - -redef class Nvalue_object - redef fun to_nit_object - do - var obj = new JsonObject - var members = n_members - if members != null then - var pairs = members.pairs - for pair in pairs do obj[pair.name] = pair.value - end - return obj - end -end - -redef class Nmembers - fun pairs: Array[Npair] is abstract -end - -redef class Nmembers_tail - redef fun pairs - do - var arr = n_members.pairs - arr.add n_pair - return arr - end -end - -redef class Nmembers_head - redef fun pairs do return [n_pair] -end - -redef class Npair - fun name: String do return n_string.to_nit_string - fun value: nullable Jsonable do return n_value.to_nit_object -end - -redef class Nvalue_array - redef fun to_nit_object - do - var arr = new JsonArray - var elements = n_elements - if elements != null then - var items = elements.items - for item in items do arr.add(item.to_nit_object) - end - return arr - end -end - -redef class Nelements - fun items: Array[Nvalue] is abstract -end - -redef class Nelements_tail - redef fun items - do - var items = n_elements.items - items.add(n_value) - return items - end -end - -redef class Nelements_head - redef fun items do return [n_value] -end - -redef class Text - # Parse a JSON String as Jsonable entities - # - # Example with `JsonObject`" - # - # var obj = "\{\"foo\": \{\"bar\": true, \"goo\": [1, 2, 3]\}\}".to_jsonable - # assert obj isa JsonObject - # assert obj["foo"] isa JsonObject - # assert obj["foo"].as(JsonObject)["bar"] == true - # - # Example with `JsonArray` - # - # var arr = "[1, 2, 3]".to_jsonable - # assert arr isa JsonArray - # assert arr.length == 3 - # assert arr.first == 1 - # assert arr.last == 3 - # - # Example with `String` - # - # var str = "\"foo, bar, baz\"".to_jsonable - # assert str isa String - # assert str == "foo, bar, baz" - # - # Malformed JSON input returns a `JsonError` object - # - # var bad = "\{foo: \"bar\"\}".to_jsonable - # assert bad isa JsonError - # assert bad.error == "JsonLexerError" - fun to_jsonable: nullable Jsonable - do - var lexer = new Lexer_json(to_s) - var parser = new Parser_json - var tokens = lexer.lex - parser.tokens.add_all(tokens) - var root_node = parser.parse - if root_node isa NStart then - return root_node.n_0.to_nit_object - else if root_node isa NLexerError then - var pos = root_node.position - var msg = "{root_node.message} at {pos or else ""} for {root_node}" - return new JsonError("JsonLexerError", msg) - else if root_node isa NParserError then - var pos = root_node.position - var msg = "{root_node.message} at {pos or else ""} for {root_node}" - return new JsonError("JsonParsingError", msg) - else abort - end -end - diff --git a/lib/neo4j/neo4j.nit b/lib/neo4j/neo4j.nit index b84a60b..efe4f6a 100644 --- a/lib/neo4j/neo4j.nit +++ b/lib/neo4j/neo4j.nit @@ -64,6 +64,7 @@ module neo4j import curl_json +import error # Handles Neo4j server start and stop command # @@ -336,29 +337,31 @@ class Neo4jClient # Parse the cURL `response` as a JSON string private fun parse_response(response: CurlResponse): Jsonable do if response isa CurlResponseSuccess then - if response.body_str.is_empty then + var str = response.body_str + if str.is_empty then return new JsonObject + var res = str.parse_json + if res isa JsonParseError then + var e = new NeoError(res.to_s, "JsonParseError") + e.cause = res + return e + end + if res == null then + # empty response wrap it in empty object return new JsonObject - else - var str = response.body_str - var res = str.to_jsonable - if res == null then - # empty response wrap it in empty object - return new JsonObject - else if res isa JsonObject and res.has_key("exception") then - var error = "Neo4jError::{res["exception"] or else "null"}" - var msg = "" - if res.has_key("message") then - msg = res["message"].to_s - end - return new JsonError(error, msg.to_json) - else - return res + else if res isa JsonObject and res.has_key("exception") then + var error = "Neo4jError::{res["exception"] or else "null"}" + var msg = "" + if res.has_key("message") then + msg = res["message"].to_s end + return new NeoError(msg, error) + else + return res end else if response isa CurlResponseFailed then - return new JsonError("Curl error", "{response.error_msg} ({response.error_code})") + return new NeoError("{response.error_msg} ({response.error_code})", "CurlError") else - return new JsonError("Curl error", "Unexpected response '{response}'") + return new NeoError("Unexpected response \"{response}\".", "CurlError") end end end @@ -896,7 +899,7 @@ class NeoBatch fun save_edges(edges: Collection[NeoEdge]) do for edge in edges do save_edge(edge) # Execute the batch and update local nodes - fun execute: List[JsonError] do + fun execute: List[NeoError] do var request = new JsonPOST(client.batch_url, client.curl) # request.headers["X-Stream"] = "true" var json_jobs = new JsonArray @@ -908,16 +911,16 @@ class NeoBatch end # Associate data from response in original nodes and edges - private fun finalize_batch(response: Jsonable): List[JsonError] do - var errors = new List[JsonError] + private fun finalize_batch(response: Jsonable): List[NeoError] do + var errors = new List[NeoError] if not response isa JsonArray then - errors.add(new JsonError("Neo4jError", "Unexpected batch response format")) + errors.add(new NeoError("Unexpected batch response format.", "Neo4jError")) return errors end # print " {res.length} jobs executed" for res in response do if not res isa JsonObject then - errors.add(new JsonError("Neo4jError", "Unexpected job format in batch response")) + errors.add(new NeoError("Unexpected job format in batch response.", "Neo4jError")) continue end var id = res["id"].as(Int) diff --git a/src/doc/doc_pages.nit b/src/doc/doc_pages.nit index 5d71b02..9c04688 100644 --- a/src/doc/doc_pages.nit +++ b/src/doc/doc_pages.nit @@ -17,6 +17,7 @@ module doc_pages import toolcontext import doc_model +private import json::static redef class ToolContext private var opt_dir = new OptionString("output directory", "-d", "--dir") @@ -212,23 +213,23 @@ class QuickSearch var tpl = new Template tpl.add "var nitdocQuickSearchRawList=\{ " for mmodule in mmodules do - tpl.add "\"{mmodule.name}\":[" - tpl.add "\{txt:\"{mmodule.full_name}\",url:\"{mmodule.nitdoc_url}\"\}," + tpl.add "{mmodule.name.to_json}:[" + tpl.add "\{txt:{mmodule.full_name.to_json},url:{mmodule.nitdoc_url.to_json}\}," tpl.add "]," end for mclass in mclasses do var full_name = mclass.intro.mmodule.full_name - tpl.add "\"{mclass.name}\":[" - tpl.add "\{txt:\"{full_name}\",url:\"{mclass.nitdoc_url}\"\}," + tpl.add "{mclass.name.to_json}:[" + tpl.add "\{txt:{full_name.to_json},url:{mclass.nitdoc_url.to_json}\}," tpl.add "]," end for mproperty, mprops in mpropdefs do - tpl.add "\"{mproperty}\":[" + tpl.add "{mproperty.to_json}:[" for mpropdef in mprops do var full_name = mpropdef.mclassdef.mclass.full_name var cls_url = mpropdef.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mpropdef.mproperty.nitdoc_id}" - tpl.add "\{txt:\"{full_name}\",url:\"{def_url}\"\}," + tpl.add "\{txt:{full_name.to_json},url:{def_url.to_json}\}," end tpl.add "]," end diff --git a/tests/sav/nitserial_args1.res b/tests/sav/nitserial_args1.res index 2ebbe38..e95605c 100644 --- a/tests/sav/nitserial_args1.res +++ b/tests/sav/nitserial_args1.res @@ -12,8 +12,6 @@ redef class Deserializer if name == "Array[Serializable]" then return new Array[Serializable].from_deserializer(self) if name == "Array[String]" then return new Array[String].from_deserializer(self) if name == "Array[Object]" then return new Array[Object].from_deserializer(self) - if name == "Array[Match]" then return new Array[Match].from_deserializer(self) - if name == "Array[FlatBuffer]" then return new Array[FlatBuffer].from_deserializer(self) return super end end diff --git a/tests/sav/test_json_static.res b/tests/sav/test_json_static.res index 0a122d4..22ab94a 100644 Binary files a/tests/sav/test_json_static.res and b/tests/sav/test_json_static.res differ diff --git a/tests/test_json_static.nit b/tests/test_json_static.nit index b510725..db6b1cf 100644 --- a/tests/test_json_static.nit +++ b/tests/test_json_static.nit @@ -1,6 +1,7 @@ # This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Alexis Laferrière +# Copyright 2014 Jean-Christophe Beaupré # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -22,9 +23,11 @@ end var a = "\{\"__kind\": \"obj\", \"__id\": 0, \"__class\": \"C\", \"a\": \{\"__kind\": \"obj\", \"__id\": 1, \"__class\": \"A\", \"b\": true, \"c\": \"a\", \"f\": 0.123, \"i\": 1234, \"s\": \"asdf\", \"n\": null, \"array\": [88, \"hello\", null]\}, \"b\": \{\"__kind\": \"obj\", \"__id\": 2, \"__class\": \"B\", \"b\": false, \"c\": \"b\", \"f\": 123.123, \"i\": 2345, \"s\": \"hjkl\", \"n\": null, \"array\": [88, \"hello\", null], \"ii\": 1111, \"ss\": \"qwer\"\}, \"aa\": \{\"__kind\": \"ref\", \"__id\": 1\}\}" var b = "\{\"__kind\": \"obj\", \"__id\": 0, \"__class\": \"A\", \"b\": true, \"c\": \"a\", \"f\": 0.123, \"i\": 1234, \"s\": \"asdf\", \"n\": null, \"array\": [88, \"hello\", null]\}" +var c = "\{\"foo\":\"bar\\\"\\\\\\/\\b\\f\\n\\r\\t\\u0020\\u0000\"\}" +var d = "\{ \"face with tears of joy\" : \"\\uD83D\\uDE02\" \}" -for s in [a, b] do - var obj = s.json_to_nit_object +for s in [a, b, c, d] do + var obj = s.parse_json print "# Json: {s}" print "# Nit: {obj or else ""}" end diff --git a/tests/test_neo4j.nit b/tests/test_neo4j.nit index f8cbd4b..7ee824c 100644 --- a/tests/test_neo4j.nit +++ b/tests/test_neo4j.nit @@ -43,7 +43,7 @@ assert res1.is_linked print res1["name"].to_s print res1["age"].to_s print res1["status"].to_s -print res1["groups"].to_s +print res1["groups"].to_json print res1.labels.join(" ") assert res1.out_edges.is_empty @@ -92,7 +92,7 @@ assert res4.is_linked print res4["name"].to_s print res4["age"].to_s print res4["status"].to_s -print res4["groups"].to_s +print res4["groups"].to_json print res4.labels.join(" ") assert res4.in_edges.is_empty assert not res4.out_edges.is_empty diff --git a/tests/test_neo4j_batch.nit b/tests/test_neo4j_batch.nit index e6ac890..7455557 100644 --- a/tests/test_neo4j_batch.nit +++ b/tests/test_neo4j_batch.nit @@ -65,7 +65,7 @@ assert res4.is_linked print res4["name"].to_s print res4["age"].to_s print res4["status"].to_s -print res4["groups"].to_s +print res4["groups"].to_json print res4.labels.join(" ") assert res4.in_edges.is_empty assert not res4.out_edges.is_empty