Merge: SELF type
authorJean Privat <jean@pryen.org>
Tue, 25 Nov 2014 01:04:13 +0000 (20:04 -0500)
committerJean Privat <jean@pryen.org>
Tue, 25 Nov 2014 01:04:13 +0000 (20:04 -0500)
If Object defines a virtual type SELF, redefines it automatically in all subclasses. Also checks for some conditions on the declaration of SELF: it must be public, it must not be fixed and it must be declared on Object (or not declared at all). These conditions ensures that its refinements are valid.

The error messages when trying to redefining SELF could be improved. Maybe adding a special case preventing any redef of SELF?

Pull-Request: #902
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Etienne M. Gagnon <egagnon@j-meg.com>
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

208 files changed:
benchmarks/strings/array_to_s_vars/array_to_s_manual.nit
contrib/friendz/src/friendz.nit
contrib/github_merge.nit
contrib/github_search_for_jni/src/github_search_for_jni.nit
contrib/nitcc/examples/json.sablecc
contrib/opportunity/src/opportunity_controller.nit
contrib/opportunity/src/opportunity_model.nit
contrib/opportunity/src/templates/meetup.nit
contrib/opportunity/src/templates/meetup_creation.nit
contrib/opportunity/tests/db_tests.nit
examples/rosettacode/ab.nit
examples/shoot/src/shoot_logic.nit
lib/a_star.nit
lib/android/platform.nit
lib/app/data_store.nit
lib/bucketed_game.nit
lib/counter.nit
lib/dummy_array.nit
lib/github_api.nit
lib/hash_debug.nit
lib/java/java.nit
lib/json/dynamic.nit
lib/json/error.nit [new file with mode: 0644]
lib/json/json.nit
lib/json/json_lexer.nit
lib/json/static.nit
lib/json_serialization.nit
lib/jvm.nit
lib/markdown/markdown.nit
lib/mnit/mnit_injected_input.nit
lib/mnit/tileset.nit
lib/mnit_android/android_app.nit
lib/mnit_android/android_assets.nit
lib/neo4j/curl_json.nit
lib/neo4j/error.nit [new file with mode: 0644]
lib/neo4j/json_store.nit
lib/neo4j/jsonable.nit [deleted file]
lib/neo4j/neo4j.nit
lib/pthreads/extra.nit [new file with mode: 0644]
lib/pthreads/pthreads.nit
lib/standard/collection/array.nit
lib/standard/collection/hash_collection.nit
lib/standard/collection/union_find.nit
lib/standard/kernel.nit
lib/standard/math.nit
lib/standard/string.nit
lib/standard/string_nit.c
lib/string_experimentations/utf8.nit
lib/string_experimentations/utf8_noindex.nit
src/compiler/abstract_compiler.nit
src/compiler/android_annotations.nit
src/compiler/android_platform.nit
src/compiler/global_compiler.nit
src/doc/doc_pages.nit
src/ffi/java.nit
src/interpreter/naive_interpreter.nit
src/markdown.nit
src/modelbuilder.nit
src/testing/testing_doc.nit
tests/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.nit [new file with mode: 0644]
tests/base_import_standard.nit [new file with mode: 0644]
tests/base_import_standard2.nit [new file with mode: 0644]
tests/base_init_linext.nit
tests/base_init_linext2.nit
tests/cc.skip
tests/emscripten.skip
tests/error_prop_loc.nit
tests/nitg-e.skip
tests/nitg-g.skip
tests/nitg-s.skip
tests/nitg-sg.skip
tests/niti.skip
tests/nitunit.args
tests/nitvm.skip
tests/sav/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.res [new file with mode: 0644]
tests/sav/ab.res
tests/sav/base_import_standard.res [new file with mode: 0644]
tests/sav/base_import_standard2.res [new file with mode: 0644]
tests/sav/base_init_inherit.res
tests/sav/base_init_inherit2_alt0.res
tests/sav/base_init_inherit2_alt1.res
tests/sav/base_init_inherit2_alt2.res
tests/sav/base_init_inherit2_alt3.res
tests/sav/base_init_inherit3_alt0.res
tests/sav/base_init_inherit3_alt1.res
tests/sav/base_init_inherit3_alt2.res
tests/sav/base_init_inherit3_alt3.res
tests/sav/base_init_inherit4_alt0.res
tests/sav/base_init_inherit4_alt1.res
tests/sav/base_init_inherit4_alt2.res
tests/sav/base_init_inherit4_alt3.res
tests/sav/base_init_inherit5.res
tests/sav/base_init_inherit6_alt1.res
tests/sav/base_init_inherit6_alt2.res
tests/sav/base_init_inherit_alt1.res
tests/sav/base_init_linext.res
tests/sav/base_init_linext2.res
tests/sav/base_init_linext2_alt1.res
tests/sav/base_init_linext2_alt2.res
tests/sav/base_init_linext2_alt3.res
tests/sav/base_init_linext2_alt4.res
tests/sav/base_init_linext2_alt5.res
tests/sav/base_init_linext_alt0.res
tests/sav/base_init_linext_alt1.res [deleted file]
tests/sav/base_init_linext_alt10.res [deleted file]
tests/sav/base_init_linext_alt11.res [deleted file]
tests/sav/base_init_linext_alt12.res [deleted file]
tests/sav/base_init_linext_alt13.res [deleted file]
tests/sav/base_init_linext_alt14.res [deleted file]
tests/sav/base_init_linext_alt15.res [deleted file]
tests/sav/base_init_linext_alt2.res [deleted file]
tests/sav/base_init_linext_alt3.res [deleted file]
tests/sav/base_init_linext_alt4.res [deleted file]
tests/sav/base_init_linext_alt5.res [deleted file]
tests/sav/base_init_linext_alt6.res [deleted file]
tests/sav/base_init_linext_alt7.res [deleted file]
tests/sav/base_init_linext_alt8.res [deleted file]
tests/sav/base_init_linext_alt9.res [deleted file]
tests/sav/base_init_linext_raf.res
tests/sav/error_prop_loc.res
tests/sav/error_prop_loc_alt1.res [new file with mode: 0644]
tests/sav/error_redef_1alt4_alt5.res
tests/sav/error_redef_1alt4_alt9.res
tests/sav/fixme/error_prop_loc.res [deleted file]
tests/sav/fixme/error_redef_1alt4_alt5.res [deleted file]
tests/sav/fixme/error_redef_1alt4_alt9.res [deleted file]
tests/sav/fixme/test_inheritance_raf.res [deleted file]
tests/sav/nitc/base_for_nullable.res [deleted file]
tests/sav/nitc/base_for_nullable_alt1.res [deleted file]
tests/sav/nitc/base_for_nullable_alt2.res [deleted file]
tests/sav/nitg-common/fixme/base_attr_init_val_int_alt1.res [moved from tests/sav/nitg-e/fixme/base_attr_init_val_int_alt1.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_init_val_int_alt2.res [moved from tests/sav/nitg-e/fixme/base_attr_init_val_int_alt2.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_init_val_int_alt3.res [moved from tests/sav/nitg-e/fixme/base_attr_init_val_int_alt3.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_init_val_int_alt4.res [moved from tests/sav/nitg-e/fixme/base_attr_init_val_int_alt4.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_init_val_raf_alt1.res [moved from tests/sav/nitg-e/fixme/base_attr_init_val_raf_alt1.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_nullable_int_alt1.res [moved from tests/sav/nitg-e/fixme/base_attr_nullable_int_alt1.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_nullable_int_alt2.res [moved from tests/sav/nitg-e/fixme/base_attr_nullable_int_alt2.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_nullable_int_alt3.res [moved from tests/sav/nitg-e/fixme/base_attr_nullable_int_alt3.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_nullable_int_alt4.res [moved from tests/sav/nitg-e/fixme/base_attr_nullable_int_alt4.res with 100% similarity]
tests/sav/nitg-common/fixme/base_attr_nullable_int_alt5.res [moved from tests/sav/nitg-e/fixme/base_attr_nullable_int_alt5.res with 100% similarity]
tests/sav/nitg-common/fixme/base_covar_int2_alt3.res [moved from tests/sav/nitg-e/fixme/base_covar_int2_alt3.res with 100% similarity]
tests/sav/nitg-common/fixme/base_covar_int2_alt5.res [moved from tests/sav/nitg-e/fixme/base_covar_int2_alt5.res with 100% similarity]
tests/sav/nitg-common/fixme/base_covar_int_alt3.res [moved from tests/sav/nitg-e/fixme/base_covar_int_alt3.res with 100% similarity]
tests/sav/nitg-common/fixme/base_for_finish.res [moved from tests/sav/nitg-e/fixme/base_for_finish.res with 100% similarity]
tests/sav/nitg-e/fixme/base_gen_reassign_alt4.res
tests/sav/nitg-e/fixme/base_gen_reassign_alt5.res
tests/sav/nitg-e/fixme/base_gen_reassign_alt6.res
tests/sav/nitg-g/fixme/base_attr_init_val_int_alt1.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_init_val_int_alt2.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_init_val_int_alt3.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_init_val_int_alt4.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_init_val_raf_alt1.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_nullable_int_alt1.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_nullable_int_alt2.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_nullable_int_alt3.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_nullable_int_alt4.res [deleted file]
tests/sav/nitg-g/fixme/base_attr_nullable_int_alt5.res [deleted file]
tests/sav/nitg-g/fixme/base_covar_int2_alt3.res [deleted file]
tests/sav/nitg-g/fixme/base_covar_int2_alt5.res [deleted file]
tests/sav/nitg-g/fixme/base_covar_int_alt3.res [deleted file]
tests/sav/nitg-g/fixme/base_for_finish.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_init_val_int_alt1.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_init_val_int_alt2.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_init_val_int_alt3.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_init_val_int_alt4.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_init_val_raf_alt1.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_nullable_int_alt1.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_nullable_int_alt2.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_nullable_int_alt3.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_nullable_int_alt4.res [deleted file]
tests/sav/nitg-s/fixme/base_attr_nullable_int_alt5.res [deleted file]
tests/sav/nitg-s/fixme/base_covar_int2_alt3.res [deleted file]
tests/sav/nitg-s/fixme/base_covar_int2_alt5.res [deleted file]
tests/sav/nitg-s/fixme/base_covar_int_alt3.res [deleted file]
tests/sav/nitg-s/fixme/base_for_finish.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt1.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt2.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt3.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt4.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_init_val_raf_alt1.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt1.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt2.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt3.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt4.res [deleted file]
tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt5.res [deleted file]
tests/sav/nitg-sg/fixme/base_covar_int2_alt3.res [deleted file]
tests/sav/nitg-sg/fixme/base_covar_int2_alt5.res [deleted file]
tests/sav/nitg-sg/fixme/base_covar_int_alt3.res [deleted file]
tests/sav/nitg-sg/fixme/base_for_finish.res [deleted file]
tests/sav/nitg-sg/fixme/test_platform_android.res [new file with mode: 0644]
tests/sav/nitserial_args1.res
tests/sav/nituml_args3.res
tests/sav/nituml_args4.res
tests/sav/nitunit_args1.res
tests/sav/nitunit_args4.res [new file with mode: 0644]
tests/sav/test_ffi_java_types.res
tests/sav/test_inheritance_raf.res
tests/sav/test_json_static.res
tests/sav/test_new_native_alt1.res
tests/search_tests.sh
tests/test_ffi_java_types.nit
tests/test_gen.nit
tests/test_json_static.nit
tests/test_native_array.nit
tests/test_neo4j.nit
tests/test_neo4j_batch.nit
tests/test_nitunit2.nit [new file with mode: 0644]
tests/tests.sh

index 843eb93..da6b667 100644 (file)
@@ -21,8 +21,6 @@ redef class NativeArray[E]
 end
 
 redef class Array[E]
-       super StringCapable
-
        redef fun to_s: String do
                var l = length
                var its = _items
@@ -35,7 +33,7 @@ redef class Array[E]
                        na[i] = tmp
                        i += 1
                end
-               var ns = calloc_string(sl + 1)
+               var ns = new NativeString(sl + 1)
                ns[sl] = '\0'
                i = 0
                var off = 0
index b8187ff..3877fe2 100644 (file)
@@ -834,7 +834,7 @@ redef class Display
        fun measureText(str: String, height: Int): Int
        do
                var font = app.game.font
-               return str.length * (app.game.font.width + app.game.font.hspace)
+               return str.length * (font.width + font.hspace.to_i)
        end
 
        # displays a debug rectangle
index c30732f..0d5e052 100644 (file)
@@ -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)
index d12f288..35b19b2 100644 (file)
@@ -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
index d1e9c6b..0de8914 100644 (file)
@@ -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')+;
 
index 831d90d..f52f07d 100644 (file)
@@ -60,13 +60,18 @@ class OpportunityWelcome
                        var mname = request.string_arg("meetup_name")
                        var mdate = request.string_arg("meetup_date")
                        var mplace = request.string_arg("meetup_place")
+                       var mmodestr = request.string_arg("meetup_mode")
+                       var mmode = 0
                        if mdate == null then mdate = ""
                        if mplace == null then mplace = ""
+                       if mmodestr != null then
+                               mmode = 1
+                       end
                        if mname == null then
                                mname = ""
                                var rsp = new HttpResponse(200)
                                var meetpage = new MeetupCreationPage
-                               var meet = new Meetup(mname, mdate, mplace)
+                               var meet = new Meetup(mname, mdate, mplace, mmode)
                                meetpage.ans = ansset
                                meetpage.meet = meet
                                meetpage.error = "'Meetup name' is a mandatory fields."
@@ -75,7 +80,7 @@ class OpportunityWelcome
 
                        end
                        var db = new OpportunityDB.open(db_path)
-                       var meet = new Meetup(mname, mdate, mplace)
+                       var meet = new Meetup(mname, mdate, mplace, mmode)
                        if ansset.is_empty then
                                db.close
                                var rsp = new HttpResponse(200)
@@ -176,7 +181,7 @@ class OpportunityAnswerREST
        redef fun answer(request, uri) do
                var persid = request.int_arg("pers_id")
                var ansid = request.int_arg("answer_id")
-               var ans = request.bool_arg("answer")
+               var ans = request.int_arg("answer")
                if persid == null or ansid == null or ans == null then return bad_req
                var db = new OpportunityDB.open(db_path)
                db.change_answer(ansid, persid, ans)
@@ -197,12 +202,11 @@ class OpportunityMeetupREST
                        var ans = request.string_arg("answers").split("&")
                        if name == null or m_id == null then return bad_req
                        print ans
-                       var ansmap = new HashMap[Int, Bool]
+                       var ansmap = new HashMap[Int, Int]
                        for i in ans do
                                var mp = i.split("=")
-                               var b = false
-                               if mp.last == "true" then b = true
                                var id = mp.first.split("_").last
+                               var b = mp.last.to_i
                                if not id.is_numeric then continue
                                ansmap[id.to_i] = b
                        end
@@ -220,7 +224,7 @@ class OpportunityMeetupREST
                        var p = new People(rname, rsurname)
                        for i in m.answers(db) do
                                if not ansmap.has_key(i.id) then
-                                       p.answers[i] = false
+                                       p.answers[i] = 0
                                else
                                        p.answers[i] = ansmap[i.id]
                                end
index aa9adec..7b5bde8 100644 (file)
@@ -31,7 +31,7 @@ class OpportunityDB
 
        # Creates the tables and triggers for Opportunity (SQLite3 DB)
        fun create_db do
-               assert create_table("IF NOT EXISTS meetups (id CHAR(40) PRIMARY KEY, name TEXT, date TEXT, place TEXT);") else
+               assert create_table("IF NOT EXISTS meetups (id CHAR(40) PRIMARY KEY, name TEXT, date TEXT, place TEXT, answer_mode INTEGER DEFAULT 0);") else
                        print error or else "?"
                end
                assert create_table("IF NOT EXISTS people(id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, surname TEXT);") else
@@ -68,7 +68,16 @@ class OpportunityDB
        fun find_meetup_by_id(id: String): nullable Meetup do
                var req = select("* FROM meetups where id={id.to_sql_string};")
                for i in req do
-                       return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s)
+                       return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s, i[4].to_i)
+               end
+               return null
+       end
+
+       # Find an `Answer` by its `id` or `null` if it could not be found
+       fun find_answer_by_id(id: Int): nullable Answer do
+               var req = select("* FROM answers WHERE id={id};")
+               for i in req do
+                       return new Answer.from_db(i[0].to_i, i[2].to_s)
                end
                return null
        end
@@ -76,16 +85,20 @@ class OpportunityDB
        # Change an Answer `ansid` for someone with an id `pid` to `resp`
        #
        # Returns `true` if the request was sucessful, false otherwise
-       fun change_answer(pid: Int, ansid: Int, resp: Bool): Bool do
-               var rsp = 0
-               if resp then rsp = 1
-               var rq = execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES({pid},{ansid},{rsp});")
-               if not rq then
+       fun change_answer(pid: Int, ansid: Int, resp: Int): Bool do
+               var p = find_people_by_id(pid)
+               if p == null then
                        print "Error while updating answer {ansid}:{pid}"
-                       print error or else "Unknown error"
                        return false
                end
-               return true
+               var a = find_answer_by_id(ansid)
+               if a == null then
+                       print "Error while updating answer {ansid}:{pid}"
+                       return false
+               end
+               p.answers[a] = resp
+               if p.commit(self) then return true
+               return false
        end
 
        # Removes a person in the Database by its `id`
@@ -120,7 +133,10 @@ class People
        # Surname of the participant
        var surname: String
        # Map of the answers of a Meetup and the answers of the participant
-       var answers: Map[Answer, Bool] = new HashMap[Answer, Bool]
+       # 0 = No
+       # 1 = Maybe
+       # 2 = Yes
+       var answers: Map[Answer, Int] = new HashMap[Answer, Int]
 
        # To be used internally when fetching the `People` in Database
        private init from_db(id: Int, name, surname: String) do
@@ -129,7 +145,7 @@ class People
        end
 
        # Changes an answer `ans` (or adds it)
-       fun answer=(ans: Answer, resp: Bool) do
+       fun answer=(ans: Answer, resp: Int) do
                answers[ans] = resp
        end
 
@@ -137,12 +153,11 @@ class People
        #
        # NOTE: If `self` does not exist in the Database, no answers will be fetched
        fun load_answers(db: OpportunityDB, meetup: Meetup) do
-               self.answers = new HashMap[Answer, Bool]
-               var req = db.select("answers.id, answers.name, part_answers.value FROM part_answers, answers WHERE part_answers.id_part={id} AND answers.id=part_answers.id_ans AND answers.meetup_id={meetup.id.to_sql_string} GROUP BY answers.id;")
+               self.answers = new HashMap[Answer, Int]
+               var req = db.select("answers.id, answers.name, part_answers.value FROM part_answers, answers WHERE part_answers.id_part={id} AND answers.id=part_answers.id_ans AND answers.meetup_id={meetup.id.html_escape.to_sql_string} GROUP BY answers.id;")
                for i in req do
                        var ans = new Answer.from_db(i[0].to_i, i[1].to_s)
-                       answers[ans] = false
-                       if i[2].to_i == 1 then answers[ans] = true
+                       answers[ans] = i[2].to_i
                end
        end
 
@@ -150,14 +165,14 @@ class People
 
        redef fun commit(db) do
                if id == -1 then
-                       if not db.execute("INSERT INTO people (name,surname) VALUES ({name.to_sql_string}, {surname.to_sql_string});") then
+                       if not db.execute("INSERT INTO people (name,surname) VALUES ({name.html_escape.to_sql_string}, {surname.html_escape.to_sql_string});") then
                                print "Error while adding people {self}"
                                print db.error or else "Unknown error"
                                return false
                        end
                        id = db.last_insert_rowid
                else
-                       if not db.execute("UPDATE people SET name={name.to_sql_string}, surname={surname.to_sql_string} WHERE ID={id};") then
+                       if not db.execute("UPDATE people SET name={name.html_escape.to_sql_string}, surname={surname.html_escape.to_sql_string} WHERE ID={id};") then
                                print "Error while updating people {self}"
                                print db.error or else "Unknown error"
                                return false
@@ -165,10 +180,18 @@ class People
                end
                for i,j in answers do
                        if i.id == -1 then i.commit(db)
-                       var val = 0
-                       if j then val = 1
-                       if not db.execute("INSERT OR REPLACE INTO part_answers(id_part, id_ans, value) VALUES ({id},{i.id},{val});") then
-                               print("Error while adding/replacing part_answers {id}|{i.id}|{j}")
+                       var val = j
+                       var s = db.select("* FROM part_answers WHERE id_part={id} AND id_ans={i.id}")
+                       if s != null and s.iterator.is_ok then
+                               if not db.execute("UPDATE part_answers SET value={j} WHERE id_part={id} AND id_ans={i.id};") then
+                                       print "Error while updating part_answers {id}|{i.id} = {j}"
+                                       print db.error or else "Unknown error"
+                                       return false
+                               end
+                               continue
+                       end
+                       if not db.execute("INSERT INTO part_answers(id_part, id_ans, value) VALUES ({id},{i.id},{val});") then
+                               print("Error while adding part_answers {id}|{i.id}|{j}")
                                print db.error or else "Unknown error"
                                return false
                        end
@@ -189,13 +212,13 @@ class Meetup
        var date: String
        # Place of the meetup
        var place: String
+       # Mode of answering to the meetup (atm supports with or without Maybe)
+       var answer_mode: Int
 
        # Builds the object with all the informations found in the database
-       private init from_db(id, name, date, place: String) do
+       private init from_db(id, name, date, place: String, mode: Int) do
                self.id = id
-               self.name = name
-               self.date = date
-               self.place = place
+               init(name, date, place, mode)
        end
 
        # Gets the answers bound to the current `Meetup`
@@ -225,7 +248,7 @@ class Meetup
                if id == "" then
                        var time = get_time
                        var tmpid = (name + date + place + time.to_s).sha1_to_s
-                       if not db.execute("INSERT INTO meetups (id, name, date, place) VALUES({tmpid.to_sql_string}, {name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string});") then
+                       if not db.execute("INSERT INTO meetups (id, name, date, place, answer_mode) VALUES({tmpid.to_sql_string}, {name.html_escape.to_sql_string}, {date.html_escape.to_sql_string}, {place.html_escape.to_sql_string}, {answer_mode});") then
                                print "Error recording entry Meetup {self}"
                                print db.error or else "Null error"
                                return false
@@ -233,7 +256,7 @@ class Meetup
                        id = tmpid
                        return true
                else
-                       return db.execute("UPDATE meetups (name, date, place) VALUES({name.to_sql_string}, {date.to_sql_string}, {place.to_sql_string}) WHERE ID={id.to_sql_string};")
+                       return db.execute("UPDATE meetups SET name={name.html_escape.to_sql_string}, date={date.html_escape.to_sql_string}, place={place.html_escape.to_sql_string}, answer_mode={answer_mode} WHERE ID={id.to_sql_string};")
                end
        end
 
@@ -259,6 +282,11 @@ class Answer
                self.id = id
        end
 
+       redef fun hash do
+               if id != -1 then return id
+               return super
+       end
+
        # Loads the Meetup associated to `self`
        #
        # REQUIRE: is loaded in database
@@ -266,12 +294,39 @@ class Answer
                assert id != -1
                var res = db.select("meetups.* FROM meetups, answers WHERE answers.id={id} AND answers.meetup_id=meetups.id;")
                for i in res do
-                       return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s)
+                       return new Meetup.from_db(i[0].to_s, i[1].to_s, i[2].to_s, i[3].to_s, i[4].to_i)
                end
                # If no Meetup could be loaded, the contract was not respected
                abort
        end
 
+       # Counts the number of positive or maybe answers
+       fun count(db: OpportunityDB): Int do
+               if id == -1 then return -1
+               var count = 0
+               var res = db.select("part_answers.value FROM part_answers WHERE part_answers.id_ans={id};")
+               if meetup == null then meetup = load_meetup(db)
+               for i in res do
+                       if meetup.answer_mode == 0 then
+                               count += i[0].to_i
+                       else
+                               if i[0].to_i == 2 then count += 1
+                       end
+               end
+               return count
+       end
+
+       # Counts the score for this particular answer
+       fun score(db: OpportunityDB): Int do
+               if id == -1 then return -1
+               var score = 0
+               var res = db.select("part_answers.value FROM part_answers WHERE part_answers.id_ans={id};")
+               for i in res do
+                       score += i[0].to_i
+               end
+               return score
+       end
+
        redef fun commit(db) do
                var m = meetup
                if m == null then return false
@@ -282,14 +337,14 @@ class Answer
                        end
                end
                if id == -1 then
-                       if not db.execute("INSERT INTO answers (name, meetup_id) VALUES({name.to_sql_string}, {m.id.to_sql_string});") then
+                       if not db.execute("INSERT INTO answers (name, meetup_id) VALUES({name.html_escape.to_sql_string}, {m.id.to_sql_string});") then
                                print "Cannot create {self} in database"
                                print db.error or else "Unknown error"
                                return false
                        end
                        id = db.last_insert_rowid
                else
-                       if not db.execute("UPDATE answers (name) VALUES ({name.to_sql_string}) WHERE meetup_id={m.id.to_sql_string};") then
+                       if not db.execute("UPDATE answers SET name=({name.html_escape.to_sql_string}) WHERE meetup_id={m.id.to_sql_string};") then
                                print "Error updating {self} in database"
                                print db.error or else "Unknown error"
                                return false
index 72503cf..e5d1f56 100644 (file)
@@ -26,33 +26,107 @@ class OpportunityMeetupPage
 
        # Meetup the page is supposed to show
        var meetup: nullable Meetup = null
+       # Answer mode for the meetup
+       var mode = 0
 
        init from_id(id: String) do
                var db = new OpportunityDB.open("opportunity")
                meetup = db.find_meetup_by_id(id)
                db.close
+               if meetup != null then mode = meetup.answer_mode
+               init
        end
 
        init do
-               header.page_js = """
+               header.page_js = "mode = {mode};\n"
+               header.page_js += """
+               function update_scores(){
+                       var anss = $('.answer');
+                       var count = {};
+                       var scores = {};
+                       var answers = [];
+                       var maxscore = 0;
+                       for(i=0; i < anss.length; i++){
+                               var incscore = 0;
+                               var inccount = 0;
+                               var idparts = anss[i].id.split("_");
+                               var ansid = idparts[1];
+                               var html = anss[i].innerHTML;
+                               if(html === "<center>✔</center>"){
+                                       inccount = 1;
+                                       incscore = 2;
+                               }else if(html === "<center>❓</center>"){
+                                       incscore = 1;
+                               }
+                               var intansid = parseInt(ansid)
+                               if(answers.indexOf(intansid) == -1){
+                                       answers.push(intansid);
+                               }
+                               if(ansid in count){
+                                       count[ansid] += inccount;
+                               }else{
+                                       count[ansid] = inccount;
+                               }
+                               if(ansid in scores){
+                                       scores[ansid] += incscore;
+                               }else{
+                                       scores[ansid] = incscore;
+                               }
+                               if(scores[ansid] > maxscore){
+                                       maxscore = scores[ansid];
+                               }
+                       }
+                       for(i=0; i < answers.length; i++){
+                               var ansid = answers[i].toString();
+                               var el = $('#total'+ansid)[0];
+                               var ins = "<center>"+count[ansid];
+                               if(scores[ansid] >= maxscore){
+                                       ins += "<br/><span style=\\"color:blue\\">★</span>";
+                               }
+                               ins += "</center>";
+                               el.innerHTML = ins;
+                       }
+               }
                function change_answer(ele, id){
                        // modify only the currently selected entry
                        if (in_modification_id != id) return;
 
                        var e = document.getElementById(ele.id);
                        var i = e.innerHTML;
-                       var ans = true;
+                       var ans = true;"""
+               if mode == 0 then
+                       header.page_js += """
                        if(i === "<center>✔</center>"){
-                               ans = false;
+                               ans = 0;
                                e.innerHTML = "<center>✘</center>"
                                e.style.color = "red";
                        }else{
+                               ans = 1;
                                e.innerHTML = "<center>✔</center>";
                                e.style.color = "green";
-                       }
+                       }"""
+
+               else
+                       header.page_js += """
+                       if(i === "<center>✔</center>"){
+                               ans = 1;
+                               e.innerHTML = "<center>❓</center>"
+                               e.style.color = "#B8860B";
+                       }else if(i === "<center>❓</center>"){
+                               ans = 0;
+                               e.innerHTML = "<center>✘</center>"
+                               e.style.color = "red";
+                       }else{
+                               ans = 2;
+                               e.innerHTML = "<center>✔</center>";
+                               e.style.color = "green";
+                       }"""
+               end
+               header.page_js += """
                        var a = ele.id.split('_')
                        var pid = a[1]
                        var aid = a[2]
+                       update_scores();
                        $.ajax({
                                type: "POST",
                                url: "./rest/answer",
@@ -65,16 +139,33 @@ class OpportunityMeetupPage
                }
                function change_temp_answer(ele){
                        var e = document.getElementById(ele.id);
-                       var i = e.innerHTML;
-                       var ans = true;
+                       var i = e.innerHTML;"""
+               if mode == 0 then
+                       header.page_js += """
+                       if(i === "<center>✔</center>"){
+                               e.innerHTML = "<center>✘</center>"
+                               e.style.color = "red";
+                       }else{
+                               e.innerHTML = "<center>✔</center>";
+                               e.style.color = "green";
+                       }
+                       """
+               else
+                       header.page_js += """
                        if(i === "<center>✔</center>"){
-                               ans = false;
-                               e.innerHTML = "<center>✘</center>";
+                               e.innerHTML = "<center>❓</center>";
+                               e.style.color = "#B8860B";
+                       }else if(i === "<center>❓</center>"){
+                               e.innerHTML = "<center>✘</center>"
                                e.style.color = "red";
                        }else{
                                e.innerHTML = "<center>✔</center>";
                                e.style.color = "green";
                        }
+                       """
+               end
+               header.page_js += """
+                       update_scores();
                }
                function add_part(ele){
                        var e = document.getElementById(ele.id);
@@ -85,11 +176,25 @@ class OpportunityMeetupPage
                        ansmap = {};
                        for(i=0;i<ans.length;i++){
                                var curr = ans.eq(i)
+                       """
+               if mode == 0 then
+                       header.page_js += """
                                if(curr[0].innerHTML === "<center>✔</center>"){
-                                       ansmap[curr.attr('id')] = true
+                                       ansmap[curr.attr('id')] = 1
                                }else{
-                                       ansmap[curr.attr('id')] = false
-                               }
+                                       ansmap[curr.attr('id')] = 0
+                               }"""
+               else
+                       header.page_js += """
+                               if(curr[0].innerHTML === "<center>✔</center>"){
+                                       ansmap[curr.attr('id')] = 2
+                               }else if(curr[0].innerHTML === "<center>❓</center>"){
+                                       ansmap[curr.attr('id')] = 1
+                               }else{
+                                       ansmap[curr.attr('id')] = 0
+                               }"""
+               end
+               header.page_js += """
                        }
                        $.ajax({
                                type: "POST",
@@ -111,6 +216,7 @@ class OpportunityMeetupPage
                        var arr = ele.id.split("_")
                        var pid = arr[1]
                        $('#' + ele.id).parent().parent().parent().remove();
+                       update_scores();
                        $.ajax({
                                type: "POST",
                                url: "./rest/people",
@@ -120,7 +226,6 @@ class OpportunityMeetupPage
                                }
                        });
                }
-
                // ID of line currently open for modification
                var in_modification_id = null;
                function modify_people(ele, id){
@@ -192,18 +297,39 @@ redef class Meetup
                        t.add "<td>"
                        t.add i.to_s
                        t.add "</td>"
-                       for j,k in i.answers do
+                       for j, k in i.answers do
                                var color
-                               if k then
-                                       color = "green"
-                               else color = "red"
-
+                               if answer_mode == 0 then
+                                       if k == 1 then
+                                               color = "green"
+                                       else
+                                               color = "red"
+                                       end
+                               else
+                                       if k == 2 then
+                                               color = "green"
+                                       else if k == 1 then
+                                               color = "#B8860B"
+                                       else
+                                               color = "red"
+                                       end
+                               end
                                t.add """<td class="answer" onclick="change_answer(this, {{{i.id}}})" id="answer_{{{j.id}}}_{{{i.id}}}" style="color:{{{color}}}">"""
                                t.add "<center>"
-                               if k then
-                                       t.add "✔"
+                               if answer_mode == 0 then
+                                       if k == 1 then
+                                               t.add "✔"
+                                       else
+                                               t.add "✘"
+                                       end
                                else
-                                       t.add "✘"
+                                       if k == 2 then
+                                               t.add "✔"
+                                       else if k == 1 then
+                                               t.add "❓"
+                                       else
+                                               t.add "✘"
+                                       end
                                end
                                t.add "</center></td>"
                        end
@@ -221,6 +347,28 @@ redef class Meetup
                t.add """
        <td><center><span id="add_{{{id}}}" onclick="add_part(this)" style="color:green;" class="action"><button class="btn btn-xs btn-success" type="button">Done</button></span></center></td>"""
                t.add "</tr>"
+               # Compute score for each answer
+               var scores = new HashMap[Int, Int]
+               var maxsc = 0
+               for i in answers(db) do
+                       scores[i.id] = i.score(db)
+                       if scores[i.id] > maxsc then maxsc = scores[i.id]
+               end
+               t.add """
+<tr id="total">
+       <th>Total</th>
+               """
+               for i in answers(db) do
+                       t.add """<th id="total{{{i.id}}}"><center>{{{i.count(db)}}}"""
+                       if scores.has_key(i.id) and scores[i.id] >= maxsc then
+                               t.add """<br/><span style="color:blue">★</span>"""
+                       end
+                       t.add "</center></th>"
+               end
+               t.add "</th>"
+               t.add """
+               <th></th>
+</tr>"""
                t.add "</table>"
                t.add "</div>"
                return t
index 6f42561..5b1a048 100644 (file)
@@ -93,6 +93,12 @@ function new_answer(sender){
                                        <input name="meetup_place" id="meetup_place" type="text" class="form-control" placeholder="Place of the event" value="{{{if meet != null then meet.place else ""}}}">
                                </div>
                        </div>
+                       <div class = "form-group">
+                               <label for="meetup=maybe" class="col-sm-4 control-label">Add a Maybe option?</label>
+                               <div class="col-sm-8">
+                                       <input name="meetup_mode" id="meetup_mode" type="checkbox" class="form-control">
+                               </div>
+                       </div>
                                <h2>Opportunities</h2>
 <div id="answers">
 """
index cbec6f7..a5a39a7 100644 (file)
@@ -47,7 +47,7 @@ db = new OpportunityDB.open("opportunity")
 
 var hj = new People("Jack", "Handsome")
 
-var m = new Meetup("Awaken the warrior", "2024/05/28", "Vault of the Warrior")
+var m = new Meetup("Awaken the warrior", "2024/05/28", "Vault of the Warrior", 0)
 assert m.commit(db)
 
 var vh = new People("Hunter", "Vault")
@@ -66,17 +66,17 @@ var h = new Answer("I have no choice, I'm a hostage")
 h.meetup = m
 h.commit(db)
 
-hj.answer(y) = true
-hj.answer(n) = false
-hj.answer(h) = false
+hj.answer(y) = 1
+hj.answer(n) = 0
+hj.answer(h) = 0
 
-vh.answer(y) = true
-vh.answer(n) = false
-vh.answer(h) = false
+vh.answer(y) = 1
+vh.answer(n) = 0
+vh.answer(h) = 0
 
-ll.answer(y) = true
-ll.answer(n) = false
-ll.answer(h) = true
+ll.answer(y) = 1
+ll.answer(n) = 0
+ll.answer(h) = 1
 
 hj.commit db
 vh.commit db
index 9138c0c..ce18fc0 100644 (file)
@@ -10,4 +10,8 @@
 module ab
 
 var words = gets.split(" ")
+if words.length != 2 then
+       print "Expected two numbers"
+       return
+end
 print words[0].to_i + words[1].to_i
index 838c78b..824af57 100644 (file)
@@ -1052,6 +1052,7 @@ end
 
 fun headless_run
 do
+       srand_from 0
        print "Headless run"
        # Only run the playscene
        var scene = new PlayScene(80000,60000)
index 125b053..4108268 100644 (file)
 # ~~~
 module a_star
 
-redef class Object
-       protected fun debug_a_star: Bool do return false
-       private fun debug(msg: String) do if debug_a_star then
-               sys.stderr.write "a_star debug: {msg}\n"
-       end
-end
-
 # General graph node
 class Node
        type N: Node
@@ -130,14 +123,12 @@ class Node
                                var current_bucket = buckets[cost % nbr_buckets]
 
                                if current_bucket.is_empty then # move to next bucket
-                                       debug "b {cost} {cost % nbr_buckets} {buckets[cost % nbr_buckets].hash}"
                                        cost += 1
                                        if cost > max_cost then return null
                                        bucket_searched += 1
 
                                        if bucket_searched > nbr_buckets then break
                                else # found a node
-                                       debug "c {cost}"
                                        frontier_node = current_bucket.pop
 
                                        if frontier_node.open then break
@@ -151,7 +142,6 @@ class Node
                        # at destination
                        else if frontier_node == destination or
                             (alt_targets != null and alt_targets.accept(frontier_node)) then
-                               debug "picked {frontier_node}, is destination"
 
                                var path = new Path[N](cost)
 
@@ -166,11 +156,8 @@ class Node
                        else
                                frontier_node.open = false
 
-                               debug "w exploring adjacents of {frontier_node}"
-
                                for link in frontier_node.links do
                                        var peek_node = link.to
-                                       debug "v {context.is_blocked(link)} {peek_node.last_pathfinding_evocation != graph.pathfinding_current_evocation} {peek_node.best_cost_up_to > frontier_node.best_cost_up_to + context.cost(link)}, {peek_node.best_cost_up_to} > {frontier_node.best_cost_up_to} + {context.cost(link)}"
                                        if not context.is_blocked(link) and
                                         (peek_node.last_pathfinding_evocation != graph.pathfinding_current_evocation or
                                           (peek_node.open and
@@ -190,8 +177,6 @@ class Node
 
                                                var at_bucket = buckets[est_cost % nbr_buckets]
                                                at_bucket.add(peek_node)
-
-                                               debug "u putting {peek_node} at {est_cost} -> {est_cost % nbr_buckets} {at_bucket.hash}, {cost}+{context.cost(link)}"
                                        end
                                end
                        end
index dab001d..fca03e0 100644 (file)
@@ -21,6 +21,8 @@ module platform is
        new_annotation max_api_version
        new_annotation target_api_version
        new_annotation android_manifest
+       new_annotation android_manifest_application
+       new_annotation android_manifest_activity
 end
 
 import java
index afb6a1d..13891fc 100644 (file)
@@ -34,5 +34,5 @@ interface DataStore
        fun [](key: String): nullable Object is abstract
 
        # Store `value` at `key`
-       fun []=(key: String, value: Serializable) is abstract
+       fun []=(key: String, value: nullable Serializable) is abstract
 end
index 45a439c..09a4ee8 100644 (file)
@@ -83,6 +83,8 @@ class Buckets[G: Game]
                var current_bucket = buckets[current_bucket_key]
 
                var next_bucket = new HashSet[Bucketable[G]]
+               buckets[current_bucket_key] = next_bucket
+               self.next_bucket = next_bucket
 
                for e in current_bucket do
                        var act_at = e.act_at
@@ -96,9 +98,6 @@ class Buckets[G: Game]
                                end
                        end
                end
-
-               self.next_bucket = next_bucket
-               buckets[current_bucket_key] = next_bucket
        end
 end
 
index 670fc59..3d1555b 100644 (file)
@@ -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
index 21da4bd..88e7008 100644 (file)
@@ -12,7 +12,6 @@
 
 class DummyArray
        super Set[Int]
-       super ArrayCapable[Int]
        private var capacity: Int
        redef var length: Int
        private var keys: NativeArray[Int]
@@ -79,8 +78,8 @@ class DummyArray
        init(capacity: Int)
        do
                _capacity = capacity
-               _keys = calloc_array(capacity)
-               _values = calloc_array(capacity)
+               _keys = new NativeArray[Int](capacity)
+               _values = new NativeArray[Int](capacity)
        end
 end
 
index 2394592..39fca84 100644 (file)
@@ -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}"
index b618729..8426097 100644 (file)
@@ -121,7 +121,7 @@ do
        super
 end
 
-redef class HashCollection[K,N]
+redef class HashCollection[K]
        redef fun node_at_idx(i,k)
        do
                sys.gt_count += 1
index 56a375f..c3db430 100644 (file)
@@ -27,7 +27,7 @@
 # most of JNI functions. You can use it to further customize the behavior
 # of your code.
 module java is
-       c_compiler_option("-I $(JAVA_HOME)/include/")
+       c_compiler_option "-I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
        c_linker_option("-L $(JNI_LIB_PATH) -ljvm")
        new_annotation extra_java_files
 end
index ec3ba25..d15ceb7 100644 (file)
@@ -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 (file)
index 0000000..dab1689
--- /dev/null
@@ -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
index 9353350..b458d01 100644 (file)
@@ -1,6 +1,7 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 # 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
index f597c82..3273525 100644 (file)
@@ -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
index 51800fc..29e5c22 100644 (file)
@@ -1,6 +1,8 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014 Alexandre Terrasa <alexandre@moz-concept.com>
+# Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
 
 # 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 "<unknown>"} 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 "<unknown>"} for {root_node}"
-                       return null
-               else abort
-       end
-end
index 6a2d82a..f090b24 100644 (file)
@@ -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
index 56080a5..8012746 100644 (file)
@@ -173,6 +173,7 @@ extern class JavaVM `{JavaVM *`}
                        JavaVM_jni_error(NULL, "Could not attach current thread to Java VM", res);
                        return NULL;
                }
+               return env;
        `}
 end
 
@@ -216,36 +217,41 @@ extern class JniEnv `{JNIEnv *`}
        # Call a method on `obj` designed by `method_id` with an array `args` of argument returning a JavaObject
        fun call_object_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): JavaObject import convert_args_to_jni `{
                jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
-               (*recv)->CallObjectMethod(recv, obj, method_id, args_tab);
+               jobject res = (*recv)->CallObjectMethod(recv, obj, method_id, args_tab);
                free(args_tab);
+               return res;
        `}
        
        # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Bool
        fun call_boolean_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Bool import convert_args_to_jni `{
                jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
-               return (*recv)->CallBooleanMethod(recv, obj, method_id, args_tab);
+               jboolean res = (*recv)->CallBooleanMethod(recv, obj, method_id, args_tab);
                free(args_tab);
+               return res;
        `}
 
        # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Char
        fun call_char_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Char import convert_args_to_jni `{
                jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
-               return (*recv)->CallCharMethod(recv, obj, method_id, args_tab);
+               jchar res = (*recv)->CallCharMethod(recv, obj, method_id, args_tab);
                free(args_tab);
+               return res;
        `}
 
        # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning an Int
        fun call_int_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Int import convert_args_to_jni `{
                jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
-               return (*recv)->CallIntMethod(recv, obj, method_id, args_tab);
+               jint res = (*recv)->CallIntMethod(recv, obj, method_id, args_tab);
                free(args_tab);
+               return res;
        `}
        
        # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a Float
        fun call_float_method(obj: JavaObject, method_id: JMethodID, args: nullable Array[nullable Object]): Float import convert_args_to_jni `{
                jvalue * args_tab = JniEnv_convert_args_to_jni(recv, args);
-               return (*recv)->CallFloatMethod(recv, obj, method_id, args_tab);
+               jfloat res = (*recv)->CallFloatMethod(recv, obj, method_id, args_tab);
                free(args_tab);
+               return res;
        `}
 
        # Call a method on `obj` designed by `method_id` with an array `args` of arguments returning a NativeString
index 5b73d3b..add43e1 100644 (file)
@@ -30,6 +30,7 @@ import template
 # SEE: `String::md_to_html` for a shortcut.
 class MarkdownProcessor
 
+       # `MarkdownEmitter` used for ouput.
        var emitter: MarkdownEmitter is noinit
 
        init do self.emitter = new MarkdownEmitter(self)
@@ -261,6 +262,101 @@ class MarkdownProcessor
                return new LineOther
        end
 
+       # Get the token kind at `pos`.
+       fun token_at(text: Text, pos: Int): Token do
+               var c0: Char
+               var c1: Char
+               var c2: Char
+
+               if pos > 0 then
+                       c0 = text[pos - 1]
+               else
+                       c0 = ' '
+               end
+               var c = text[pos]
+
+               if pos + 1 < text.length then
+                       c1 = text[pos + 1]
+               else
+                       c1 = ' '
+               end
+               if pos + 2 < text.length then
+                       c2 = text[pos + 2]
+               else
+                       c2 = ' '
+               end
+
+               if c == '*' then
+                       if c1 == '*' then
+                               if c0 != ' ' or c2 != ' ' then
+                                       return new TokenStrongStar(pos, c)
+                               else
+                                       return new TokenEmStar(pos, c)
+                               end
+                       end
+                       if c0 != ' ' or c1 != ' ' then
+                               return new TokenEmStar(pos, c)
+                       else
+                               return new TokenNone(pos, c)
+                       end
+               else if c == '_' then
+                       if c1 == '_' then
+                               if c0 != ' ' or c2 != ' 'then
+                                       return new TokenStrongUnderscore(pos, c)
+                               else
+                                       return new TokenEmUnderscore(pos, c)
+                               end
+                       end
+                       if c0 != ' ' or c1 != ' ' then
+                               return new TokenEmUnderscore(pos, c)
+                       else
+                               return new TokenNone(pos, c)
+                       end
+               else if c == '!' then
+                       if c1 == '[' then return new TokenImage(pos, c)
+                       return new TokenNone(pos, c)
+               else if c == '[' then
+                       return new TokenLink(pos, c)
+               else if c == ']' then
+                       return new TokenNone(pos, c)
+               else if c == '`' then
+                       if c1 == '`' then
+                               return new TokenCodeDouble(pos, c)
+                       else
+                               return new TokenCodeSingle(pos, c)
+                       end
+               else if c == '\\' then
+                       if c1 == '\\' or c1 == '[' or c1 == ']' or c1 == '(' or c1 == ')' or c1 == '{' or c1 == '}' or c1 == '#' or c1 == '"' or c1 == '\'' or c1 == '.' or c1 == '<' or c1 == '>' or c1 == '*' or c1 == '+' or c1 == '-' or c1 == '_' or c1 == '!' or c1 == '`' or c1 == '~' or c1 == '^' then
+                               return new TokenEscape(pos, c)
+                       else
+                               return new TokenNone(pos, c)
+                       end
+               else if c == '<' then
+                       return new TokenHTML(pos, c)
+               else if c == '&' then
+                       return new TokenEntity(pos, c)
+               else if c == '^' then
+                       if c0 == '^' or c1 == '^' then
+                               return new TokenNone(pos, c)
+                       else
+                               return new TokenSuper(pos, c)
+                       end
+               else
+                       return new TokenNone(pos, c)
+               end
+       end
+
+       # Find the position of a `token` in `self`.
+       fun find_token(text: Text, start: Int, token: Token): Int do
+               var pos = start
+               while pos < text.length do
+                       if token_at(text, pos).is_same_type(token) then
+                               return pos
+                       end
+                       pos += 1
+               end
+               return -1
+       end
 end
 
 # Emit output corresponding to blocks content.
@@ -276,11 +372,6 @@ class MarkdownEmitter
        # Default is `HTMLDecorator`
        var decorator: Decorator = new HTMLDecorator is writable
 
-       # Create a new `MardownEmitter` using the default `HTMLDecorator`
-       init(processor: MarkdownProcessor) do
-               self.processor = processor
-       end
-
        # Create a new `MarkdownEmitter` using a custom `decorator`.
        init with_decorator(processor: MarkdownProcessor, decorator: Decorator) do
                init processor
@@ -312,7 +403,7 @@ class MarkdownEmitter
                current_text = text
                current_pos = start
                while current_pos < text.length do
-                       var mt = text.token_at(current_pos)
+                       var mt = processor.token_at(text, current_pos)
                        if (token != null and not token isa TokenNone) and
                        (mt.is_same_type(token) or
                        (token isa TokenEmStar and mt isa TokenStrongStar) or
@@ -1112,8 +1203,7 @@ class MDLine
        var next_empty: Bool = false is writable
 
        # Initialize a new MDLine from its string value
-       init(value: String) do
-               self.value = value
+       init do
                self.leading = process_leading
                if leading != value.length then
                        self.is_empty = false
@@ -1675,7 +1765,7 @@ abstract class TokenCode
 
        redef fun emit(v) do
                var a = pos + next_pos + 1
-               var b = v.current_text.find_token(a, self)
+               var b = v.processor.find_token(v.current_text.as(not null), a, self)
                if b > 0 then
                        v.current_pos = b + next_pos
                        while a < b and v.current_text[a] == ' ' do a += 1
@@ -1967,102 +2057,6 @@ end
 
 redef class Text
 
-       # Get the token kind at `pos`.
-       private fun token_at(pos: Int): Token do
-               var c0: Char
-               var c1: Char
-               var c2: Char
-
-               if pos > 0 then
-                       c0 = self[pos - 1]
-               else
-                       c0 = ' '
-               end
-               var c = self[pos]
-
-               if pos + 1 < length then
-                       c1 = self[pos + 1]
-               else
-                       c1 = ' '
-               end
-               if pos + 2 < length then
-                       c2 = self[pos + 2]
-               else
-                       c2 = ' '
-               end
-
-               if c == '*' then
-                       if c1 == '*' then
-                               if c0 != ' ' or c2 != ' ' then
-                                       return new TokenStrongStar(pos, c)
-                               else
-                                       return new TokenEmStar(pos, c)
-                               end
-                       end
-                       if c0 != ' ' or c1 != ' ' then
-                               return new TokenEmStar(pos, c)
-                       else
-                               return new TokenNone(pos, c)
-                       end
-               else if c == '_' then
-                       if c1 == '_' then
-                               if c0 != ' ' or c2 != ' 'then
-                                       return new TokenStrongUnderscore(pos, c)
-                               else
-                                       return new TokenEmUnderscore(pos, c)
-                               end
-                       end
-                       if c0 != ' ' or c1 != ' ' then
-                               return new TokenEmUnderscore(pos, c)
-                       else
-                               return new TokenNone(pos, c)
-                       end
-               else if c == '!' then
-                       if c1 == '[' then return new TokenImage(pos, c)
-                       return new TokenNone(pos, c)
-               else if c == '[' then
-                       return new TokenLink(pos, c)
-               else if c == ']' then
-                       return new TokenNone(pos, c)
-               else if c == '`' then
-                       if c1 == '`' then
-                               return new TokenCodeDouble(pos, c)
-                       else
-                               return new TokenCodeSingle(pos, c)
-                       end
-               else if c == '\\' then
-                       if c1 == '\\' or c1 == '[' or c1 == ']' or c1 == '(' or c1 == ')' or c1 == '{' or c1 == '}' or c1 == '#' or c1 == '"' or c1 == '\'' or c1 == '.' or c1 == '<' or c1 == '>' or c1 == '*' or c1 == '+' or c1 == '-' or c1 == '_' or c1 == '!' or c1 == '`' or c1 == '~' or c1 == '^' then
-                               return new TokenEscape(pos, c)
-                       else
-                               return new TokenNone(pos, c)
-                       end
-               else if c == '<' then
-                       return new TokenHTML(pos, c)
-               else if c == '&' then
-                       return new TokenEntity(pos, c)
-               else if c == '^' then
-                       if c0 == '^' or c1 == '^' then
-                               return new TokenNone(pos, c)
-                       else
-                               return new TokenSuper(pos, c)
-                       end
-               else
-                       return new TokenNone(pos, c)
-               end
-       end
-
-       # Find the position of a `token` in `self`.
-       private fun find_token(start: Int, token: Token): Int do
-               var pos = start
-               while pos < length do
-                       if token_at(pos).is_same_type(token) then
-                               return pos
-                       end
-                       pos += 1
-               end
-               return -1
-       end
-
        # Get the position of the next non-space character.
        private fun skip_spaces(start: Int): Int do
                var pos = start
index 4ed9664..a72b34d 100644 (file)
@@ -78,6 +78,8 @@ redef class App
                var env = "MNIT_SRAND".environ
                if env != "" then
                        srand_from(env.to_i)
+               else
+                       srand_from(0)
                end
 
                var input = "MNIT_READ_INPUT".environ
index 568fad5..5bd5d4d 100644 (file)
@@ -70,11 +70,11 @@ class TileSetFont
 
        # Additional space to insert horizontally between characters
        # A negave value will display tile overlaped
-       var hspace: Int = 0 is writable
+       var hspace: Numeric = 0.0 is writable
 
        # Additional space to insert vertically between characters
        # A negave value will display tile overlaped
-       var vspace: Int = 0 is writable
+       var vspace: Numeric = 0.0 is writable
 
        # The glyph (tile) associated to the caracter `c` according to `chars`
        # Returns null if `c` is not in `chars`
@@ -91,10 +91,11 @@ redef class Display
        # '\n' are rendered as carriage return
        fun text(text: String, font: TileSetFont, x, y: Numeric)
        do
+               x = x.to_f
                var cx = x
-               var cy = y
-               var sw = font.width + font.hspace
-               var sh = font.height + font.vspace
+               var cy = y.to_f
+               var sw = font.width.to_f + font.hspace.to_f
+               var sh = font.height.to_f + font.vspace.to_f
                for c in text.chars do
                        if c == '\n' then
                                cx = x
index 725c612..87afb4a 100644 (file)
 # limitations under the License.
 
 # Impements the services of `mnit:app` using the API from the Android ndk
-module android_app
+module android_app is
+       android_manifest_activity """
+               android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
+               android:configChanges="orientation|keyboardHidden"
+               android:screenOrientation="portrait""""
+end
 
 import mnit
 import android
index 20704b6..2c703bb 100644 (file)
@@ -129,8 +129,8 @@ redef class Opengles1Image
                int has_alpha;
 
                unsigned int row_bytes;
-               png_bytepp row_pointers;
-               unsigned char *pixels;
+               png_bytepp row_pointers = NULL;
+               unsigned char *pixels = NULL;
                unsigned int i;
 
                unsigned char sig[8];
@@ -166,23 +166,24 @@ redef class Opengles1Image
 
                png_get_IHDR(   png_ptr, info_ptr, &width, &height,
                                                &depth, &color_type, NULL, NULL, NULL);
-               if (color_type == PNG_COLOR_TYPE_RGBA)
-                       has_alpha = 1;
-               else if (color_type == PNG_COLOR_TYPE_RGB)
-                       has_alpha = 0;
-               else {
-                       LOGW("unknown color_type");
-                       goto close_png_ptr;
+               has_alpha = color_type & PNG_COLOR_MASK_ALPHA;
+
+               // If we get gray and alpha only, standardize the format of the pixels.
+               // GA is not supported by OpenGL ES 1.
+               if (!(color_type & PNG_COLOR_MASK_COLOR)) {
+                       png_set_gray_to_rgb(png_ptr);
+                       png_set_palette_to_rgb(png_ptr);
+                       png_read_update_info(png_ptr, info_ptr);
                }
 
                LOGW("w: %i, h: %i", width, height);
 
                row_bytes = png_get_rowbytes(png_ptr, info_ptr);
                pixels = malloc(row_bytes * height);
-        row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
+               row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
 
-        for (i=0; i<height; i++)
-            row_pointers[i] = (png_byte*) malloc(row_bytes);
+               for (i=0; i<height; i++)
+                       row_pointers[i] = (png_byte*) malloc(row_bytes);
 
                png_read_image(png_ptr, row_pointers);
 
@@ -199,6 +200,15 @@ redef class Opengles1Image
                else
                        png_destroy_read_struct(&png_ptr, NULL, NULL);
 
+               if (pixels != NULL)
+                       free(pixels);
+
+               if (row_pointers != NULL) {
+                       for (i=0; i<height; i++)
+                               free(row_pointers[i]);
+                       free(row_pointers);
+               }
+
        close_stream:
                return recv;
        `}
index dc5c8ac..e458eb1 100644 (file)
@@ -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 (file)
index 0000000..f9df000
--- /dev/null
@@ -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
index 49e055c..cf9f441 100644 (file)
@@ -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 (file)
index c82a240..0000000
+++ /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 "<unknown>"} 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 "<unknown>"} for {root_node}"
-                       return new JsonError("JsonParsingError", msg)
-               else abort
-       end
-end
-
index b84a60b..efe4f6a 100644 (file)
@@ -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/lib/pthreads/extra.nit b/lib/pthreads/extra.nit
new file mode 100644 (file)
index 0000000..416b174
--- /dev/null
@@ -0,0 +1,62 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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.
+
+# Offers some POSIX threads services that are not available on all platforms
+module extra is
+       c_compiler_option("-pthread")
+       c_linker_option("-pthread")
+end
+
+intrude import pthreads
+
+in "C" `{
+       // TODO protect with: #ifdef WITH_LIBGC
+       #ifndef ANDROID
+               #define GC_THREADS
+               #include <gc.h>
+       #endif
+`}
+
+redef extern class NativePthread
+       fun cancel: Bool `{
+               return pthread_cancel(*recv);
+       `}
+end
+
+redef class Thread
+       # Cancel the execution of the thread
+       fun cancel
+       do
+               if native == null then return
+               native.cancel
+               native = null
+       end
+end
+
+# Does not return if the running thread is to be cancelled
+fun test_cancel `{ pthread_testcancel(); `}
+
+private extern class NativePthreadBarrier in "C" `{ pthread_barrier_t * `}
+       new(count: Int) `{
+               pthread_barrier_t *barrier = malloc(sizeof(pthread_barrier_t));
+               int res = pthread_barrier_init(barrier, NULL, count);
+               return barrier;
+       `}
+
+       fun destroy `{ pthread_barrier_destroy(recv); `}
+
+       fun wait `{ pthread_barrier_wait(recv); `}
+end
index 1e76e28..94cf078 100644 (file)
@@ -34,9 +34,10 @@ in "C" `{
        // TODO protect with: #ifdef WITH_LIBGC
        // We might have to add the next line to gc_chooser.c too, especially
        // if we get an error like "thread not registered with GC".
+       #ifndef ANDROID
                #define GC_THREADS
                #include <gc.h>
-       //#endif
+       #endif
 `}
 
 redef class Sys
@@ -114,10 +115,6 @@ private extern class NativePthread in "C" `{ pthread_t * `}
                return (nullable_Object)thread_return;
        `}
 
-       fun cancel: Bool `{
-               return pthread_cancel(*recv);
-       `}
-
        fun attr: NativePthreadAttr `{
                pthread_attr_t *pattr = malloc(sizeof(pthread_attr_t));
                pthread_getattr_np(*recv, pattr);
@@ -192,18 +189,6 @@ private extern class NativePthreadMutexAttr in "C" `{ pthread_mutexattr_t * `}
        # pthread_mutexattr_setrobust_np
 end
 
-private extern class NativePthreadBarrier in "C" `{ pthread_barrier_t * `}
-       new(count: Int) `{
-               pthread_barrier_t *barrier = malloc(sizeof(pthread_barrier_t));
-               int res = pthread_barrier_init(barrier, NULL, count);
-               return barrier;
-       `}
-
-       fun destroy `{ pthread_barrier_destroy(recv); `}
-
-       fun wait `{ pthread_barrier_wait(recv); `}
-end
-
 private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
        new `{
                pthread_key_t *key = malloc(sizeof(pthread_key_t));
@@ -222,6 +207,27 @@ private extern class NativePthreadKey in "C" `{ pthread_key_t * `}
        `}
 end
 
+private extern class NativePthreadCond in "C" `{ pthread_cond_t * `}
+       new `{
+               pthread_cond_t cond;
+               int r = pthread_cond_init(&cond, NULL);
+               if (r == 0) {
+                       pthread_cond_t *pcond = malloc(sizeof(pthread_cond_t));
+                       memmove(pcond, &cond, sizeof(pthread_cond_t));
+                       return pcond;
+               }
+               return NULL;
+       `}
+
+       fun destroy `{ pthread_cond_destroy(recv); `}
+
+       fun signal `{ pthread_cond_signal(recv); `}
+
+       fun broadcast `{ pthread_cond_broadcast(recv);  `}
+
+       fun wait(mutex: NativePthreadMutex) `{ pthread_cond_wait(recv, mutex); `}
+end
+
 #
 ## Nity part
 #
@@ -272,14 +278,6 @@ abstract class Thread
                return r
        end
 
-       # Cancel the execution of the thread
-       fun cancel
-       do
-               if native == null then return
-               native.cancel
-               native = null
-       end
-
        redef fun finalize
        do
                if native == null then return
@@ -298,9 +296,6 @@ end
 # Exit current thread and return `value` to caller of `Thread::join`
 fun exit_thread(value: nullable Object) `{ pthread_exit(value); `}
 
-# Does not return if the running thread is to be cancelled
-fun test_cancel `{ pthread_testcancel(); `}
-
 # Returns the handle to the running `Thread`
 fun thread: Thread
 do
@@ -364,24 +359,36 @@ end
 class Barrier
        super Finalizable
 
+       private var mutex = new Mutex
+       private var cond: nullable NativePthreadCond = new NativePthreadCond
+
        # Number of threads that must be waiting for `wait` to unblock
        var count: Int
 
-       private var native: nullable NativePthreadBarrier is noinit
-
-       init do native = new NativePthreadBarrier(count)
+       private var threads_waiting = 0
 
        # Wait at this barrier and block until there are a `count` threads waiting
-       fun wait do native.wait
+       fun wait
+       do
+               mutex.lock
+               threads_waiting += 1
+               if threads_waiting == count then
+                       threads_waiting = 0
+                       cond.broadcast
+               else
+                       cond.wait(mutex.native.as(not null))
+               end
+               mutex.unlock
+       end
 
        redef fun finalize
        do
-               var native = self.native
-               if native != null then
-                       native.destroy
-                       native.free
+               var cond = self.cond
+               if cond != null then
+                       cond.destroy
+                       cond.free
                end
-               self.native = null
+               self.cond = null
        end
 end
 
index 7d710bd..ca6e9fa 100644 (file)
@@ -251,7 +251,6 @@ end
 #     assert a == b
 class Array[E]
        super AbstractArray[E]
-       super ArrayCapable[E]
 
        redef fun [](index)
        do
@@ -286,7 +285,7 @@ class Array[E]
                var c = _capacity
                if cap <= c then return
                while c <= cap do c = c * 2 + 2
-               var a = calloc_array(c)
+               var a = new NativeArray[E](c)
                if _capacity > 0 then _items.copy_to(a, _length)
                _items = a
                _capacity = c
@@ -317,7 +316,7 @@ class Array[E]
        init with_capacity(cap: Int)
        do
                assert positive: cap >= 0
-               _items = calloc_array(cap)
+               _items = new NativeArray[E](cap)
                _capacity = cap
                _length = 0
        end
@@ -326,7 +325,7 @@ class Array[E]
        init filled_with(value: E, count: Int)
        do
                assert positive: count >= 0
-               _items = calloc_array(count)
+               _items = new NativeArray[E](count)
                _capacity = count
                _length = count
                var i = 0
@@ -769,12 +768,6 @@ end
 
 # Native classes ##############################################################
 
-# Subclasses of this class can create native arrays
-interface ArrayCapable[E]
-       # Get a new array of `size` elements.
-       protected fun calloc_array(size: Int): NativeArray[E] is intern
-end
-
 # Native Nit array
 # Access are unchecked and it has a fixed size
 # Not for public use: may become private.
index 6490024..92b8d5c 100644 (file)
@@ -16,8 +16,8 @@ module hash_collection
 import array
 
 # A HashCollection is an array of HashNode[K] indexed by the K hash value
-private abstract class HashCollection[K: Object, N: HashNode[Object]]
-       super ArrayCapable[nullable N]
+private abstract class HashCollection[K: Object]
+       type N: HashNode[K]
 
        private var array: nullable NativeArray[nullable N] = null # Used to store items
        private var capacity: Int = 0 # Size of _array
@@ -163,7 +163,7 @@ private abstract class HashCollection[K: Object, N: HashNode[Object]]
                _last_accessed_key = null
 
                # get a new array
-               var new_array = calloc_array(cap)
+               var new_array = new NativeArray[nullable N](cap)
                _array = new_array
 
                # clean the new array
@@ -203,7 +203,9 @@ end
 # Keys of such a map cannot be null and require a working `hash` method
 class HashMap[K: Object, V]
        super Map[K, V]
-       super HashCollection[K, HashMapNode[K, V]]
+       super HashCollection[K]
+
+       redef type N: HashMapNode[K, V] is fixed
 
        redef fun [](key)
        do
@@ -389,7 +391,9 @@ end
 # Keys of such a map cannot be null and require a working `hash` method
 class HashSet[E: Object]
        super Set[E]
-       super HashCollection[E, HashSetNode[E]]
+       super HashCollection[E]
+
+       redef type N: HashSetNode[E] is fixed
 
        redef fun length do return _the_length
 
index e865703..f9ac76f 100644 (file)
@@ -37,6 +37,17 @@ class DisjointSet[E: Object]
        # The node in the hiearchical structure for each element
        private var nodes = new HashMap[E, DisjointSetNode]
 
+       # The number of subsets in the partition
+       #
+       #     var s = new DisjointSet[Int]
+       #     s.add_all([1,2,3,4,5])
+       #     assert s.number_of_subsets == 5
+       #     s.union_all([1,4,5])
+       #     assert s.number_of_subsets == 3
+       #     s.union(4,5)
+       #     assert s.number_of_subsets == 3
+       var number_of_subsets: Int = 0
+
        # Get the root node of an element
        # require: `has(e)`
        private fun find(e:E): DisjointSetNode
@@ -84,6 +95,7 @@ class DisjointSet[E: Object]
                if nodes.has_key(e) then return
                var ne = new DisjointSetNode
                nodes[e] = ne
+               number_of_subsets += 1
        end
 
        # Are two elements in the same subset?
@@ -171,6 +183,7 @@ class DisjointSet[E: Object]
                                ne.rank = er+1
                        end
                end
+               number_of_subsets -= 1
        end
 
        # Combine the subsets of all elements of `es`
index 6d2e4c6..a179e9a 100644 (file)
@@ -353,6 +353,21 @@ universal Float
                end
        end
 
+       # Compare float numbers with a given precision.
+       #
+       # Because of the loss of precision in floating numbers,
+       # the `==` method is often not the best way to compare them.
+       #
+       # ~~~
+       # assert 0.01.is_approx(0.02, 0.1)   == true
+       # assert 0.01.is_approx(0.02, 0.001) == false
+       # ~~~
+       fun is_approx(other, precision: Float): Bool
+       do
+               assert precision >= 0.0
+               return self <= other + precision and self >= other - precision
+       end
+
        redef fun max(other)
        do
                if self < other then
index 5a980aa..6214a65 100644 (file)
@@ -138,6 +138,14 @@ redef class Float
        #     assert 2.0.floor == 2.0
        #     assert (-1.5).floor == -2.0
        fun floor: Float `{ return floor(recv); `}
+
+       # Rounds the value of a float to its nearest integer value
+       #
+       #     assert 1.67.round == 2.0
+       #     assert 1.34.round == 1.0
+       #     assert -1.34.round == -1.0
+       #     assert -1.67.round == -2.0
+       fun round: Float is extern "round"
        
        # Returns a random `Float` in `[0.0 .. self[`.
        fun rand: Float is extern "kernel_Float_Float_rand_0"
@@ -178,7 +186,31 @@ redef class Collection[ E ]
        end
 end
 
+redef class Sys
+       init
+       do
+               srand
+       end
+end
+
 fun atan2(x: Float, y: Float): Float is extern "kernel_Any_Any_atan2_2"
 fun pi: Float is extern "kernel_Any_Any_pi_0"
+
+# Initialize the pseudo-random generator with the given seed.
+# The pseudo-random generator is used by the method `rand` and other to generate sequence of numbers.
+# These sequences are repeatable by calling `srand_from` with a same seed value.
+#
+# ~~~~
+# srand_from(0)
+# var a = 10.rand
+# var b = 100.rand
+# srand_from(0)
+# assert 10.rand == a
+# assert 100.rand == b
+# ~~~~
 fun srand_from(x: Int) is extern "kernel_Any_Any_srand_from_1"
+
+# Reinitialize the pseudo-random generator used by the method `rand` and other.
+# This method is automatically invoked at the begin of the program, so usually, there is no need to manually invoke it.
+# The only exception is in conjunction with `srand_from` to reset the pseudo-random generator.
 fun srand is extern "kernel_Any_Any_srand_0"
index 28ad7b5..48cb238 100644 (file)
@@ -30,7 +30,6 @@ intrude import collection::array
 # High-level abstraction for all text representations
 abstract class Text
        super Comparable
-       super StringCapable
 
        redef type OTHER: Text
 
@@ -515,6 +514,29 @@ abstract class Text
                return b.to_s
        end
 
+       # Escape to include in a Makefile
+       #
+       # Unfortunately, some characters are not escapable in Makefile.
+       # These characters are `;`, `|`, `\`, and the non-printable ones.
+       # They will be rendered as `"?{hex}"`.
+       fun escape_to_mk: String do
+               var b = new FlatBuffer
+               for i in [0..length[ do
+                       var c = chars[i]
+                       if c == '$' then
+                               b.append("$$")
+                       else if c == ':' or c == ' ' or c == '#' then
+                               b.add('\\')
+                               b.add(c)
+                       else if c.ascii < 32 or c == ';' or c == '|' or c == '\\' or c == '=' then
+                               b.append("?{c.ascii.to_base(16, false)}")
+                       else
+                               b.add(c)
+                       end
+               end
+               return b.to_s
+       end
+
        # Return a string where Nit escape sequences are transformed.
        #
        #     var s = "\\n"
@@ -969,7 +991,7 @@ class FlatString
 
        redef fun reversed
        do
-               var native = calloc_string(self.length + 1)
+               var native = new NativeString(self.length + 1)
                var length = self.length
                var items = self.items
                var pos = 0
@@ -1007,7 +1029,7 @@ class FlatString
 
        redef fun to_upper
        do
-               var outstr = calloc_string(self.length + 1)
+               var outstr = new NativeString(self.length + 1)
                var out_index = 0
 
                var myitems = self.items
@@ -1027,7 +1049,7 @@ class FlatString
 
        redef fun to_lower
        do
-               var outstr = calloc_string(self.length + 1)
+               var outstr = new NativeString(self.length + 1)
                var out_index = 0
 
                var myitems = self.items
@@ -1072,7 +1094,7 @@ class FlatString
                if real_items != null then
                        return real_items.as(not null)
                else
-                       var newItems = calloc_string(length + 1)
+                       var newItems = new NativeString(length + 1)
                        self.items.copy_to(newItems, length, index_from, 0)
                        newItems[length] = '\0'
                        self.real_items = newItems
@@ -1150,7 +1172,7 @@ class FlatString
 
                var total_length = my_length + its_length
 
-               var target_string = calloc_string(my_length + its_length + 1)
+               var target_string = new NativeString(my_length + its_length + 1)
 
                self.items.copy_to(target_string, my_length, index_from, 0)
                if s isa FlatString then
@@ -1181,7 +1203,7 @@ class FlatString
 
                var my_items = self.items
 
-               var target_string = calloc_string((final_length) + 1)
+               var target_string = new NativeString(final_length + 1)
 
                target_string[final_length] = '\0'
 
@@ -1479,7 +1501,7 @@ class FlatBuffer
                # The COW flag can be set at false here, since
                # it does a copy of the current `Buffer`
                written = false
-               var a = calloc_string(c+1)
+               var a = new NativeString(c+1)
                if length > 0 then items.copy_to(a, length, 0, 0)
                items = a
                capacity = c
@@ -1495,7 +1517,7 @@ class FlatBuffer
        redef fun to_cstring
        do
                if is_dirty then
-                       var new_native = calloc_string(length + 1)
+                       var new_native = new NativeString(length + 1)
                        new_native[length] = '\0'
                        if length > 0 then items.copy_to(new_native, length, 0, 0)
                        real_items = new_native
@@ -1511,7 +1533,7 @@ class FlatBuffer
        do
                capacity = s.length + 1
                length = s.length
-               items = calloc_string(capacity)
+               items = new NativeString(capacity)
                if s isa FlatString then
                        s.items.copy_to(items, length, s.index_from, 0)
                else if s isa FlatBuffer then
@@ -1531,7 +1553,7 @@ class FlatBuffer
        do
                assert cap >= 0
                # _items = new NativeString.calloc(cap)
-               items = calloc_string(cap+1)
+               items = new NativeString(cap+1)
                capacity = cap
                length = 0
        end
@@ -1588,7 +1610,7 @@ class FlatBuffer
        redef fun reverse
        do
                written = false
-               var ns = calloc_string(capacity)
+               var ns = new NativeString(capacity)
                var si = length - 1
                var ni = 0
                var it = items
@@ -1659,7 +1681,6 @@ end
 
 private class FlatBufferCharView
        super BufferCharView
-       super StringCapable
 
        redef type SELFTYPE: FlatBuffer
 
@@ -2095,7 +2116,6 @@ end
 
 # Native strings are simple C char *
 extern class NativeString `{ char* `}
-       super StringCapable
        # Creates a new NativeString with a capacity of `length`
        new(length: Int) is intern
        fun [](index: Int): Char is intern
@@ -2127,7 +2147,7 @@ extern class NativeString `{ char* `}
        fun to_s_with_copy: FlatString
        do
                var length = cstring_length
-               var new_self = calloc_string(length + 1)
+               var new_self = new NativeString(length + 1)
                copy_to(new_self, length, 0, 0)
                var str = new FlatString.with_infos(new_self, length, 0, length - 1)
                new_self[length] = '\0'
@@ -2136,11 +2156,6 @@ extern class NativeString `{ char* `}
        end
 end
 
-# StringCapable objects can create native strings
-interface StringCapable
-       protected fun calloc_string(size: Int): NativeString is intern
-end
-
 redef class Sys
        private var args_cache: nullable Sequence[String]
 
index 0159249..1a11e99 100644 (file)
@@ -14,7 +14,7 @@
 // Integer to NativeString method
 char* native_int_to_s(long recv){
        int len = snprintf(NULL, 0, "%ld", recv);
-       char* str = malloc(len);
+       char* str = malloc(len+1);
        sprintf(str, "%ld", recv);
        return str;
 }
index 5feef35..b7a74ce 100644 (file)
@@ -214,7 +214,7 @@ redef class FlatString
        redef fun to_cstring
        do
                if real_items != null then return real_items.as(not null)
-               var new_items = calloc_string(bytelen + 1)
+               var new_items = new NativeString(bytelen + 1)
                self.items.copy_to(new_items, bytelen, index[index_from].pos, 0)
                new_items[bytelen] = '\0'
                self.real_items = new_items
@@ -245,7 +245,7 @@ redef class FlatString
 
        redef fun reversed
        do
-               var native = calloc_string(self.bytelen + 1)
+               var native = new NativeString(self.bytelen + 1)
                var length = self.length
                var index = self.index
                var pos = 0
@@ -278,7 +278,7 @@ redef class FlatString
                var my_real_len = length
                var my_real_fin_len = my_real_len * i
 
-               var target_string = calloc_string((finlen) + 1)
+               var target_string = new NativeString((finlen) + 1)
 
                var my_index = index
                var new_index = new StringIndex(my_real_fin_len)
@@ -300,7 +300,7 @@ redef class FlatString
 
        redef fun to_upper
        do
-               var outstr = calloc_string(self.bytelen + 1)
+               var outstr = new NativeString(self.bytelen + 1)
 
                var out_index = 0
                var index = self.index
@@ -322,7 +322,7 @@ redef class FlatString
 
        redef fun to_lower
        do
-               var outstr = calloc_string(self.bytelen + 1)
+               var outstr = new NativeString(self.bytelen + 1)
 
                var out_index = 0
                var index = self.index
@@ -406,7 +406,7 @@ redef class NativeString
                var real_len = new Container[Int](0)
                var length = cstring_length
                var x = make_index(length, real_len)
-               var new_self = calloc_string(length + 1)
+               var new_self = new NativeString(length + 1)
                copy_to(new_self, length, 0, 0)
                return new FlatString.with_infos_index(new_self, real_len.item, 0, real_len.item - 1, x, length)
        end
index c473a3c..db51f0a 100644 (file)
@@ -401,7 +401,7 @@ redef class FlatString
        end
 
        redef fun reversed do
-               var new_str = calloc_string(bytelen)
+               var new_str = new NativeString(bytelen)
                var s_pos = bytelen
                var my_pos = index_from
                var its = items
@@ -415,7 +415,7 @@ redef class FlatString
        end
 
        redef fun to_upper do
-               var ns = calloc_string(bytelen)
+               var ns = new NativeString(bytelen)
                var offset = 0
                for i in [0 .. length[
                do
@@ -427,7 +427,7 @@ redef class FlatString
        end
 
        redef fun to_lower do
-               var ns = calloc_string(bytelen)
+               var ns = new NativeString(bytelen)
                var offset = 0
                for i in [0 .. length[
                do
@@ -441,7 +441,7 @@ redef class FlatString
        redef fun +(o) do
                if o isa Buffer then o = o.to_s
                if o isa FlatString then
-                       var new_str = calloc_string(bytelen + o.bytelen + 1)
+                       var new_str = new NativeString(bytelen + o.bytelen + 1)
                        var new_bytelen = bytelen + o.bytelen
                        new_str[new_bytelen] = '\0'
                        var newlen = length + o.length
@@ -461,7 +461,7 @@ redef class FlatString
                var new_bytelen = mybtlen * i
                var mylen = length
                var newlen = mylen * i
-               var ns = calloc_string(new_bytelen + 1)
+               var ns = new NativeString(new_bytelen + 1)
                ns[new_bytelen] = '\0'
                var offset = 0
                while i > 0 do
@@ -499,7 +499,7 @@ redef class FlatString
 
        redef fun to_cstring do
                if real_items != null then return real_items.as(not null)
-               var new_items = calloc_string(bytelen + 1)
+               var new_items = new NativeString(bytelen + 1)
                self.items.copy_to(new_items, bytelen, index_from, 0)
                new_items[bytelen] = '\0'
                self.real_items = new_items
@@ -523,7 +523,7 @@ redef class FlatBuffer
                        with_capacity(50)
                        for i in s.substrings do self.append(i)
                end
-               items = calloc_string(s.bytelen)
+               items = new NativeString(s.bytelen)
                if s isa FlatString then
                        s.items.copy_to(items, s.bytelen, s.index_from, 0)
                else
@@ -611,7 +611,7 @@ redef class FlatBuffer
                var c = capacity
                if cap <= c then return
                while c <= cap do c = c * 2 + 2
-               var a = calloc_string(c+1)
+               var a = new NativeString(c+1)
                if bytelen > 0 then items.copy_to(a, bytelen, 0, 0)
                items = a
                capacity = c
@@ -635,7 +635,7 @@ redef class FlatBuffer
 
        redef fun reverse
        do
-               var nns = calloc_string(bytelen)
+               var nns = new NativeString(bytelen)
                var ns = items
                var btlen = bytelen
                var myp = 0
@@ -701,7 +701,7 @@ redef class FlatBuffer
        end
 
        redef fun to_cstring do
-               var ns = calloc_string(bytelen)
+               var ns = new NativeString(bytelen)
                items.copy_to(ns, bytelen, 0, 0)
                return ns
        end
@@ -723,7 +723,7 @@ redef class NativeString
        redef fun to_s_with_copy
        do
                var length = cstring_length
-               var new_self = calloc_string(length + 1)
+               var new_self = new NativeString(length + 1)
                copy_to(new_self, length, 0, 0)
                return new FlatString.with_bytelen(new_self, 0, length - 1, length)
        end
index 16c0e4b..abfbf3f 100644 (file)
@@ -315,7 +315,14 @@ class MakefileToolchain
 
                var outname = outfile(mainmodule)
 
-               var outpath = compile_dir.relpath(outname)
+               var real_outpath = compile_dir.relpath(outname)
+               var outpath = real_outpath.escape_to_mk
+               if outpath != real_outpath then
+                       # If the name is crazy and need escaping, we will do an indirection
+                       # 1. generate the binary in the .nit_compile dir under an escaped name
+                       # 2. copy the binary at the right place in the `all` goal.
+                       outpath = mainmodule.c_name
+               end
                var makename = makefile_name(mainmodule)
                var makepath = "{compile_dir}/{makename}"
                var makefile = new OFStream.open(makepath)
@@ -348,7 +355,11 @@ class MakefileToolchain
 
                makefile.write("ifdef NEED_LIBUNWIND\n\tLDLIBS += -lunwind\nendif\n")
 
-               makefile.write("all: {outpath}\n\n")
+               makefile.write("all: {outpath}\n")
+               if outpath != real_outpath then
+                       makefile.write("\tcp -- {outpath.escape_to_sh} {real_outpath.escape_to_sh.replace("$","$$")}")
+               end
+               makefile.write("\n")
 
                var ofiles = new Array[String]
                var dep_rules = new Array[String]
@@ -413,9 +424,12 @@ endif
                if not pkgconfigs.is_empty then
                        pkg = "`pkg-config --libs {pkgconfigs.join(" ")}`"
                end
-               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
+               makefile.write("{outpath}: {dep_rules.join(" ")}\n\t$(CC) $(LDFLAGS) -o {outpath.escape_to_sh} {ofiles.join(" ")} $(LDLIBS) {pkg}\n\n")
                # Clean
-               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n\n")
+               makefile.write("clean:\n\trm {ofiles.join(" ")} 2>/dev/null\n")
+               if outpath != real_outpath then
+                       makefile.write("\trm -- {outpath.escape_to_sh} 2>/dev/null\n")
+               end
                makefile.close
                self.toolcontext.info("Generated makefile: {makepath}", 2)
 
@@ -524,9 +538,9 @@ abstract class AbstractCompiler
                stream.write("static const C_Nit_Names map[{names.length}] = \{\n")
                for i in names.keys do
                        stream.write("\{\"")
-                       stream.write(i)
+                       stream.write(i.escape_to_c)
                        stream.write("\",\"")
-                       stream.write(names[i])
+                       stream.write(names[i].escape_to_c)
                        stream.write("\"\},\n")
                end
                stream.write("\};\n")
index cc24d6e..ad34b9a 100644 (file)
@@ -44,6 +44,9 @@ class AndroidProject
        # Custom lines to add to the AndroidManifest.xml in the <application> node
        var manifest_application_lines = new Array[String]
 
+       # Custom lines to add to AndroidManifest.xml as attributes inside the <activity> node
+       var manifest_activity_attributes = new Array[String]
+
        # Minimum API level required for the application to run
        var min_api: nullable Int = null
 
@@ -107,6 +110,9 @@ redef class ModelBuilder
                annots = collect_annotations_on_modules("android_manifest_application", mmodule)
                for an in annots do project.manifest_application_lines.add an.arg_as_string(self) or else ""
 
+               annots = collect_annotations_on_modules("android_manifest_activity", mmodule)
+               for an in annots do project.manifest_activity_attributes.add an.arg_as_string(self) or else ""
+
                # Get the date and time (down to the minute) as string
                var local_time = new Tm.localtime
                var local_time_s = local_time.strftime("%y%m%d%H%M")
index 55468fd..1cf6ba8 100644 (file)
@@ -124,10 +124,12 @@ class AndroidToolchain
 
                # Also copy over the java files
                dir = "{android_project_root}/src/"
-               var extra_java_files = compiler.mainmodule.extra_java_files
-               if extra_java_files != null then for file in extra_java_files do
-                       var path = file.filename
-                       path.file_copy_to("{dir}/{path.basename("")}")
+               for mmodule in compiler.mainmodule.in_importation.greaters do
+                       var extra_java_files = mmodule.extra_java_files
+                       if extra_java_files != null then for file in extra_java_files do
+                               var path = file.filename
+                               path.file_copy_to(dir/path.basename(""))
+                       end
                end
 
                ## Generate delagating makefile
@@ -178,13 +180,11 @@ $(call import-module,android/native_app_glue)
              This will take care of integrating with our NDK code. -->
         <activity android:name="android.app.NativeActivity"
                 android:label="@string/app_name"
-                android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
-                android:configChanges="orientation|keyboardHidden"
-                android:screenOrientation="portrait"
+                {{{project.manifest_activity_attributes.join("\n")}}}
                 {{{icon_declaration}}}>
-            <!-- Tell NativeActivity the name of or .so -->
-            <meta-data android:name=\"{{{app_package}}}\"
-                    android:value=\"{{{app_name}}}\" />
+            <!-- Tell NativeActivity the name of our .so -->
+            <meta-data android:name=\"android.app.lib_name\"
+                    android:value=\"main\" />
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
index 113b549..94dda37 100644 (file)
@@ -116,7 +116,7 @@ class GlobalCompiler
 
        init
        do
-               var file = new_file("{mainmodule.name}.nitgg")
+               var file = new_file("{mainmodule.c_name}.nitgg")
                self.header = new CodeWriter(file)
                self.live_primitive_types = new Array[MClassType]
                for t in runtime_type_analysis.live_types do
@@ -396,6 +396,7 @@ class GlobalCompilerVisitor
        redef fun native_array_instance(elttype: MType, length: RuntimeVariable): RuntimeVariable
        do
                var ret_type = self.get_class("NativeArray").get_mtype([elttype])
+               ret_type = anchor(ret_type).as(MClassType)
                return self.new_expr("NEW_{ret_type.c_name}({length})", ret_type)
        end
 
index 5d71b02..78ccd7f 100644 (file)
@@ -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")
@@ -182,9 +183,7 @@ end
 # All entities are grouped by name to make the research easier.
 class QuickSearch
 
-       private var mmodules = new HashSet[MModule]
-       private var mclasses = new HashSet[MClass]
-       private var mpropdefs = new HashMap[String, Set[MPropDef]]
+       private var table = new QuickSearchTable
 
        var ctx: ToolContext
        var model: Model
@@ -192,51 +191,72 @@ class QuickSearch
        init do
                for mmodule in model.mmodules do
                        if mmodule.is_fictive then continue
-                       mmodules.add mmodule
+                       add_result_for(mmodule.name, mmodule.full_name, mmodule.nitdoc_url)
                end
                for mclass in model.mclasses do
                        if mclass.visibility < ctx.min_visibility then continue
-                       mclasses.add mclass
+                       add_result_for(mclass.name, mclass.full_name, mclass.nitdoc_url)
                end
                for mproperty in model.mproperties do
                        if mproperty.visibility < ctx.min_visibility then continue
                        if mproperty isa MAttribute then continue
-                       if not mpropdefs.has_key(mproperty.name) then
-                               mpropdefs[mproperty.name] = new HashSet[MPropDef]
+                       for mpropdef in mproperty.mpropdefs 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}"
+                               add_result_for(mproperty.name, full_name, def_url)
                        end
-                       mpropdefs[mproperty.name].add_all(mproperty.mpropdefs)
                end
        end
 
+       private fun add_result_for(query: String, txt: String, url: String) do
+               table[query].add new QuickSearchResult(txt, url)
+       end
+
        fun render: Template do
                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 "],"
-               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 "],"
-               end
-               for mproperty, mprops in mpropdefs do
-                       tpl.add "\"{mproperty}\":["
-                       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}\"\},"
-                       end
-                       tpl.add "],"
-               end
-               tpl.add " \};"
+               var buffer = new RopeBuffer
+               tpl.add buffer
+               buffer.append "var nitdocQuickSearchRawList="
+               table.append_json buffer
+               buffer.append ";"
                return tpl
        end
 end
 
+# The result map for QuickSearch.
+private class QuickSearchTable
+       super JsonMapRead[String, QuickSearchResultList]
+       super HashMap[String, QuickSearchResultList]
+
+       redef fun provide_default_value(key) do
+               var v = new QuickSearchResultList
+               self[key] = v
+               return v
+       end
+end
+
+# A QuickSearch result list.
+private class QuickSearchResultList
+       super JsonSequenceRead[QuickSearchResult]
+       super Array[QuickSearchResult]
+end
+
+# A QuickSearch result.
+private class QuickSearchResult
+       super Jsonable
+
+       # The text of the link.
+       var txt: String
+
+       # The destination of the link.
+       var url: String
+
+       redef fun to_json do
+               return "\{\"txt\":{txt.to_json},\"url\":{url.to_json}\}"
+       end
+end
+
 # Nitdoc base page
 # Define page structure and properties
 abstract class NitdocPage
index fa3cc84..9191141 100644 (file)
@@ -242,7 +242,7 @@ redef class MModule
        # Tell the C compiler where to find jni.h and how to link with libjvm
        private fun insert_compiler_options
        do
-               c_compiler_options = "{c_compiler_options} -I $(JAVA_HOME)/include/"
+               c_compiler_options = "{c_compiler_options} -I $(JAVA_HOME)/include/ -I $(JAVA_HOME)/include/linux/"
                c_linker_options = "{c_linker_options} -L $(JNI_LIB_PATH) -ljvm"
        end
 
index 86bba5f..476608c 100644 (file)
@@ -901,6 +901,8 @@ redef class AMethPropdef
                                return v.bool_instance(args[0].to_f.is_nan)
                        else if pname == "is_inf_extern" then
                                return v.bool_instance(args[0].to_f.is_inf != 0)
+                       else if pname == "round" then
+                               return v.float_instance(args[0].to_f.round)
                        end
                else if cname == "NativeString" then
                        if pname == "new" then
@@ -1046,6 +1048,9 @@ redef class AMethPropdef
                        return v.native_string_instance(txt)
                else if pname == "get_time" then
                        return v.int_instance(get_time)
+               else if pname == "srand" then
+                       srand
+                       return null
                else if pname == "srand_from" then
                        srand_from(args[1].to_i)
                        return null
index 01a054c..e254480 100644 (file)
@@ -27,6 +27,9 @@ private class Doc2Mdwn
        # The lines of the current code block, empty is no current code block
        var curblock = new Array[String]
 
+       # Count empty lines between code blocks
+       var empty_lines = 0
+
        fun work(mdoc: MDoc): HTMLTag
        do
                var root = new HTMLTag("div")
@@ -70,16 +73,18 @@ private class Doc2Mdwn
 
                        # Is codeblock? Then just collect them
                        if indent >= 3 then
+                               for i in [0..empty_lines[ do curblock.add("")
+                               empty_lines = 0
                                # to allows 4 spaces including the one that follows the #
                                curblock.add(text)
                                continue
                        end
 
-                       # Was a codblock just before the current line ?
-                       close_codeblock(n or else root)
-
                        # fence opening
                        if text.substring(0,3) == "~~~" then
+                               # Was a codblock just before the current line ?
+                               close_codeblock(n or else root)
+
                                var l = 3
                                while l < text.length and text.chars[l] == '~' do l += 1
                                in_fence = text.substring(0, l)
@@ -96,9 +101,15 @@ private class Doc2Mdwn
                        if text.is_empty or indent < lastindent then
                                n = null
                                ul = null
-                               if text.is_empty then continue
+                               if text.is_empty then
+                                       if not curblock.is_empty then empty_lines += 1
+                                       continue
+                               end
                        end
 
+                       # Was a codblock just before the current line ?
+                       close_codeblock(n or else root)
+
                        # Special first word: new paragraph
                        if text.has_prefix("TODO") or text.has_prefix("FIXME") then
                                n = new HTMLTag("p")
@@ -186,15 +197,20 @@ private class Doc2Mdwn
        do
                # Is there a codeblock to manage?
                if not curblock.is_empty then
+                       empty_lines = 0
+
                        # determine the smalest indent
                        var minindent = -1
                        for text in curblock do
                                var indent = 0
                                while indent < text.length and text.chars[indent] == ' ' do indent += 1
+                               # skip white lines
+                               if indent >= text.length then continue
                                if minindent == -1 or indent < minindent then
                                        minindent = indent
                                end
                        end
+                       if minindent < 0 then minindent = 0
 
                        # Generate the text
                        var btext = new FlatBuffer
index 83f5e0e..1ae7e45 100644 (file)
@@ -705,6 +705,13 @@ class ModelBuilder
                self.toolcontext.info("{mmodule} imports {imported_modules.join(", ")}", 3)
                mmodule.set_imported_mmodules(imported_modules)
 
+               # Force standard to be public if imported
+               for sup in mmodule.in_importation.greaters do
+                       if sup.name == "standard" then
+                               mmodule.set_visibility_for(sup, public_visibility)
+                       end
+               end
+
                # TODO: Correctly check for useless importation
                # It is even doable?
                var directs = mmodule.in_importation.direct_greaters
index f393950..7bb39ca 100644 (file)
@@ -36,6 +36,9 @@ class NitUnitExecutor
 
        redef fun process_code(n: HTMLTag, text: String)
        do
+               # Skip non-blocks
+               if n.tag != "pre" then return
+
                # Try to parse it
                var ast = toolcontext.parse_something(text)
 
@@ -98,7 +101,7 @@ class NitUnitExecutor
                toolcontext.modelbuilder.unit_entities += 1
 
                cpt += 1
-               var file = "{prefix}{cpt}.nit"
+               var file = "{prefix}-{cpt}.nit"
 
                toolcontext.info("Execute doc-unit {tc.attrs["name"]} in {file}", 1)
 
diff --git a/tests/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.nit b/tests/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.nit
new file mode 100644 (file)
index 0000000..11c4b4c
--- /dev/null
@@ -0,0 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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.
+
+import kernel
+
+1.output
diff --git a/tests/base_import_standard.nit b/tests/base_import_standard.nit
new file mode 100644 (file)
index 0000000..2e76488
--- /dev/null
@@ -0,0 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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.
+
+private import template
+
+print 1
diff --git a/tests/base_import_standard2.nit b/tests/base_import_standard2.nit
new file mode 100644 (file)
index 0000000..da5111e
--- /dev/null
@@ -0,0 +1,17 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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.
+
+import base_import_standard
+
+print 2
index ac93fb4..ac1d618 100644 (file)
@@ -31,7 +31,7 @@ class B
                'B'.output
                '1'.output
                ' '.output
-               inita
+               super
                'B'.output
                '2'.output
                ' '.output
@@ -45,7 +45,7 @@ class C
                'C'.output
                '1'.output
                ' '.output
-               inita
+               super
                'C'.output
                '2'.output
                ' '.output
@@ -58,52 +58,12 @@ class D
        super C
        init do
                'D'.output
-               '0'.output
-               ' '.output
-#alt1#         inita
-#alt3#         inita
-#alt5#         inita
-#alt7#         inita
-#alt9#         inita
-#alt11#                inita
-#alt13#                inita
-#alt15#                inita
-               'D'.output
                '1'.output
                ' '.output
-#alt2#         initb
-#alt3#         initb
-#alt6#         initb
-#alt7#         initb
-#alt10#                initb
-#alt11#                initb
-#alt14#                initb
-#alt15#                initb
+               super
                'D'.output
                '2'.output
                ' '.output
-#alt4#         initc
-#alt5#         initc
-#alt6#         initc
-#alt7#         initc
-#alt12#                initc
-#alt13#                initc
-#alt14#                initc
-#alt15#                initc
-               'D'.output
-               '3'.output
-               ' '.output
-#alt8#         inite
-#alt9#         inite
-#alt10#                inite
-#alt11#                inite
-#alt12#                inite
-#alt13#                inite
-#alt14#                inite
-#alt15#                inite
-               'D'.output
-               '4'.output
-               ' '.output
        end
 
        init inite do
index 7b5bfc4..aa032c0 100644 (file)
@@ -68,7 +68,7 @@ class C
                'C'.output
                '1'.output
                ' '.output
-               inita
+               super
                'C'.output
                '2'.output
                ' '.output
index 2e20b7e..93a2d8f 100644 (file)
@@ -1,10 +1,3 @@
 bcm2835
-gtk3_6
-gtk3_8
-posix_ext
-physical_interface_for_mpd_on_rpi
 blink
 input
-android_app
-android_assets
-opengles1
index c782ec0..e69de29 100644 (file)
@@ -1,2 +0,0 @@
-init_inherit
-init_linext
index b74fc3c..4872f08 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import end
-class Object
-end
+import kernel
 
 class A
-       fun toto do end
+       fun toto do 1.output
 end
 class B
        super A
-       redef fun toto do end
+       redef fun toto do 2.output
 end
 class C
        super A
-       redef fun toto do end
+       redef fun toto do 3.output
 end
 class D
        super B
        super C
 end
+
+(new A).toto
+(new B).toto
+(new C).toto
+#alt1#(new D).toto
index c782ec0..e69de29 100644 (file)
@@ -1,2 +0,0 @@
-init_inherit
-init_linext
index fb091a0..7175bb7 100644 (file)
@@ -1,3 +1,2 @@
-init_inherit
-init_linext
 nitg
+nitdoc
index c782ec0..e69de29 100644 (file)
@@ -1,2 +0,0 @@
-init_inherit
-init_linext
index 5f17ed8..e69de29 100644 (file)
@@ -1,3 +0,0 @@
-init_inherit
-init_linext
-android
index baf5b76..1fec913 100644 (file)
@@ -1,5 +1,3 @@
-init_inherit
-init_linext
 test_mem
 shoot_logic
 bench_
@@ -16,7 +14,6 @@ nitg_args6
 nitg_args8
 test_markdown_args1
 pep8analysis
-nitcc_parser_gen
 emscripten
 nitserial_args
 nitunit_args
index cc7239d..70d5027 100644 (file)
@@ -1,3 +1,4 @@
 test_nitunit.nit --no-color -o $WRITE
 test_nitunit.nit --gen-suite --only-show
 test_nitunit.nit --gen-suite --only-show --private
+test_nitunit2.nit -o $WRITE
index afed2b6..da1d367 100644 (file)
@@ -1,5 +1,3 @@
-init_inherit
-init_linext
 test_mem
 shoot_logic
 bench_
diff --git a/tests/sav/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.res b/tests/sav/--base_very bad name@1&$|;:.ext'"<>{}`~!#^*()_=+[]\,?.res
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
index a9dbe22..a5dab0a 100644 (file)
@@ -1 +1 @@
-Runtime error: Assert 'index' failed (../lib/standard/collection/array.nit:258)
+Expected two numbers
diff --git a/tests/sav/base_import_standard.res b/tests/sav/base_import_standard.res
new file mode 100644 (file)
index 0000000..d00491f
--- /dev/null
@@ -0,0 +1 @@
+1
diff --git a/tests/sav/base_import_standard2.res b/tests/sav/base_import_standard2.res
new file mode 100644 (file)
index 0000000..0cfbf08
--- /dev/null
@@ -0,0 +1 @@
+2
index 04165c5..2240200 100644 (file)
@@ -4,5 +4,5 @@ B{A}ba
 C{B{A}}cba
 Ama
 A2ma
-B{A}nbma
-O{C{B{A}}}onmcba
+B{A}nmba
+O{C{B{A}}}oncmba
index bf88578..863486b 100644 (file)
@@ -1 +1 @@
-alt/base_init_inherit2_alt3.nit:75,7: Error: Explicit constructor required in N since multiple inheritance of constructor is forbiden. Conflicting classes are B, C. Costructors are base_init_inherit2_alt3::B::cb, base_init_inherit2_alt3::A::ca, base_init_inherit2_alt3::C::cc.
+Aa
index 9a3590f..d25bc4f 100644 (file)
@@ -2,6 +2,6 @@ Aa
 Ama
 B{A}ba
 Ana
-B{A}omba
-B{A}pbna
-B{A}qmbna
+B{A}obma
+B{A}pnba
+B{A}qnbma
index 3a08969..3aef777 100644 (file)
@@ -2,6 +2,6 @@ Aa
 Ama
 B{A}ba
 Ana
-B{A}omba
-B{A}pbna
-B{A}qombna
+B{A}obma
+B{A}pnba
+B{A}qonbma
index 77e587f..340bcfd 100644 (file)
@@ -2,6 +2,6 @@ Aa
 Ama
 B{A}ba
 Ana
-B{A}omba
-B{A}pbna
-B{A}qmpbna
+B{A}obma
+B{A}pnba
+B{A}qpnbma
index 7293394..486e174 100644 (file)
@@ -2,6 +2,6 @@ Aa
 Ama
 B{A}ba
 Ana
-B{A}omba
-B{A}pbna
-B{A}qompbna
+B{A}obma
+B{A}pnba
+B{A}qponbma
index c67526f..246f558 100644 (file)
@@ -2,5 +2,5 @@ A1a
 B2{A1}ba
 C3{B2{A1}}cba
 A1ma
-B2{A1}nbma
-O4{C3{B2{A1}}}onmcba
+B2{A1}nmba
+O4{C3{B2{A1}}}oncmba
index 893493f..a912b09 100644 (file)
@@ -1 +1,5 @@
-alt/base_init_inherit6_alt1.nit:32,10--14: Error: init is not a constructor in B.
+1
+2
+1
+3
+1
index 900503c..ce7a77e 100644 (file)
@@ -1 +1,5 @@
-alt/base_init_inherit6_alt2.nit:33,10--18: Error: foo is not a constructor in B.
+1
+2
+1
+3
+2
index a63698b..1243025 100644 (file)
@@ -1,11 +1,19 @@
-alt/base_init_inherit_alt1.nit:95,2--9: Error: ca is not a constructor in B.
-alt/base_init_inherit_alt1.nit:96,2--10: Error: ca2 is not a constructor in B.
-alt/base_init_inherit_alt1.nit:98,2--9: Error: ca is not a constructor in C.
-alt/base_init_inherit_alt1.nit:99,2--10: Error: ca2 is not a constructor in C.
-alt/base_init_inherit_alt1.nit:100,2--9: Error: cb is not a constructor in C.
-alt/base_init_inherit_alt1.nit:104,2--9: Error: ca is not a constructor in N.
-alt/base_init_inherit_alt1.nit:105,2--10: Error: ca2 is not a constructor in N.
-alt/base_init_inherit_alt1.nit:107,2--9: Error: ca is not a constructor in O.
-alt/base_init_inherit_alt1.nit:108,2--10: Error: ca2 is not a constructor in O.
-alt/base_init_inherit_alt1.nit:109,2--9: Error: cb is not a constructor in O.
-alt/base_init_inherit_alt1.nit:110,2--9: Error: cc is not a constructor in O.
+Aa
+A2a
+B{A}ba
+Aba
+A2ba
+C{B{A}}cba
+Acba
+A2cba
+B{A}cba
+Ama
+A2ma
+B{A}nmba
+Anmba
+A2nmba
+O{C{B{A}}}oncmba
+Aoncmba
+A2oncmba
+B{A}oncmba
+C{B{A}}oncmba
index 6f74ed3..b0e29f8 100644 (file)
@@ -1 +1 @@
-B1 A B2 C1 C2 D0 D1 D2 D3 D4 
+D1 C1 B1 A B2 C2 D2 
index c12605c..daa73b2 100644 (file)
@@ -3,6 +3,6 @@ A2x
 B1 A B2 
 A B3 
 A B4y 
-C1 A C2 
+C1 C2 
 A2z C3z 
-D1 B1 A B2 C1 C2 D2 D3 
+D1 B1 A B2 D2 D3 
index 1b64c0f..fc63fc7 100644 (file)
@@ -1 +1 @@
-alt/base_init_linext2_alt1.nit:100,1--5: Error: No contructor named initd in superclasses.
+alt/base_init_linext2_alt1.nit:100,1--5: Error: No super method to call for initd.
index c7c14d4..fe7551d 100644 (file)
@@ -1 +1,8 @@
-alt/base_init_linext2_alt2.nit:99,2--5: Error: there is no available compatible constructor in B. Discarded candidates are base_init_linext2_alt2::B::initb, base_init_linext2_alt2::B::init_par, base_init_linext2_alt2::B::init_par2(c: Char).
+A 
+A2x 
+B1 A B2 
+A B3 
+A B4y 
+C1 C2 
+A2z C3z 
+C1 C2 D1 D2 D3 
index 924b31d..7eadb94 100644 (file)
@@ -3,6 +3,6 @@ A2x
 B1 A B2 
 A B3 
 A B4y 
-C1 A C2 
+C1 C2 
 A2z C3z 
 D1 B1 A B2 D2 C1 C2 D3 
index 1bd61e3..9bc59e1 100644 (file)
@@ -1 +1,8 @@
-alt/base_init_linext2_alt4.nit:99,2--5: Error: there is no available compatible constructor in B. Discarded candidates are base_init_linext2_alt4::B::initb, base_init_linext2_alt4::B::init_par, base_init_linext2_alt4::B::init_par2(c: Char).
+A 
+A2x 
+B1 A B2 
+A B3 
+A B4y 
+C1 C2 
+A2z C3z 
+D1 C1 C2 B1 A B2 D2 D3 
index d04a73a..bc59762 100644 (file)
@@ -1 +1,8 @@
-alt/base_init_linext2_alt5.nit:109,1--5: Error: Only one super constructor invocation of class B is allowed.
+A 
+A2x 
+B1 A B2 
+A B3 
+A B4y 
+C1 C2 
+A2z C3z 
+D1 B1 A B2 D2 B1 A B2 D3 
index 90938b0..6d4b14b 100644 (file)
@@ -1,5 +1,5 @@
 A 
 B1 A B2 
 C1 A C2 
-B1 A B2 C1 C2 D0 D1 D2 D3 D4 
-E1 A E2 B1 B2 E3 C1 C2 E4 
+D1 C1 B1 A B2 C2 D2 
+E1 D1 C1 B1 A B2 C2 D2 E2 D1 C1 B1 A B2 C2 D2 E3 D1 C1 B1 A B2 C2 D2 E4 
diff --git a/tests/sav/base_init_linext_alt1.res b/tests/sav/base_init_linext_alt1.res
deleted file mode 100644 (file)
index 04e3275..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A B1 B2 C1 C2 D1 D2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt10.res b/tests/sav/base_init_linext_alt10.res
deleted file mode 100644 (file)
index 683e93c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 D1 B1 A B2 D2 D3 E1 E2 E3 C1 C2 E4 D4 
diff --git a/tests/sav/base_init_linext_alt11.res b/tests/sav/base_init_linext_alt11.res
deleted file mode 100644 (file)
index edef552..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A D1 B1 B2 D2 D3 E1 E2 E3 C1 C2 E4 D4 
diff --git a/tests/sav/base_init_linext_alt12.res b/tests/sav/base_init_linext_alt12.res
deleted file mode 100644 (file)
index a92a06b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 D1 D2 C1 A C2 D3 E1 E2 B1 B2 E3 E4 D4 
diff --git a/tests/sav/base_init_linext_alt13.res b/tests/sav/base_init_linext_alt13.res
deleted file mode 100644 (file)
index cea7bbd..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A D1 D2 C1 C2 D3 E1 E2 B1 B2 E3 E4 D4 
diff --git a/tests/sav/base_init_linext_alt14.res b/tests/sav/base_init_linext_alt14.res
deleted file mode 100644 (file)
index ce9cfc0..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 D1 B1 A B2 D2 C1 C2 D3 E1 E2 E3 E4 D4 
diff --git a/tests/sav/base_init_linext_alt15.res b/tests/sav/base_init_linext_alt15.res
deleted file mode 100644 (file)
index d48d0e2..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A D1 B1 B2 D2 C1 C2 D3 E1 E2 E3 E4 D4 
diff --git a/tests/sav/base_init_linext_alt2.res b/tests/sav/base_init_linext_alt2.res
deleted file mode 100644 (file)
index 0d383df..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 D1 B1 A B2 C1 C2 D2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt3.res b/tests/sav/base_init_linext_alt3.res
deleted file mode 100644 (file)
index 539f8ad..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A D1 B1 B2 C1 C2 D2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt4.res b/tests/sav/base_init_linext_alt4.res
deleted file mode 100644 (file)
index 2aa8b4e..0000000
+++ /dev/null
@@ -1 +0,0 @@
-B1 A B2 D0 D1 D2 C1 C2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt5.res b/tests/sav/base_init_linext_alt5.res
deleted file mode 100644 (file)
index 7883af4..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A B1 B2 D1 D2 C1 C2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt6.res b/tests/sav/base_init_linext_alt6.res
deleted file mode 100644 (file)
index ce5e04a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 D1 B1 A B2 D2 C1 C2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt7.res b/tests/sav/base_init_linext_alt7.res
deleted file mode 100644 (file)
index c075e86..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A D1 B1 B2 D2 C1 C2 D3 D4 
diff --git a/tests/sav/base_init_linext_alt8.res b/tests/sav/base_init_linext_alt8.res
deleted file mode 100644 (file)
index 305d19c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 D1 D2 D3 E1 A E2 B1 B2 E3 C1 C2 E4 D4 
diff --git a/tests/sav/base_init_linext_alt9.res b/tests/sav/base_init_linext_alt9.res
deleted file mode 100644 (file)
index 322c6ab..0000000
+++ /dev/null
@@ -1 +0,0 @@
-D0 A D1 D2 D3 E1 E2 B1 B2 E3 C1 C2 E4 D4 
index 2d4d642..27671d4 100644 (file)
@@ -1,4 +1,4 @@
 A 
 b1 B1 A B2 b2 
 c1 C1 A C2 c2 
-d1 b1 B1 A B2 b2 c1 C1 C2 c2 D0 D1 D2 D3 D4 d2 
+d1 D1 c1 C1 b1 B1 A B2 b2 C2 c2 D2 d2 
index 6a6e994..01e79c3 100644 (file)
@@ -1 +1,3 @@
-Property inheritance conflict in class D for `toto': conflicting properties are defined in B, C
+1
+2
+3
diff --git a/tests/sav/error_prop_loc_alt1.res b/tests/sav/error_prop_loc_alt1.res
new file mode 100644 (file)
index 0000000..ca9822e
--- /dev/null
@@ -0,0 +1,5 @@
+alt/error_prop_loc_alt1.nit:38,1--12: Warning: conflicting property definitions for property toto in D: error_prop_loc_alt1#B#toto error_prop_loc_alt1#C#toto
+1
+2
+3
+3
index 18f8f3d..e69de29 100644 (file)
@@ -1 +0,0 @@
-alt/error_redef_1alt4_alt5.nit:30,12--13: Redef error: The procedure B::f1 redefines the function A::f1.
index da892e9..e69de29 100644 (file)
@@ -1 +0,0 @@
-alt/error_redef_1alt4_alt9.nit:34,15: Syntax error: untyped parameter.
diff --git a/tests/sav/fixme/error_prop_loc.res b/tests/sav/fixme/error_prop_loc.res
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/sav/fixme/error_redef_1alt4_alt5.res b/tests/sav/fixme/error_redef_1alt4_alt5.res
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/sav/fixme/error_redef_1alt4_alt9.res b/tests/sav/fixme/error_redef_1alt4_alt9.res
deleted file mode 100644 (file)
index e69de29..0000000
diff --git a/tests/sav/fixme/test_inheritance_raf.res b/tests/sav/fixme/test_inheritance_raf.res
deleted file mode 100644 (file)
index dd8e78f..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-1
-1
-5
-5
-1
-2
-5
-2
-1
-1
-3
-7
-1
-2
-3
-4
diff --git a/tests/sav/nitc/base_for_nullable.res b/tests/sav/nitc/base_for_nullable.res
deleted file mode 100644 (file)
index a75893d..0000000
+++ /dev/null
@@ -1 +0,0 @@
-base_for_nullable.nit:21,10--13: Type error: 'for' on a nullable expression.
diff --git a/tests/sav/nitc/base_for_nullable_alt1.res b/tests/sav/nitc/base_for_nullable_alt1.res
deleted file mode 100644 (file)
index 1baed81..0000000
+++ /dev/null
@@ -1 +0,0 @@
-alt/base_for_nullable_alt1.nit:21,10--13: Type error: 'for' on a nullable expression.
diff --git a/tests/sav/nitc/base_for_nullable_alt2.res b/tests/sav/nitc/base_for_nullable_alt2.res
deleted file mode 100644 (file)
index 14de0ee..0000000
+++ /dev/null
@@ -1 +0,0 @@
-alt/base_for_nullable_alt2.nit:21,10--13: Type error: 'for' on a nullable expression.
index 37e9ff8..980ce17 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:396)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:411)
 11
 21
 31
index 37e9ff8..980ce17 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:396)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:411)
 11
 21
 31
index 37e9ff8..980ce17 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:396)
+Runtime error: Cast failed. Expected `OTHER`, got `Float` (../lib/standard/kernel.nit:411)
 11
 21
 31
diff --git a/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt1.res b/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt1.res
deleted file mode 100644 (file)
index c508d53..0000000
+++ /dev/null
@@ -1 +0,0 @@
-false
diff --git a/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt2.res b/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt2.res
deleted file mode 100644 (file)
index f76dd23..0000000
Binary files a/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt2.res and /dev/null differ
diff --git a/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt3.res b/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt3.res
deleted file mode 100644 (file)
index 573541a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt4.res b/tests/sav/nitg-g/fixme/base_attr_init_val_int_alt4.res
deleted file mode 100644 (file)
index 945da8f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0.000000
diff --git a/tests/sav/nitg-g/fixme/base_attr_init_val_raf_alt1.res b/tests/sav/nitg-g/fixme/base_attr_init_val_raf_alt1.res
deleted file mode 100644 (file)
index 2f1465d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-1
-1
-0
diff --git a/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt1.res b/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt1.res
deleted file mode 100644 (file)
index 5625f1f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-0
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt2.res b/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt2.res
deleted file mode 100644 (file)
index 2e8046c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-1
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt3.res b/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt3.res
deleted file mode 100644 (file)
index f0c5f8f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-0
-0
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt4.res b/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt4.res
deleted file mode 100644 (file)
index a12c8e0..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-10
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt5.res b/tests/sav/nitg-g/fixme/base_attr_nullable_int_alt5.res
deleted file mode 100644 (file)
index f343f0e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-10
-20
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-g/fixme/base_covar_int2_alt3.res b/tests/sav/nitg-g/fixme/base_covar_int2_alt3.res
deleted file mode 100644 (file)
index fbffc32..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-1
-1
-1
-aa
diff --git a/tests/sav/nitg-g/fixme/base_covar_int2_alt5.res b/tests/sav/nitg-g/fixme/base_covar_int2_alt5.res
deleted file mode 100644 (file)
index 5f6d34c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-1
-1
-1
-1
-a
diff --git a/tests/sav/nitg-g/fixme/base_covar_int_alt3.res b/tests/sav/nitg-g/fixme/base_covar_int_alt3.res
deleted file mode 100644 (file)
index d3413ee..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-true
-true
-true
-false
-true
diff --git a/tests/sav/nitg-g/fixme/base_for_finish.res b/tests/sav/nitg-g/fixme/base_for_finish.res
deleted file mode 100644 (file)
index 390c4e4..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-1
-2
-3
-0
-
-1
-100
-2
-0
-
-1
-10
-20
-100
-2
-10
diff --git a/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt1.res b/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt1.res
deleted file mode 100644 (file)
index c508d53..0000000
+++ /dev/null
@@ -1 +0,0 @@
-false
diff --git a/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt2.res b/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt2.res
deleted file mode 100644 (file)
index f76dd23..0000000
Binary files a/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt2.res and /dev/null differ
diff --git a/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt3.res b/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt3.res
deleted file mode 100644 (file)
index 573541a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt4.res b/tests/sav/nitg-s/fixme/base_attr_init_val_int_alt4.res
deleted file mode 100644 (file)
index 945da8f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0.000000
diff --git a/tests/sav/nitg-s/fixme/base_attr_init_val_raf_alt1.res b/tests/sav/nitg-s/fixme/base_attr_init_val_raf_alt1.res
deleted file mode 100644 (file)
index 2f1465d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-1
-1
-0
diff --git a/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt1.res b/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt1.res
deleted file mode 100644 (file)
index 5625f1f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-0
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt2.res b/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt2.res
deleted file mode 100644 (file)
index 2e8046c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-1
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt3.res b/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt3.res
deleted file mode 100644 (file)
index f0c5f8f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-0
-0
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt4.res b/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt4.res
deleted file mode 100644 (file)
index a12c8e0..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-10
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt5.res b/tests/sav/nitg-s/fixme/base_attr_nullable_int_alt5.res
deleted file mode 100644 (file)
index f343f0e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-10
-20
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-s/fixme/base_covar_int2_alt3.res b/tests/sav/nitg-s/fixme/base_covar_int2_alt3.res
deleted file mode 100644 (file)
index fbffc32..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-1
-1
-1
-aa
diff --git a/tests/sav/nitg-s/fixme/base_covar_int2_alt5.res b/tests/sav/nitg-s/fixme/base_covar_int2_alt5.res
deleted file mode 100644 (file)
index 5f6d34c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-1
-1
-1
-1
-a
diff --git a/tests/sav/nitg-s/fixme/base_covar_int_alt3.res b/tests/sav/nitg-s/fixme/base_covar_int_alt3.res
deleted file mode 100644 (file)
index d3413ee..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-true
-true
-true
-false
-true
diff --git a/tests/sav/nitg-s/fixme/base_for_finish.res b/tests/sav/nitg-s/fixme/base_for_finish.res
deleted file mode 100644 (file)
index 390c4e4..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-1
-2
-3
-0
-
-1
-100
-2
-0
-
-1
-10
-20
-100
-2
-10
diff --git a/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt1.res b/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt1.res
deleted file mode 100644 (file)
index c508d53..0000000
+++ /dev/null
@@ -1 +0,0 @@
-false
diff --git a/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt2.res b/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt2.res
deleted file mode 100644 (file)
index f76dd23..0000000
Binary files a/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt2.res and /dev/null differ
diff --git a/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt3.res b/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt3.res
deleted file mode 100644 (file)
index 573541a..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0
diff --git a/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt4.res b/tests/sav/nitg-sg/fixme/base_attr_init_val_int_alt4.res
deleted file mode 100644 (file)
index 945da8f..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0.000000
diff --git a/tests/sav/nitg-sg/fixme/base_attr_init_val_raf_alt1.res b/tests/sav/nitg-sg/fixme/base_attr_init_val_raf_alt1.res
deleted file mode 100644 (file)
index 2f1465d..0000000
+++ /dev/null
@@ -1,3 +0,0 @@
-1
-1
-0
diff --git a/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt1.res b/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt1.res
deleted file mode 100644 (file)
index 5625f1f..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-0
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt2.res b/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt2.res
deleted file mode 100644 (file)
index 2e8046c..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-1
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt3.res b/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt3.res
deleted file mode 100644 (file)
index f0c5f8f..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-0
-0
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt4.res b/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt4.res
deleted file mode 100644 (file)
index a12c8e0..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-10
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt5.res b/tests/sav/nitg-sg/fixme/base_attr_nullable_int_alt5.res
deleted file mode 100644 (file)
index f343f0e..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-10
-20
-0
-1
-2
-10
-20
-30
diff --git a/tests/sav/nitg-sg/fixme/base_covar_int2_alt3.res b/tests/sav/nitg-sg/fixme/base_covar_int2_alt3.res
deleted file mode 100644 (file)
index fbffc32..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-1
-1
-1
-aa
diff --git a/tests/sav/nitg-sg/fixme/base_covar_int2_alt5.res b/tests/sav/nitg-sg/fixme/base_covar_int2_alt5.res
deleted file mode 100644 (file)
index 5f6d34c..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-1
-1
-1
-1
-a
diff --git a/tests/sav/nitg-sg/fixme/base_covar_int_alt3.res b/tests/sav/nitg-sg/fixme/base_covar_int_alt3.res
deleted file mode 100644 (file)
index d3413ee..0000000
+++ /dev/null
@@ -1,5 +0,0 @@
-true
-true
-true
-false
-true
diff --git a/tests/sav/nitg-sg/fixme/base_for_finish.res b/tests/sav/nitg-sg/fixme/base_for_finish.res
deleted file mode 100644 (file)
index 390c4e4..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-1
-2
-3
-0
-
-1
-100
-2
-0
-
-1
-10
-20
-100
-2
-10
diff --git a/tests/sav/nitg-sg/fixme/test_platform_android.res b/tests/sav/nitg-sg/fixme/test_platform_android.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
index 2ebbe38..e95605c 100644 (file)
@@ -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
index af28f14..e248df6 100644 (file)
@@ -41,7 +41,7 @@ Bool [
 Object -> Bool [dir=back arrowtail=open style=dashed];
 
 Float [
- label = "{Float||}"
+ label = "{Float||+ is_approx(other: Float, precision: Float): Bool\l}"
 ]
 Numeric -> Float [dir=back arrowtail=open style=dashed];
 
index 6189897..9a7b30f 100644 (file)
@@ -41,7 +41,7 @@ Bool [
 Object -> Bool [dir=back arrowtail=open style=dashed];
 
 Float [
- label = "{Float||}"
+ label = "{Float||+ is_approx(other: Float, precision: Float): Bool\l}"
 ]
 Numeric -> Float [dir=back arrowtail=open style=dashed];
 
index 3a07d20..12b7181 100644 (file)
@@ -1,6 +1,6 @@
-test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit2.nit): Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
+test_nitunit.nit:20,1--22,0: ERROR: nitunit.test_nitunit.test_nitunit::X.<class> (in .nitunit/test_nitunit-2.nit): Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
 
-test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit3.nit): .nitunit/test_nitunit3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
+test_nitunit.nit:23,2--25,0: FAILURE: nitunit.test_nitunit.test_nitunit::X.test_nitunit::X::foo (in .nitunit/test_nitunit-3.nit): .nitunit/test_nitunit-3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
 
 test_test_nitunit.nit:36,2--40,4: ERROR: test_foo1 (in file .nitunit/test_test_nitunit_TestX_test_foo1.nit): Runtime error: Assert failed (test_test_nitunit.nit:39)
 
@@ -11,8 +11,8 @@ TestSuites:
 Class suites: 1; Test Cases: 3; Failures: 1
 <testsuites><testsuite package="test_nitunit"><testcase classname="nitunit.test_nitunit.&lt;module&gt;" name="&lt;module&gt;"><system-err></system-err><system-out>assert true
 </system-out></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="&lt;class&gt;"><system-err></system-err><system-out>assert false
-</system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit2.nit:5)
+</system-out><error message="Runtime error: Assert failed (.nitunit/test_nitunit-2.nit:5)
 "></error></testcase><testcase classname="nitunit.test_nitunit.test_nitunit::X" name="test_nitunit::X::foo"><system-err></system-err><system-out>assert undefined_identifier
-</system-out><failure message=".nitunit/test_nitunit3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
+</system-out><failure message=".nitunit/test_nitunit-3.nit:5,8--27: Error: Method or variable 'undefined_identifier' unknown in Sys.
 "></failure></testcase></testsuite><testsuite package="test_test_nitunit"><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo"><system-err></system-err><system-out>out</system-out></testcase><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo1"><system-err></system-err><system-out>out</system-out><error message="Runtime error: Assert failed (test_test_nitunit.nit:39)
 "></error></testcase><testcase classname="nitunit.test_test_nitunit.test_test_nitunit::TestX" name="test_test_nitunit::TestX::test_foo2"><system-err></system-err><system-out>out</system-out></testcase></testsuite></testsuites>
\ No newline at end of file
diff --git a/tests/sav/nitunit_args4.res b/tests/sav/nitunit_args4.res
new file mode 100644 (file)
index 0000000..faaa0c9
--- /dev/null
@@ -0,0 +1,21 @@
+DocUnits:
+DocUnits Success
+Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0
+
+TestSuites:
+No test cases found
+Class suites: 0; Test Cases: 0; Failures: 0
+<testsuites><testsuite package="test_nitunit2"><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::foo1"><system-err></system-err><system-out>if true then
+
+   assert true
+
+end
+</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::bar2"><system-err></system-err><system-out>if true then
+
+    assert true
+
+end
+</system-out></testcase><testcase classname="nitunit.test_nitunit2.standard::kernel::Object" name="test_nitunit2::Object::foo3"><system-err></system-err><system-out>var a = 1
+assert a == 1
+assert a == 1
+</system-out></testcase></testsuite><testsuite></testsuite></testsuites>
\ No newline at end of file
index a007f82..dd8e78f 100644 (file)
@@ -1 +1,16 @@
-Property inheritance conflict in class B for `i': conflicting properties are defined in B, A
+1
+1
+5
+5
+1
+2
+5
+2
+1
+1
+3
+7
+1
+2
+3
+4
index 0a122d4..22ab94a 100644 (file)
Binary files a/tests/sav/test_json_static.res and b/tests/sav/test_json_static.res differ
index bf79e4b..ba6c5fe 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:789)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:782)
 NativeString
 N
 Nit
index ee239cc..c4e3593 100755 (executable)
@@ -15,7 +15,7 @@
 
 stop=false
 while [ "$stop" = false ]; do
-       case $1 in
+       case "$1" in
                -v) verbose=true; shift;;
                *) stop=true
        esac
@@ -42,29 +42,31 @@ for f in "$@"; do
                                echo "$f"
                                continue
                        fi
-                       b=`basename "$f" .nit`
+                       b=`basename -- "$f" .nit`
                        ;;
                *.res)
-                       b=`basename "$f" .res`
+                       b=`basename -- "$f" .res`
                        ;;
                *)
-                       b=`basename "$f"`
+                       b=`basename -- "$f"`
                        ;;
        esac
+       # remove bad chars
+       c=`echo "$b" | sed 's/\([\\.*^$]\|\[\|]\)/./g'`
        # Remove alts of args test variations
-       c=`echo "$b" | sed 's/\(_[0-9]*alt[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/g;s/\(_args[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/'`
+       c=`echo "$c" | sed 's/\(_[0-9]*alt[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/g;s/\(_args[0-9][0-9]*\)/\\\\(\1\\\\)\\\\?/'`
        b=`echo "$b" | sed 's/_[0-9]*alt[0-9][0-9]*//g;s/_args[0-9][0-9]*//'`
        # Search the orig nit file in the list
-       cat listfull.out | grep "\b$c.nit" || {
+       cat listfull.out | grep -- "\(^\|/\)$c.nit" || {
                res=1
                echo >&2 "No test $b.nit found for $f"
                test "$verbose" == "true" || continue
                # Search the nit file outside the list...
-               find ../../nit* -name $b.nit >&2
+               find ../../nit* -name "$b.nit" >&2
                # Search the nit file in the git history...
-               git log -1 -- $b.nit >&2
+               git log -1 -- "$b.nit" >&2
                # Search the orig file in the git history...
-               git log -1 -- $f >&2
+               git log -1 -- "$f" >&2
        }
 done
 
index 642abeb..19ca640 100644 (file)
@@ -27,11 +27,11 @@ extern class JavaArrayList in "Java" `{ java.util.ArrayList `}
                return new ArrayList();
        `}
 
-       fun add(o: Int) in "Java" `{ recv.add(o); `}
+       fun add(o: JavaString) in "Java" `{ recv.add(o); `}
 
        redef fun output in "Java" `{
                for (Object i: recv) {
-                       System.out.println((long)i);
+                       System.out.println((String)i);
                }
        `}
 
@@ -41,12 +41,12 @@ extern class JavaArrayList in "Java" `{ java.util.ArrayList `}
 end
 
 var ll = new JavaArrayList
-ll.add(1)
-ll.add(2)
-ll.add(1)
-ll.add(3)
-ll.add(20)
-ll.add(10)
+ll.add "1".to_java_string
+ll.add "2".to_java_string
+ll.add "1".to_java_string
+ll.add "3".to_java_string
+ll.add "20".to_java_string
+ll.add "10".to_java_string
 ll.output
 ll.sort
 ll.output
index 0d15f98..019f202 100644 (file)
@@ -28,7 +28,7 @@ class Toto[E]
 end
 
 class TestNative
-       super ArrayCapable[Int]
+
 
 init
 do
@@ -39,7 +39,7 @@ do
        a[1] = 2
        print(a[0])
        print(a[1])
-       b = calloc_array(5)
+       b = new NativeArray[Int](5)
        b[0]=200
        b[1]=300
        print(b[0])
index b510725..db6b1cf 100644 (file)
@@ -1,6 +1,7 @@
 # This file is part of NIT ( http://www.nitlanguage.org ).
 #
 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+# Copyright 2014 Jean-Christophe Beaupré <jcbrinfo@users.noreply.github.com>
 #
 # 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 "<null>"}"
 end
index aac7a83..467fa78 100644 (file)
 # limitations under the License.
 
 class Toto
-       super ArrayCapable[Int]
+
 
        fun toto
        do
-               var a = calloc_array(3)
+               var a = new NativeArray[Int](3)
                a[0] = 10
                a[1] = 20
                a[2] = 30
index f8cbd4b..7ee824c 100644 (file)
@@ -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
index e6ac890..7455557 100644 (file)
@@ -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
diff --git a/tests/test_nitunit2.nit b/tests/test_nitunit2.nit
new file mode 100644 (file)
index 0000000..899ff94
--- /dev/null
@@ -0,0 +1,43 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# 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.
+
+# a fence unit
+#
+# ~~~~
+# if true then
+#
+#    assert true
+#
+# end
+# ~~~~
+fun foo1 do end
+
+# a block unit
+#
+#    if true then
+#
+#        assert true
+#
+#    end
+fun bar2 do end
+
+# a context continuation
+#
+#    var a = 1
+#    assert a == 1
+#
+# bla bla
+#
+#    assert a == 1
+fun foo3 do end
index 76ecf2c..8720ca3 100755 (executable)
@@ -27,13 +27,13 @@ unset NIT_DIR
 
 # Get the first Java lib available
 shopt -s nullglob
-paths=`echo /usr/lib/jvm/*/`
-paths=($paths) 
-JAVA_HOME=${paths[0]}
+JAVA_HOME=$(dirname $(dirname $(readlink -f $(which javac))))
 
 paths=`echo $JAVA_HOME/jre/lib/*/{client,server}/`
 paths=($paths) 
 JNI_LIB_PATH=${paths[0]}
+echo $JAVA_HOME
+echo $JNI_LIB_PATH
 shopt -u nullglob
 
 outdir="out"
@@ -65,7 +65,7 @@ saferun()
        local o=
        local a=
        while [ $stop = false ]; do
-               case $1 in
+               case "$1" in
                        -o) o="$2"; shift; shift;;
                        -a) a="-a"; shift;;
                        *) stop=true
@@ -129,8 +129,8 @@ function compare_to_result()
        local pattern="$1"
        local sav="$2"
        if [ ! -r "$sav" ]; then return 0; fi
-       test "`cat "$sav"`" = "UNDEFINED" && return 1
-       diff -u "$sav" "$outdir/$pattern.res" > "$outdir/$pattern.diff.sav.log"
+       test "`cat -- "$sav"`" = "UNDEFINED" && return 1
+       diff -u -- "$sav" "$outdir/$pattern.res" > "$outdir/$pattern.diff.sav.log"
        if [ "$?" == 0 ]; then
                return 1
        fi
@@ -146,6 +146,13 @@ function compare_to_result()
        fi
 }
 
+function xmlesc()
+{
+       sed 's/&/\&amp;/g; s/</\&lt;/g; s/>/\&gt;/g; s/"/\&quot;/g; s/'"'"'/\&#39;/g'<<EOF
+$*
+EOF
+}
+
 # As argument: the pattern used for the file
 function process_result()
 {
@@ -164,7 +171,7 @@ function process_result()
        OLD=""
        LIST=""
        FIRST=""
-       echo >>$xml "<testcase classname='$pack' name='$description' time='`cat $outdir/$pattern.time.out`' `timestamp`>"
+       echo >>$xml "<testcase classname='`xmlesc "$pack"`' name='`xmlesc "$description"`' time='`cat -- "$outdir/$pattern.time.out"`' `timestamp`>"
        #for sav in "sav/$engine/fixme/$pattern.res" "sav/$engine/$pattern.res" "sav/fixme/$pattern.res" "sav/$pattern.res" "sav/$pattern.sav"; do
        for savdir in $savdirs; do
                sav=$savdir/fixme/$pattern.res
@@ -225,7 +232,7 @@ function process_result()
        if [ -n "$SAV" ]; then
                if [ -n "$OLD" ]; then
                        echo "[*ok*] $outdir/$pattern.res $SAV - but $OLD remains!"
-                       echo >>$xml "<error message='ok $outdir/$pattern.res - but $OLD remains'/>"
+                       echo >>$xml "<error message='`xmlesc "ok $outdir/$pattern.res - but $OLD remains"`'/>"
                        remains="$remains $OLD"
                else
                        echo "[ok] $outdir/$pattern.res $SAV"
@@ -234,7 +241,7 @@ function process_result()
        elif [ -n "$FIXME" ]; then
                if [ -n "$OLD" ]; then
                        echo "[*fixme*] $outdir/$pattern.res $FIXME - but $OLD remains!"
-                       echo >>$xml "<error message='ok $outdir/$pattern.res - but $OLD remains'/>"
+                       echo >>$xml "<error message='`xmlesc "ok $outdir/$pattern.res - but $OLD remains"`'/>"
                        remains="$remains $OLD"
                else
                        echo "[fixme] $outdir/$pattern.res $FIXME"
@@ -247,41 +254,41 @@ function process_result()
                todos="$todos $pattern"
        elif [ -n "$SOSO" ]; then
                echo "[======= soso $outdir/$pattern.res $SOSO =======]"
-               echo >>$xml "<error message='soso $outdir/$pattern.res $SOSO'/>"
+               echo >>$xml "<error message='`xmlesc "soso $outdir/$pattern.res $SOSO"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$SOSOF" ]; then
                echo "[======= fixme soso $outdir/$pattern.res $SOSOF =======]"
-               echo >>$xml "<error message='soso $outdir/$pattern.res $SOSO'/>"
+               echo >>$xml "<error message='`xmlesc "soso $outdir/$pattern.res $SOSO"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v  -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$NSAV" ]; then
                echo "[======= fail $outdir/$pattern.res $NSAV =======]"
-               echo >>$xml "<error message='fail $outdir/$pattern.res $NSAV'/>"
+               echo >>$xml "<error message='`xmlesc "fail $outdir/$pattern.res $NSAV"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
        elif [ -n "$NFIXME" ]; then
                echo "[======= changed $outdir/$pattern.res $NFIXME ======]"
-               echo >>$xml "<error message='changed $outdir/$pattern.res $NFIXME'/>"
+               echo >>$xml "<error message='`xmlesc "changed $outdir/$pattern.res $NFIXME"`'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v $outdir/$pattern.diff.sav.log | head >>$xml -n 50
+               cat -v -- "$outdir/$pattern.diff.sav.log" | head >>$xml -n 50
                echo >>$xml "]]></system-out>"
                nok="$nok $pattern"
                echo "$ii" >> "$ERRLIST"
-       elif [ -s $outdir/$pattern.res ]; then
+       elif [ -s "$outdir/$pattern.res" ]; then
                echo "[=== no sav ===] $outdir/$pattern.res is not empty"
                echo >>$xml "<error message='no sav and not empty'/>"
                echo >>$xml "<system-out><![CDATA["
-               cat -v >>$xml $outdir/$pattern.res
+               cat -v >>$xml -- "$outdir/$pattern.res"
                echo >>$xml "]]></system-out>"
                nos="$nos $pattern"
                echo "$ii" >> "$ERRLIST"
@@ -290,9 +297,9 @@ function process_result()
                echo "[0k] $outdir/$pattern.res is empty"
                ok="$ok $pattern"
        fi
-       if test -s $outdir/$pattern.cmp.err; then
+       if test -s "$outdir/$pattern.cmp.err"; then
                echo >>$xml "<system-err><![CDATA["
-               cat -v >>$xml $outdir/$pattern.cmp.err
+               cat -v >>$xml -- "$outdir/$pattern.cmp.err"
                echo >>$xml "]]></system-err>"
        fi
        echo >>$xml "</testcase>"
@@ -303,12 +310,12 @@ need_skip()
        test "$noskip" = true && return 1
        if echo "$1" | grep -f "$engine.skip" >/dev/null 2>&1; then
                echo "=> $2: [skip]"
-               echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
+               echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
                return 0
        fi
        if test -n "$isinterpret" && echo "$1" | grep -f "exec.skip" >/dev/null 2>&1; then
                echo "=> $2: [skip exec]"
-               echo >>$xml "<testcase classname='$3' name='$2' `timestamp`><skipped/></testcase>"
+               echo >>$xml "<testcase classname='`xmlesc "$3"`' name='`xmlesc "$2"`' `timestamp`><skipped/></testcase>"
                return 0
        fi
        return 1
@@ -373,22 +380,27 @@ case $engine in
                engine=nitg-s;
                enginebinname=nitg;
                OPT="--separate $OPT --compile-dir $compdir"
+               savdirs="sav/nitg-common/"
                ;;
        nitg-s)
                enginebinname=nitg;
                OPT="--separate $OPT --compile-dir $compdir"
+               savdirs="sav/nitg-common/"
                ;;
        nitg-e)
                enginebinname=nitg;
                OPT="--erasure $OPT --compile-dir $compdir"
+               savdirs="sav/nitg-common/"
                ;;
        nitg-sg)
                enginebinname=nitg;
                OPT="--semi-global $OPT --compile-dir $compdir"
+               savdirs="sav/nitg-common/"
                ;;
        nitg-g)
                enginebinname=nitg;
                OPT="--global $OPT --compile-dir $compdir"
+               savdirs="sav/nitg-common/"
                ;;
        nit)
                engine=niti
@@ -465,16 +477,16 @@ fi
 echo >$xml "<testsuites><testsuite>"
 
 for ii in "$@"; do
-       if [ ! -f $ii ]; then
+       if [ ! -f "$ii" ]; then
                echo "File '$ii' does not exist."
                continue
        fi
-       f=`basename "$ii" .nit`
+       f=`basename -- "$ii" .nit`
 
        pack="tests.${engine}".`echo $ii | perl -p -e 's|^../([^/]*)/([a-zA-Z_]*).*|\1.\2| || s|^([a-zA-Z]*)[^_]*_([a-zA-Z]*).*|\1.\2| || s|\W*([a-zA-Z_]*).*|\1|'`
 
        # Sould we skip the file for this engine?
-       need_skip $f $f $pack && continue
+       need_skip "$f" "$f" "$pack" && continue
 
        tmp=${ii/../AA}
        if [ "x$tmp" = "x$ii" ]; then
@@ -483,12 +495,12 @@ for ii in "$@"; do
                includes="-I alt"
        fi
 
-       for i in "$ii" `./alterner.pl --start '#' --altsep '_' $ii`; do
-               bf=`basename $i .nit`
+       for i in "$ii" `./alterner.pl --start '#' --altsep '_' -- "$ii"`; do
+               bf=`basename -- "$i" .nit`
                ff="$outdir/$bf"
 
                # Sould we skip the alternative for this engine?
-               need_skip $bf $bf $pack && continue
+               need_skip "$bf" "$bf" "$pack" && continue
 
                echo -n "=> $bf: "
 
@@ -507,7 +519,7 @@ for ii in "$@"; do
 
                if [ -n "$isinterpret" ]; then
                        cat > "$ff.bin" <<END
-exec $NITC --no-color $OPT $includes -- "$i" "\$@"
+exec $NITC --no-color $OPT $includes -- $(printf '%q' "$i") "\$@"
 END
                        chmod +x "$ff.bin"
                        > "$ff.cmp.err"
@@ -529,8 +541,8 @@ END
                                saferun -o "$ff.time.out" $NITC --no-color $OPT -o "$ffout" $includes $nocc "$i" 2> "$ff.cmp.err" > "$ff.compile.log"
                        ERR=$?
                        if [ "x$verbose" = "xtrue" ]; then
-                               cat "$ff.compile.log"
-                               cat >&2 "$ff.cmp.err"
+                               cat -- "$ff.compile.log"
+                               cat >&2 -- "$ff.cmp.err"
                        fi
                fi
                if [ "$engine" = "emscripten" ]; then
@@ -538,24 +550,24 @@ END
                        chmod +x "$ff.bin"
                        if grep "Fatal Error: more than one primitive class" "$ff.compile.log" > /dev/null; then
                                echo " [skip] do no not imports kernel"
-                               echo >>$xml "<testcase classname='$pack' name='$bf' `timestamp`><skipped/></testcase>"
+                               echo >>$xml "<testcase classname='`xmlesc "$pack"`' name='`xmlesc "$bf"`' `timestamp`><skipped/></testcase>"
                                continue
                        fi
                fi
                if [ "$ERR" != 0 ]; then
                        echo -n "! "
-                       cat "$ff.compile.log" "$ff.cmp.err" > "$ff.res"
-                       process_result $bf $bf $pack
+                       cat -- "$ff.compile.log" "$ff.cmp.err" > "$ff.res"
+                       process_result "$bf" "$bf" "$pack"
                elif [ -n "$nocc" ]; then
                        # not compiled
                        echo -n "nocc "
                        > "$ff.res"
-                       process_result $bf $bf $pack
+                       process_result "$bf" "$bf" "$pack"
                elif [ -x "$ff.bin" ]; then
                        if skip_exec "$bf"; then
                                # No exec
                                > "$ff.res"
-                               process_result $bf $bf $pack
+                               process_result "$bf" "$bf" "$pack"
                                break
                        fi
                        echo -n ". "
@@ -567,21 +579,21 @@ END
                        fi
                        NIT_NO_STACK=1 LD_LIBRARY_PATH=$JNI_LIB_PATH \
                                saferun -a -o "$ff.time.out" "$ff.bin" $args < "$inputs" > "$ff.res" 2>"$ff.err"
-                       mv $ff.time.out $ff.times.out
-                       awk '{ SUM += $1} END { print SUM }' $ff.times.out > $ff.time.out
+                       mv "$ff.time.out" "$ff.times.out"
+                       awk '{ SUM += $1} END { print SUM }' "$ff.times.out" > "$ff.time.out"
 
                        if [ "x$verbose" = "xtrue" ]; then
-                               cat "$ff.res"
-                               cat >&2 "$ff.err"
+                               cat -- "$ff.res"
+                               cat >&2 -- "$ff.err"
                        fi
                        if [ -f "$ff.write" ]; then
-                               cat "$ff.write" >> "$ff.res"
+                               cat -- "$ff.write" >> "$ff.res"
                        elif [ -d "$ff.write" ]; then
-                               LANG=C /bin/ls -F $ff.write >> "$ff.res"
+                               LANG=C /bin/ls -F "$ff.write" >> "$ff.res"
                        fi
-                       cp "$ff.res"  "$ff.res2"
-                       cat "$ff.cmp.err" "$ff.err" "$ff.res2" > "$ff.res"
-                       process_result $bf $bf $pack
+                       cp -- "$ff.res"  "$ff.res2"
+                       cat -- "$ff.cmp.err" "$ff.err" "$ff.res2" > "$ff.res"
+                       process_result "$bf" "$bf" "$pack"
 
                        if [ -f "$f.args" ]; then
                                fargs=$f.args
@@ -594,13 +606,13 @@ END
                                        name="$bf args $cptr"
 
                                        # Sould we skip the input for this engine?
-                                       need_skip $bff "  $name" $pack && continue
+                                       need_skip "$bff" "  $name" "$pack" && continue
 
                                        # use a specific inputs file, if required
                                        if [ -f "$bff.inputs" ]; then
                                                ffinputs="$bff.inputs"
                                        else
-                                               ffinputs=$inputs
+                                               ffinputs="$inputs"
                                        fi
 
                                        rm -rf "$fff.res" "$fff.err" "$fff.write" 2> /dev/null
@@ -613,30 +625,30 @@ END
                                        chmod +x "$fff.bin"
                                        WRITE="$fff.write" saferun -o "$fff.time.out" sh -c "NIT_NO_STACK=1 $fff.bin < $ffinputs > $fff.res 2>$fff.err"
                                        if [ "x$verbose" = "xtrue" ]; then
-                                               cat "$fff.res"
-                                               cat >&2 "$fff.err"
+                                               cat -- "$fff.res"
+                                               cat >&2 -- "$fff.err"
                                        fi
                                        if [ -f "$fff.write" ]; then
-                                               cat "$fff.write" >> "$fff.res"
+                                               cat -- "$fff.write" >> "$fff.res"
                                        elif [ -d "$fff.write" ]; then
-                                               LANG=C /bin/ls -F $fff.write >> "$fff.res"
+                                               LANG=C /bin/ls -F -- "$fff.write" >> "$fff.res"
                                        fi
                                        if [ -s "$fff.err" ]; then
-                                               cp "$fff.res"  "$fff.res2"
-                                               cat "$fff.err" "$fff.res2" > "$fff.res"
+                                               cp -- "$fff.res"  "$fff.res2"
+                                               cat -- "$fff.err" "$fff.res2" > "$fff.res"
                                        fi
-                                       process_result $bff "  $name" $pack
-                               done < $fargs
+                                       process_result "$bff" "  $name" "$pack"
+                               done < "$fargs"
                        fi
                elif [ -f "$ff.bin" ]; then
                        #Not executable (platform?)"
                        > "$ff.res"
-                       process_result $bf "$bf" $pack
+                       process_result "$bf" "$bf" "$pack"
                else
                        echo -n "! "
-                       cat "$ff.cmp.err" > "$ff.res"
+                       cat -- "$ff.cmp.err" > "$ff.res"
                        echo "Compilation error" > "$ff.res"
-                       process_result $bf "$bf" $pack
+                       process_result "$bf" "$bf" "$pack"
                fi
        done
 done