README: document nit_env.sh
[nit.git] / lib / json / static.nit
index bb578fd..966f2c9 100644 (file)
@@ -34,12 +34,56 @@ interface Jsonable
        # SEE: `append_json`
        fun to_json: String is abstract
 
+       # Use `append_json` to implement `to_json`.
+       #
+       # Therefore, one that redefine `append_json` may use the following
+       # redefinition to link `to_json` and `append_json`:
+       #
+       # ~~~nitish
+       # redef fun to_json do return to_json_by_append
+       # ~~~
+       #
+       # Note: This is not the default implementation of `to_json` in order to
+       # avoid cyclic references between `append_json` and `to_json` when none are
+       # implemented.
+       protected fun to_json_by_append: String do
+               var buffer = new RopeBuffer
+               append_json(buffer)
+               return buffer.write_to_string
+       end
+
        # Append the JSON representation of `self` to the specified buffer.
        #
        # SEE: `to_json`
-       fun append_json(buffer: Buffer) do
-               buffer.append(to_json)
+       fun append_json(buffer: Buffer) do buffer.append(to_json)
+
+       # Pretty print JSON string.
+       #
+       # ~~~
+       # var obj = new JsonObject
+       # obj["foo"] = 1
+       # obj["bar"] = true
+       # var arr = new JsonArray
+       # arr.add 2
+       # arr.add false
+       # arr.add "baz"
+       # obj["baz"] = arr
+       # var res = obj.to_pretty_json
+       # var exp = """{
+       # \t"foo": 1,
+       # \t"bar": true,
+       # \t"baz": [2, false, "baz"]
+       # }\n"""
+       # assert res == exp
+       # ~~~
+       fun to_pretty_json: String do
+               var res = new FlatBuffer
+               pretty_json_visit(res, 0)
+               res.add '\n'
+               return res.write_to_string
        end
+
+       private fun pretty_json_visit(buffer: FlatBuffer, indent: Int) is abstract
 end
 
 redef class Text
@@ -55,22 +99,22 @@ redef class Text
                                buffer.append "\\\""
                        else if char == '\/' then
                                buffer.append "\\/"
-                       else if char < 16.ascii then
+                       else if char < 16.code_point 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
+                               else if char == 0x0C.code_point then
                                        buffer.append "\\f"
-                               else if char == 0x08.ascii then
+                               else if char == 0x08.code_point then
                                        buffer.append "\\b"
                                else
-                                       buffer.append "\\u000{char.ascii.to_hex}"
+                                       buffer.append "\\u000{char.code_point.to_hex}"
                                end
                        else if char < ' ' then
-                               buffer.append "\\u00{char.ascii.to_hex}"
+                               buffer.append "\\u00{char.code_point.to_hex}"
                        else
                                buffer.add char
                        end
@@ -80,13 +124,11 @@ redef class Text
 
        # 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
+       # ~~~
+       # assert "\t\"http://example.com\"\r\n\0\\".to_json ==
+       #     "\"\\t\\\"http:\\/\\/example.com\\\"\\r\\n\\u0000\\\\\""
+       # ~~~
+       redef fun to_json do return to_json_by_append
 
        # Parse `self` as JSON.
        #
@@ -211,10 +253,29 @@ interface JsonMapRead[K: String, V: nullable Jsonable]
        #     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
+       redef fun to_json do return to_json_by_append
+
+       redef fun pretty_json_visit(buffer, indent) do
+               buffer.append "\{\n"
+               indent += 1
+               var i = 0
+               for k, v in self do
+                       buffer.append "\t" * indent
+                       buffer.append "\"{k}\": "
+                       if v isa JsonObject or v isa JsonArray then
+                               v.pretty_json_visit(buffer, indent)
+                       else
+                               buffer.append v.to_json
+                       end
+                       if i < length - 1 then
+                               buffer.append ","
+                       end
+                       buffer.append "\n"
+                       i += 1
+               end
+               indent -= 1
+               buffer.append "\t" * indent
+               buffer.append "\}"
        end
 
        private fun append_json_entry(iterator: MapIterator[String, nullable Jsonable],
@@ -259,10 +320,21 @@ class JsonSequenceRead[E: nullable Jsonable]
        #     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
+       redef fun to_json do return to_json_by_append
+
+       redef fun pretty_json_visit(buffer, indent) do
+               buffer.append "\["
+               var i = 0
+               for v in self do
+                       if v isa JsonObject or v isa JsonArray then
+                               v.pretty_json_visit(buffer, indent)
+                       else
+                               buffer.append v.to_json
+                       end
+                       if i < length - 1 then buffer.append ", "
+                       i += 1
+               end
+               buffer.append "\]"
        end
 
        private fun append_json_entry(iterator: Iterator[nullable Jsonable],
@@ -283,13 +355,15 @@ redef class JsonParseError
 
        # 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\"\}"
+       # ~~~
+       # 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}," +
@@ -302,12 +376,14 @@ redef class Position
 
        # 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" +
-       #               "\}"
+       # ~~~
+       # 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}," +
@@ -359,9 +435,9 @@ redef class Nstring
                                i += 1
                                char = text[i]
                                if char == 'b' then
-                                       char = 0x08.ascii
+                                       char = 0x08.code_point
                                else if char == 'f' then
-                                       char = 0x0C.ascii
+                                       char = 0x0C.code_point
                                else if char == 'n' then
                                        char = '\n'
                                else if char == 'r' then
@@ -374,7 +450,7 @@ redef class Nstring
                                        if code >= 128 then
                                                char = '?'
                                        else
-                                               char = code.ascii
+                                               char = code.code_point
                                        end
                                        i += 4
                                end