From: Jean Privat Date: Fri, 23 Jan 2015 04:14:01 +0000 (+0700) Subject: Merge: lib/github: implements caching to maximize rate limit. X-Git-Tag: v0.7.1~17 X-Git-Url: http://nitlanguage.org?hp=a88303f0759e0e229ee0ed585b1f0ead7fac0bc4 Merge: lib/github: implements caching to maximize rate limit. Using JsonStore to save data locally and lighten the stress on API rate limit. Pull-Request: #1110 Reviewed-by: Jean Privat --- diff --git a/VERSION b/VERSION index 882307c..03776fb 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.6.11 +v0.7 diff --git a/clib/gc_chooser.h b/clib/gc_chooser.h index fa395b0..255e07a 100644 --- a/clib/gc_chooser.h +++ b/clib/gc_chooser.h @@ -20,7 +20,7 @@ void *nit_raw_alloc(size_t); /* allocate raw memory to store a raw stram of byte void nit_gcollect(void); /* force a garbage collection */ void initialize_gc_option(void); /* Select the wanted GC using envvar `NIT_GC_OPTION` */ -void gc_set_finializer(void*); /* Tag a pointer for finalization */ +void gc_register_finalizer(void*); /* Tag a pointer for finalization */ void gc_finalize(void*, void*); /* Finalize a pointer, implemented in the generated code. */ #endif diff --git a/contrib/friendz/.gitignore b/contrib/friendz/.gitignore new file mode 100644 index 0000000..700b511 --- /dev/null +++ b/contrib/friendz/.gitignore @@ -0,0 +1 @@ +res/ diff --git a/contrib/friendz/Makefile b/contrib/friendz/Makefile index 0039fff..f236a1d 100644 --- a/contrib/friendz/Makefile +++ b/contrib/friendz/Makefile @@ -5,7 +5,8 @@ linux: ../../bin/nitc -o bin/friendz src/friendz_linux.nit android: - mkdir -p bin + mkdir -p bin res + ../inkscape_tools/bin/svg_to_icons art/icon.svg --android --out res/ ../../bin/nitc -o bin/friendz.apk src/friendz_android.nit doc: @@ -13,4 +14,4 @@ doc: ../../bin/nitdoc -d doc/ src/friendz.nit src/friendz_linux.nit clean: - rm -rf bin/ doc/ + rm -rf bin/ doc/ res/ diff --git a/contrib/friendz/README.md b/contrib/friendz/README.md index bd5f5e5..48be94e 100644 --- a/contrib/friendz/README.md +++ b/contrib/friendz/README.md @@ -33,3 +33,8 @@ In challenge level you have to place as much as monster as possible. This game was originally developed for the [Casual Gameplay Design Competition \#9](http://jayisgames.com/cgdc9). + +# Artwork + +The artwork was created by Sam Hocevar for the Monsterz arcade +puzzle game http://sam.zoy.org/monsterz/ diff --git a/contrib/friendz/art/icon.svg b/contrib/friendz/art/icon.svg new file mode 100644 index 0000000..3584b06 --- /dev/null +++ b/contrib/friendz/art/icon.svg @@ -0,0 +1,327 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/contrib/friendz/src/friendz.nit b/contrib/friendz/src/friendz.nit index 13915e6..2dd8d80 100644 --- a/contrib/friendz/src/friendz.nit +++ b/contrib/friendz/src/friendz.nit @@ -17,6 +17,8 @@ import mnit import realtime import solver import mnit::tileset +import app::data_store +import md5 intrude import grid intrude import level @@ -1080,8 +1082,8 @@ redef class Game level = null var i = levels.first for l in levels do - if l.get_state == l.l_open then break i = l + if l.get_state == l.l_open then break end entities.push(new StartButton(self, i)) end @@ -1142,7 +1144,8 @@ redef class Game end var ev = lastev if ev isa Event then - display.blit(img[4,0],ev.offset_x-42,ev.offset_y-6) + # Cursor, kept for debugging + #display.blit(img[4,0],ev.offset_x-42,ev.offset_y-6) end dirty_all = false end @@ -1267,6 +1270,18 @@ redef class Game end end end + + redef fun load_levels + do + super + + for level in levels do + var score = app.data_store["s{level.str.md5}"] + if score isa Int then + level.score = score + end + end + end end # The spash title image @@ -1634,3 +1649,10 @@ redef class KeyEvent return "unknown" end end + +redef class Level + redef fun save + do + app.data_store["s{str.md5}"] = if score > 0 then score else null + end +end diff --git a/contrib/friendz/src/friendz_android.nit b/contrib/friendz/src/friendz_android.nit index 94b9d2b..13a139e 100644 --- a/contrib/friendz/src/friendz_android.nit +++ b/contrib/friendz/src/friendz_android.nit @@ -12,16 +12,26 @@ module friendz_android import friendz import mnit_android +import android::landscape redef class App + # Zoom applied for the device display from the game logic coordinates + var zoom = 1.0 + redef fun window_created do super - var w = screen_width - display.set_viewport(0,0,w,w*display.height/display.width) + + var h = screen_height + display.set_viewport(0,0,h*display.width/display.height,h) + + zoom = display.height.to_f / h.to_f end end redef class AndroidPointerEvent - redef fun is_motion do return not motion_event.just_went_down + redef fun is_motion do return not just_went_down + + redef fun x do return super / app.zoom + redef fun y do return super / app.zoom end diff --git a/contrib/friendz/src/level.nit b/contrib/friendz/src/level.nit index d459875..9d8029e 100644 --- a/contrib/friendz/src/level.nit +++ b/contrib/friendz/src/level.nit @@ -96,10 +96,8 @@ class Level return true end - fun save - do - #save_cookie("s"+self.hash, self.score>0?self.score:"") - end + # Save the score of the level + fun save do end end # main game object @@ -174,8 +172,6 @@ class Game for i in [0..levels_code.length[ do var l = new Level(self,i, levels_code[i]) levels[i] = l - #var v = read_cookie("s"+l.hash) - #l = v end end diff --git a/contrib/jwrapper/Makefile b/contrib/jwrapper/Makefile index b554f0d..0932498 100644 --- a/contrib/jwrapper/Makefile +++ b/contrib/jwrapper/Makefile @@ -1,14 +1,22 @@ -default: - mkdir -p bin +all: nitcc grammar bin/jwrapper + +nitcc: make -C ../nitcc - ../nitcc/src/nitcc ./grammar/javap.sablecc - ../../bin/nitc ./src/jwrapper.nit -o ./bin/jwrapper - mv *.nit ./src/ - mkdir -p gen - mv javap* ./gen/ + +grammar: + ../nitcc/src/nitcc grammar/javap.sablecc + mkdir -p src gen + mv *.nit src/ + mv javap* gen/ + +bin/jwrapper: + mkdir -p bin + ../../bin/nitc src/jwrapper.nit -o bin/jwrapper clean: rm -f bin/javap_test_parser bin/jwrapper rm -f gen/* rm -rf .nit_compile/ rm src/javap_lexer.nit src/javap_parser.nit src/javap_test_parser.nit + +.PHONY: grammar bin/jwrapper diff --git a/contrib/jwrapper/README.md b/contrib/jwrapper/README.md index 8cd3bf0..da33dd2 100644 --- a/contrib/jwrapper/README.md +++ b/contrib/jwrapper/README.md @@ -1,20 +1,23 @@ -# JWRAPPER : Extern classes generator from java .class -## Description -jwrapper is a code generator that creates Nit extern classes `in "Java"` from .class files. +_jwrapper_, an extern class generator from Java class files -## Installation -jwrapper is designed to be installed from the `contrib` directory of Nit repository. (http://www.nitlanguage.org) +# Description +_jwrapper_ automates wrapping Java classes so they can be accessed from Nit code. It generates Nit extern classes by analyzing Java class files. + +_jwrapper_ reuse Nit types of already wrapped Java classes by searching in the `lib/java` and `lib/android` folders in the Nit repository. It won't wrap a class that are already is those folders. + +# Installation +_jwrapper_ is designed to be installed from the `contrib` directory of Nit repository. (http://www.nitlanguage.org) To initiate installation process, use `make` in the `contrib/jwrapper` directory. -jwrapper relies on `nitcc` that will be automatically compiled from `contrib/nitcc`. +_jwrapper_ relies on `nitcc` that will be automatically compiled from `contrib/nitcc`. -## Usage -The jwrapper binary can be found under `jwrapper/bin` directory. +# Usage +The _jwrapper_ binary can be found under `contrib/jwrapper/bin` directory. -Since jwrapper uses `grep` to find existing libraries, make sure that the environment variable `NIT_DIR` is properly set to your Nit root directory. +_jwrapper_ uses `grep` to find existing libraries, make sure that the environment variable `NIT_DIR` is properly set to your Nit root directory. -Since jwrapper uses `javap` to extract data from .class files, the JDK7 or higher has to be installed and must be in your `$PATH`. (Older versions of `javap` do not show generic signatures) +_jwrapper_ uses `javap` to extract data from .class files, the JDK7 or higher has to be installed and must be in your `$PATH`. (Older versions of `javap` do not show generic signatures) Usage : @@ -28,7 +31,7 @@ The options are : `-c, --comment` -* When a method contains at least one unknown type, the code generator will comment the whole method and let the client manage it. +* When a method contains at least one unknown type, the code generator will comment the whole method and let the client manage it. Unknown types are types that doesn't have an equivalent in Nit as of yet. `-w, --wrap` @@ -38,18 +41,4 @@ The options are : * Print the help message -Unknown types are types that doesn't have an equivalent in Nit as of yet. - -Jwrapper won't wrap a class that already is in the `lib/android` directory. - Can't use both -c and -w together, either you comment unknown types or you wrap them. - -## Limitations -jwrapper support for java primitive array is limited to one parameter and the return value. - -If the method contains more than one primitive array parameter, the method will be commented with a `NOT SUPPORTED` notice. - -## TODO List -* Generate static methods on top-level -* Extend primitive array support -* Enhance static overload support (currently, it suffixes the method name with a number) diff --git a/contrib/jwrapper/grammar/javap.sablecc b/contrib/jwrapper/grammar/javap.sablecc index 4e9165a..87dfa12 100644 --- a/contrib/jwrapper/grammar/javap.sablecc +++ b/contrib/jwrapper/grammar/javap.sablecc @@ -13,12 +13,12 @@ multi_files = class_or_interface*; class_or_interface = class_declaration | interface_declaration; -class_declaration = class_header '{' field_declaration* '}'; +class_declaration = class_header '{' property_declaration* '}'; class_header = modifier* 'class' full_class_name extends_declaration? implements_declaration? throws_declaration?; interface_declaration = modifier* 'interface' full_class_name extends_interface_declaration? - '{' field_declaration* '}'; + '{' property_declaration* '}'; modifier = 'public'|'private'|'protected'|'static'|'final'|'native'|'synchronized'|'abstract'|'threadsafe'|'transient'|'volatile'; type = type_specifier '[]'*; @@ -50,7 +50,7 @@ statement_block = '{' statement* '}'; variable_id = identifier '[]'*; method_id = identifier; -field_declaration = method_declaration | constructor_declaration | variable_declaration | static_declaration | ';'; +property_declaration = method_declaration | constructor_declaration | variable_declaration | static_declaration | ';'; variable_declaration = modifier* type variable_id throws_declaration? ';'; method_declaration = modifier* generic_param? type method_id '(' parameter_list? ')' throws_declaration? ';'; constructor_declaration = modifier* full_class_name '(' parameter_list? ')' throws_declaration? ';'; diff --git a/contrib/jwrapper/src/code_generator.nit b/contrib/jwrapper/src/code_generator.nit index 6c52a62..0da3c77 100644 --- a/contrib/jwrapper/src/code_generator.nit +++ b/contrib/jwrapper/src/code_generator.nit @@ -17,7 +17,7 @@ # Services to generate extern class `in "Java"` module code_generator -intrude import types +intrude import model class CodeGenerator @@ -105,7 +105,7 @@ class CodeGenerator do var temp = new Array[String] temp.add("extern class Native{jtype.id} in \"Java\" `\{ {jtype} `\}\n") - temp.add("\tsuper JavaObject\n\tredef type SELF: Native{jtype.id}\n\n") + temp.add("\tsuper JavaObject\n\n") return temp.join("") end @@ -121,16 +121,16 @@ class CodeGenerator var temp = new Array[String] temp.add("extern class {nit_type} in \"Java\" `\{ {jtype.to_package_name} `\}\n") - temp.add("\tsuper JavaObject\n\tredef type SELF: {nit_type}\n\nend\n") + temp.add("\tsuper JavaObject\n\nend\n") return temp.join("") end fun gen_attribute(jid: String, jtype: JavaType): String do - return "\tvar {jid.to_snake_case}: {jtype.to_nit_type}\n" + return "\tvar {jid.to_nit_method_name}: {jtype.to_nit_type}\n" end - + fun gen_method(jmethod_id: String, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType]): String do var java_params = "" @@ -138,7 +138,7 @@ class CodeGenerator var nit_id = "arg" var nit_id_no = 0 var nit_types = new Array[NitType] - var comment = "" + var comment = "" # Parameters for i in [0..jparam_list.length[ do @@ -177,7 +177,7 @@ class CodeGenerator end # Method identifier - var method_id = nmethod_id.to_snake_case + var method_id = nmethod_id.to_nit_method_name var nit_signature = new Array[String] nit_signature.add "\tfun {method_id}" @@ -207,16 +207,8 @@ class CodeGenerator nit_signature.add ": {return_type} " end - var param_to_copy = param_to_copy(jparam_list, nit_types) - var temp = new Array[String] - if nb_params > 1 then - comment = "#" - temp.add("\t# NOT SUPPORTED: more than one parameter to copy\n") - temp.add("\t# Has to be implemented manually\n") - end - temp.add(comment + nit_signature.join("")) # FIXME : This huge `if` block is only necessary to copy primitive arrays as long as there's no better way to do it @@ -224,31 +216,10 @@ class CodeGenerator temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n") # Methods with return type else if return_type != null then - if jreturn_type.is_primitive_array then - # Copy one parameter and the return value - if param_to_copy != null then - var rtype_couple = new Couple[JavaType, NitType](jreturn_type, return_type) - temp.add(code_warehouse.param_return_copy(rtype_couple, param_to_copy, jmethod_id, java_params)) - # Copy the return type - else - temp.add(code_warehouse.return_type_copy(jreturn_type, return_type, jmethod_id, java_params)) - end - # Copy the parameter - else if param_to_copy != null then - temp.add(code_warehouse.param_type_copy(param_to_copy.first, param_to_copy.second, jmethod_id, java_params, true)) - # No copy - else - temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast} recv.{jmethod_id}({java_params});\n{comment}\t`\}\n") - end + temp.add(" in \"Java\" `\{\n{comment}\t\treturn {jreturn_type.return_cast}recv.{jmethod_id}({java_params});\n{comment}\t`\}\n") # Methods without return type else if jreturn_type.is_void then - # Copy one parameter - if param_to_copy != null then - temp.add(code_warehouse.param_type_copy(param_to_copy.first, param_to_copy.second, jmethod_id, java_params, false)) - # No copy - else - temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n") - end + temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n") # No copy else temp.add(" in \"Java\" `\{\n{comment}\t\trecv.{jmethod_id}({java_params});\n{comment}\t`\}\n") @@ -256,132 +227,11 @@ class CodeGenerator return temp.join("") end - - # Only one primitive array parameter can be copied - # If there's none or more than one then `null` is returned - fun param_to_copy(jtypes: Array[JavaType], ntypes: Array[NitType]): nullable Couple[JavaType, NitType] - do - var counter = 0 - var couple = null - for i in [0..jtypes.length[ do - if jtypes[i].is_primitive_array then - counter += 1 - couple = new Couple[JavaType, NitType](jtypes[i], ntypes[i]) - end - end - - nb_params = counter - - if counter > 1 then return null - return couple - end end # Contains raw code mostly used to copy collections class CodeWarehouse - # Collection as return value - fun return_type_copy(java_type: JavaType, nit_type: NitType, jmethod_id, params_id: String): String - do - var narray_id = "nit_array" - var loop_ = create_loop(java_type, nit_type, false, "java_array", narray_id) - var imports = create_imports(nit_type, false) - - return """{{{imports}}} in "Java" `{ - {{{java_type.to_s}}} java_array = recv.{{{jmethod_id}}}({{{params_id}}}); - int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}(); - - {{{loop_}}} - - return {{{narray_id}}}; - `} -""" - end - - # Collection as parameter - fun param_type_copy(java_type: JavaType, nit_type: NitType, jmethod_id, params_id: String, has_return: Bool): String - do - var narray_id = "nit_array" - var jarray_id = "java_array" - var loop_ = create_loop(java_type, nit_type, true, jarray_id, narray_id) - var imports = create_imports(nit_type, true) - var jinstanciation = create_array_instance(java_type, nit_type, jarray_id) - var return_str = "" - - if has_return then - return_str = "return " - end - - params_id = params_id.replace(nit_type.arg_id, jarray_id) - - return """{{{imports}}} in "Java" `{ - {{{jinstanciation}}} - int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}(); - - {{{loop_}}} - - {{{return_str}}}recv.{{{jmethod_id}}}({{{params_id}}}); - `} -""" - end - - # One collection parameter and the return type will be copied - fun param_return_copy(return_types, param_types: Couple[JavaType, NitType], jmethod_id, params_id: String): String - do - var narray_id = "nit_array" - var narray_id2 = "nit_array2" - - var r_jtype = return_types.first - var r_ntype = return_types.second - - var p_jtype = param_types.first - var p_ntype = param_types.second - - var r_loop = create_loop(r_jtype, r_ntype, false, "java_array", narray_id) - var p_loop = create_loop(p_jtype, p_ntype, true, "java_array2", narray_id2) - - var imports = new Array[String] - - # Avoid import duplication - if p_ntype.to_s != r_ntype.to_s then - imports.add create_imports(p_ntype, true) - end - - imports.add create_imports(r_ntype, false) - - params_id = params_id.replace(p_ntype.arg_id, narray_id) - - var jinstanciation = create_array_instance(p_jtype, p_ntype, "java_array") - - return """{{{imports.join(", ")}}} in "Java" `{ - {{{jinstanciation}}} - - {{{p_loop}}} - - {{{r_jtype.to_s}}} java_array2 = recv.{{{jmethod_id}}}({{{params_id}}}); - int {{{narray_id2}}} = new_{{{r_ntype.id}}}_of_{{{r_ntype.generic_params.join("_")}}}(); - - {{{r_loop}}} - - return {{{narray_id2}}}; - `} -""" - end - - private fun create_array_instance(java_type: JavaType, nit_type: NitType, jarray_id: String): String - do - var jtype = java_type.to_s - var instanciation = "" - - if java_type.is_primitive_array then - instanciation = "{jtype} {jarray_id} = new {java_type.full_id}[(int)Array_of_{nit_type.generic_params[0]}_length({nit_type.arg_id})];" - else - instanciation = "{jtype} {jarray_id} = new {jtype}();" - end - - return instanciation - end - private fun create_imports(nit_type: NitType, is_param: Bool): String do var imports = "" @@ -401,48 +251,29 @@ class CodeWarehouse else imports = """ import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item""" end - + return imports end +end - private fun create_loop(java_type: JavaType, nit_type: NitType, is_param: Bool, jarray_id, narray_id: String): String +redef class String + # Convert the Java method name `self` to the Nit style + # + # * Converts to snake case + # * Strips `Get` and `Set` + # * Add suffix `=` to setters + fun to_nit_method_name: String do - var loop_header = "" - var loop_body = "" - var gen_type = nit_type.generic_params.join("_") - - if is_param then - if java_type.is_primitive_array then - loop_header = "for(int i=0; i < {jarray_id}.length; ++i)" - loop_body = """\t\t\t{{{jarray_id}}}[i] = {{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{nit_type.arg_id}}}, i);""" - else if nit_type.id == "Array" then - loop_header = """int length = Array_of_{{{gen_type}}}_length((int){{{nit_type.arg_id}}});\n\t\tfor(int i=0; i < length; ++i)""" - loop_body = """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{narray_id}}}, i));""" - else - loop_header = """int itr = {{{nit_type.id}}}_of_{{{gen_type}}}_iterator({{{nit_type.arg_id}}});\n\t\twhile(Iterator_of_{{{gen_type}}}_is_ok(itr)) {""" - if nit_type.is_map then - var key_cast = java_type.to_cast(java_type.generic_params[0].id, true) - var value_cast = java_type.to_cast(java_type.generic_params[1].id, true) - loop_body = """\t\t\t{{{jarray_id}}}[{{{key_cast}}}iterator_of_{{{nit_type.id}}}_key(itr)] = {{{value_cast}}}iterator_of_{{{nit_type.id}}}_item(itr);\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}""" - else - loop_body = """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}iterator_of_{{{nit_type.id}}}_item(itr));\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}""" - end - end + var name + if self.has_prefix("Get") then + name = self.substring_from(3) + else if self.has_prefix("Set") then + name = self.substring_from(3) + name += "=" else - if nit_type.is_map then - var key_cast = java_type.to_cast(java_type.generic_params[0].id, false) - var value_cast = java_type.to_cast(java_type.generic_params[1].id, false) - loop_header = """for (java.util.Map.Entry<{{{java_type.generic_params[0]}}}, {{{java_type.generic_params[1]}}}> e: {{{jarray_id}}})""" - loop_body = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_{{{nit_type.generic_params[1]}}}__index_assign({{{narray_id}}}, {{{key_cast}}}e.getKey(), {{{value_cast}}}e.getValue());""" - else if java_type.is_iterable then - loop_header = """for ({{{java_type.generic_params[0]}}} e: {{{jarray_id}}})""" - loop_body = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}e);""" - else - loop_header = "for(int i=0; i < {jarray_id}.length; ++i)" - loop_body = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}{{{jarray_id}}}[i]);""" - end + name = self end - return loop_header + "\n" + loop_body + return name.to_snake_case end end diff --git a/contrib/jwrapper/src/javap_visitor.nit b/contrib/jwrapper/src/javap_visitor.nit index 1ede6f0..9ee4190 100644 --- a/contrib/jwrapper/src/javap_visitor.nit +++ b/contrib/jwrapper/src/javap_visitor.nit @@ -21,7 +21,7 @@ module javap_visitor import javap_test_parser import code_generator import jtype_converter -intrude import types +intrude import model class JavaVisitor super Visitor @@ -336,11 +336,11 @@ redef class Nimplements_declaration end end -# # -# F I E L D D E C L A R A T I O N S # -# # +# # +# P R O P E R T Y D E C L A R A T I O N S # +# # -# Method declaration in the field declarations +# Method declaration redef class Nmethod_declaration redef fun accept_visitor(v) do @@ -357,7 +357,7 @@ redef class Nmethod_declaration end end -# Constructor declaration in the field declarations +# Constructor declaration redef class Nconstructor_declaration redef fun accept_visitor(v) do @@ -367,7 +367,7 @@ redef class Nconstructor_declaration end end -# Variable declaration in the field declarations +# Variable property declaration redef class Nvariable_declaration redef fun accept_visitor(v) do @@ -382,7 +382,7 @@ redef class Nvariable_declaration end end -# Static declaration in the field declarations +# Static property declaration redef class Nstatic_declaration redef fun accept_visitor(v) do @@ -392,7 +392,7 @@ redef class Nstatic_declaration end end -# Identifier of the field +# Identifier of a variable redef class Nvariable_id redef fun accept_visitor(v) do diff --git a/contrib/jwrapper/src/jtype_converter.nit b/contrib/jwrapper/src/jtype_converter.nit index c5a8b29..f70d6ed 100644 --- a/contrib/jwrapper/src/jtype_converter.nit +++ b/contrib/jwrapper/src/jtype_converter.nit @@ -61,28 +61,6 @@ class JavaTypeConverter return_cast_map["CharSequence"] = "(String)" end - init with_collections - do - self.init - # Collections - type_map["List"] = "Array" - type_map["ArrayList"] = "Array" - type_map["LinkedList"] = "List" - type_map["Vector"] = "Array" - - type_map["Set"] = "HashSet" - type_map["SortedSet"] = "Still have to make my mind on this one" - type_map["HashSet"] = "HashSet" - type_map["TreeSet"] = "HashSet" - type_map["LinkedHashSet"] = "HashSet" - type_map["Map"] = "HashMap" - type_map["SortedMap"] = "RBTreeMap" - type_map["HashMap"] = "HashMap" - type_map["TreeMap"] = "RBTreeMap" - type_map["Hashtable"] = "HashMap" - type_map["LinkedHashMap"] = "HashMap" - end - fun to_nit_type(java_type: String): nullable String do return self.type_map.get_or_null(java_type) diff --git a/contrib/jwrapper/src/types.nit b/contrib/jwrapper/src/model.nit similarity index 78% rename from contrib/jwrapper/src/types.nit rename to contrib/jwrapper/src/model.nit index 3a14d8d..b77c734 100644 --- a/contrib/jwrapper/src/types.nit +++ b/contrib/jwrapper/src/model.nit @@ -15,7 +15,7 @@ # limitations under the License. # Contains the java and nit type representation used to convert java to nit code -module types +module model import jtype_converter @@ -53,13 +53,12 @@ class JavaType fun to_nit_type: NitType do var nit_type: NitType + var type_id = null - if self.is_primitive_array then - return self.convert_primitive_array + if not is_primitive_array then + type_id = converter.to_nit_type(self.id) end - var type_id = converter.to_nit_type(self.id) - if type_id == null then nit_type = self.extern_name nit_type.is_complete = false @@ -68,7 +67,7 @@ class JavaType end if not self.has_generic_params then return nit_type - + nit_type.generic_params = new Array[NitType] for param in generic_params do @@ -82,38 +81,6 @@ class JavaType return nit_type end - fun convert_primitive_array: NitType - do - var nit_type = new NitType("Array") - - var last_nit_type = nit_type - - for i in [1..array_dimension] do - var temp: NitType - last_nit_type.generic_params = new Array[NitType] - - if i == array_dimension then - var temp_type = converter.to_nit_type(self.id) - - if temp_type == null then - temp = self.extern_name - nit_type.is_complete = false - if temp.mod != null then nit_type.mod = temp.mod - else - temp = new NitType(temp_type) - end - else - temp = new NitType("Array") - end - - last_nit_type.generic_params.add(temp) - - last_nit_type = temp - end - - return nit_type - end - fun is_iterable: Bool do return iterable.has(self.id) fun is_collection: Bool do return is_primitive_array or collections_list.has(self.id) @@ -126,15 +93,16 @@ class JavaType do if is_wrapped then return new NitType.with_module(find_extern_class.as(not null).first, find_extern_class.as(not null).second) - var name = "Native" + extern_class_name.join("") - var nit_type: NitType - if self.is_primitive_array then - nit_type = new NitType.with_generic_params("Array", name) + var name + if is_primitive_array then + # Primitive arrays have a special naming convention + name = "Native" + extern_class_name.join("").capitalized + "Array" else - nit_type = new NitType("Native" + extern_class_name.join("")) + name = "Native" + extern_class_name.join("") end - nit_type.is_complete = false + var nit_type = new NitType(name) + nit_type.is_complete = false return nit_type end @@ -155,7 +123,7 @@ class JavaType for i in [0..array_dimension[ do id += "[]" end - else if self.has_generic_params then + else if self.has_generic_params then var gen_list = new Array[String] for param in generic_params do @@ -206,12 +174,11 @@ class JavaType # Search inside `lib/android` directory for already wrapped classes # If found, contains the class identifier and the Nit Module name - var find_extern_class: nullable Couple[String, NitModule] = find_extern_class_fun is lazy + var find_extern_class: nullable Couple[String, NitModule] is lazy do - private fun find_extern_class_fun: nullable Couple[String, NitModule] - do - var regex = "extern class Native[a-zA-Z1-9]\\\+[ ]\\\+in[ ]\\\+\"Java\"[ ]*`\{[ ]*" + self.to_s + "\\\+[ ]*`\}" - var grep = new IProcess("grep", "-r", regex, "{"NIT_DIR".environ}/lib/android/") + var regex = "extern class [a-zA-Z1-9]\\\+[ ]\\\+in[ ]\\\+\"Java\"[ ]*`\{[ ]*" + self.to_s + "\\\+[ ]*`\}" + var nit_dir = "NIT_DIR".environ + var grep = new IProcess("grep", "-r", regex, nit_dir/"lib/android/", nit_dir/"lib/java/") var to_eat = ["private", "extern", "class"] var output = grep.read_line @@ -310,7 +277,7 @@ class NitType do var id = self.identifier - if self.has_generic_params then + if self.has_generic_params then var gen_list = new Array[String] for param in generic_params do @@ -327,36 +294,36 @@ end class JavaClass var class_type = new JavaType(new JavaTypeConverter) var attributes = new HashMap[String, JavaType] - var methods = new HashMap[String, Array[JReturnAndParams]] + + # Methods of this class organized by their name + var methods = new HashMap[String, Array[JavaMethod]] + var unknown_types = new HashSet[JavaType] var imports = new HashSet[NitModule] fun add_method(id: String, return_type: JavaType, params: Array[JavaType]) do - var ret_and_params = methods.get_or_default(id, new Array[JReturnAndParams]) - - ret_and_params.add(new JReturnAndParams(return_type, new Array[JavaType].from(params))) - methods[id] = ret_and_params + var signatures = methods.get_or_default(id, new Array[JavaMethod]) + signatures.add(new JavaMethod(return_type, new Array[JavaType].from(params))) + methods[id] = signatures end end -class JReturnAndParams +# A Java method, with its signature +class JavaMethod + # Type returned by the method var return_type: JavaType - var params: Array[JavaType] - init(return_type: JavaType, params: Array[JavaType]) - do - self.return_type = return_type - self.params = params - end + # Type of the arguments of the method + var params: Array[JavaType] end +# A Nit module, use to import the referenced extern classes class NitModule - var value: String - - init(str: String) do value = str + # Name of the module + var name: String redef fun ==(other): Bool do return self.to_s == other.to_s - redef fun to_s: String do return self.value - redef fun hash: Int do return self.value.hash + redef fun to_s: String do return self.name + redef fun hash: Int do return self.name.hash end diff --git a/contrib/neo_doxygen/README.md b/contrib/neo_doxygen/README.md index 9da5b92..c33e770 100644 --- a/contrib/neo_doxygen/README.md +++ b/contrib/neo_doxygen/README.md @@ -69,3 +69,22 @@ Also, they **must** be run with the current working directory set to the present directory. `gen-one.sh` handle only one project at a time while `gen-all.sh` works on a collection of projects grouped in a directory. For detail about how to invoke each script, see the comments in these scripts. + + +## Brief descriptions + +To populate the first line of a description (used as brief description in +Nitdoc), `neo_doxygen` uses the brief description provided by Doxygen. So, you +may need to change settings like `JAVADOC_AUTOBRIEF`, `QT_AUTOBRIEF` and +`MULTILINE_CPP_IS_BRIEF` in Doxygen to make `neo_doxygen` properly split the +brief description from the detailed description. In absence of brief +description, `neo_doxygen` will use the first block (usually, the first +paragraph) of the detailed as brief description. + + +## Python + +The built-in filter of Doxygen for Python is very basic. For example, it +recognizes anything in the “docstrings” as verbatim detailed description. In +order to enhance the processing of the Python scripts, the +[doxypypy](https://github.com/Feneric/doxypypy) filter may be used. diff --git a/contrib/neo_doxygen/src/doxml/doc_listener.nit b/contrib/neo_doxygen/src/doxml/doc_listener.nit new file mode 100644 index 0000000..05ecd35 --- /dev/null +++ b/contrib/neo_doxygen/src/doxml/doc_listener.nit @@ -0,0 +1,94 @@ +# 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. + +# Documentation reading. +module doxml::doc_listener + +import listener +import html + +# Processes documentation. +class DocListener + super TextListener + + # The read documentation. + var doc = new Documentation is writable + + # Mapping between the type of a Doxygen element and the corresponding + # factory. + private var factories = new HashMap[String, HtmlElementFactory] + + private var element_stack = new Array[HTMLTag] + + # Does the next block have to be added to the detailed description? + private var in_detailed_description = false + + redef fun listen_until(uri, local_name) do + super + if local_name == "briefdescription" then + in_detailed_description = false + else + in_detailed_description = true + end + end + + redef fun start_dox_element(local_name, atts) do + super + var factory = factories.get_or_null(local_name) + if factory == null then return + element_stack.push(factory.create_element(local_name, atts)) + end + + redef fun end_dox_element(local_name) do + super + if not factories.has_key(local_name) then return + if element_stack.is_empty then return + var current_element = element_stack.pop + current_element.append(flush_buffer.trim) + if element_stack.is_empty then add_block(current_element.write_to_string) + end + + redef fun end_listening do + super + if not element_stack.is_empty then + var current_element = element_stack.first.write_to_string + add_block(current_element) + end + add_block(flush_buffer.trim) + element_stack.clear + end + + private fun add_block(block: String) do + if block.is_empty then return + if in_detailed_description then + doc.add(block) + else + doc.brief_description = block + in_detailed_description = true + end + end +end + +# Provides a mean to create a certain kind of HTML elements. +private abstract class HtmlElementFactory + # Create a new empty HTML element. + # + # Parameters: + # + # * `local_name`: Type of the Doxygen element that will be represented by + # the HTML element. + # * `attributes`: Attributes of the Doxygen element that will be + # represented by the HTML element. + fun create_element(local_name: String, attributes: Attributes): HTMLTag is abstract +end diff --git a/contrib/neo_doxygen/src/doxml/entitydef.nit b/contrib/neo_doxygen/src/doxml/entitydef.nit index 6c11a6c..0f83aa1 100644 --- a/contrib/neo_doxygen/src/doxml/entitydef.nit +++ b/contrib/neo_doxygen/src/doxml/entitydef.nit @@ -15,7 +15,7 @@ # Common SAX listeners for entity definitions. module doxml::entitydef -import doc +import doc_listener # Processes the content of an entity definition. abstract class EntityDefListener diff --git a/contrib/neo_doxygen/src/doxml/language_specific.nit b/contrib/neo_doxygen/src/doxml/language_specific.nit index fc03b0f..9566f9e 100644 --- a/contrib/neo_doxygen/src/doxml/language_specific.nit +++ b/contrib/neo_doxygen/src/doxml/language_specific.nit @@ -179,3 +179,14 @@ class JavaSource super end end + +# Importation logics for Python. +class PythonSource + super SourceLanguage + + redef fun apply_member_type(member, type_text) do + # Doxygen may forgot to remove the `def` keyword on methods. + extract_keyword(type_text, "def") + super + end +end diff --git a/contrib/neo_doxygen/src/model/descriptions.nit b/contrib/neo_doxygen/src/model/descriptions.nit new file mode 100644 index 0000000..1f504ef --- /dev/null +++ b/contrib/neo_doxygen/src/model/descriptions.nit @@ -0,0 +1,117 @@ +# 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. + +# Documentation associated to an entity. +module model::descriptions + +import json::static + +# Documentation associated to an entity. +# +# The documentation is written in Markdown format. +# +# ~~~nit +# var doc = new Documentation +# +# doc.brief_description = "Do something." +# doc.detailed_description = ["Do not lunch a rocket."] +# assert doc.brief_description == "Do something." +# assert doc.detailed_description == ["Do not lunch a rocket."] +# assert doc.to_json == """["Do something.","Do not lunch a rocket."]""" +# +# doc.brief_description = "" +# doc.detailed_description = ["The answer is `42`."] +# assert doc.brief_description == "The answer is `42`." +# assert doc.detailed_description == ["The answer is `42`."] +# assert doc.to_json == """["The answer is `42`."]""" +# +# doc.detailed_description = ["The answer is `42`."] +# doc.brief_description = "" +# assert doc.brief_description == "The answer is `42`." +# assert doc.detailed_description == ["The answer is `42`."] +# assert doc.to_json == """["The answer is `42`."]""" +# +# doc.detailed_description = new Array[String] +# doc.brief_description = "" +# assert doc.is_empty +# assert doc.brief_description == "" +# assert doc.detailed_description == new Array[String] +# assert doc.to_json == "[]" +# ~~~ +class Documentation + super Jsonable + + private var content = new JsonStringArray + private var has_brief_description: Bool = false + + # The brief description. + # + # If it is empty, the first element of `detailed_description` will be used + # as brief description. + fun brief_description=(brief_description: String) do + if brief_description.is_empty then + if has_brief_description then + content.shift + has_brief_description = false + end + else if has_brief_description then + content.first = brief_description + else + content.unshift(brief_description) + has_brief_description = true + end + end + + # The brief description. + fun brief_description: String do + if not is_empty then return content.first + return "" + end + + # The detailed description. + # + # Each element should represent a block. + fun detailed_description=(detailed_description: SequenceRead[String]) do + if has_brief_description then + while content.length > 1 do content.pop + else + content.clear + end + content.add_all(detailed_description) + end + + # The detailed description. + # + # Each element should represent a block. + fun detailed_description: SequenceRead[String] do + if not has_brief_description then return content + if content.length > 1 then return content.subarray(1, content.length - 1) + return new Array[String] + end + + # Add a block of detailed description. + fun add(block: String) do content.add block + + # Is the documentation empty? + fun is_empty: Bool do return content.is_empty + + redef fun to_json do return content.to_json + redef fun append_json(b) do content.append_json(b) +end + +# A `Jsonable` array of strings. +private class JsonStringArray + super JsonSequenceRead[String] + super Array[String] +end diff --git a/contrib/neo_doxygen/src/model/graph.nit b/contrib/neo_doxygen/src/model/graph.nit index 7c8d2a3..74a81b5 100644 --- a/contrib/neo_doxygen/src/model/graph.nit +++ b/contrib/neo_doxygen/src/model/graph.nit @@ -18,6 +18,7 @@ module model::graph import neo4j import more_collections import location +import descriptions # A Neo4j graph. class NeoGraph @@ -135,7 +136,7 @@ abstract class Entity var full_name: nullable String = null is writable # Associated documentation. - var doc = new JsonArray is writable + var doc = new Documentation is writable init do self.labels.add(graph.project_name) @@ -179,7 +180,7 @@ abstract class Entity # # Called by the loader when it has finished to read the entity. fun put_in_graph do - if doc.length > 0 then + if not doc.is_empty then set_mdoc end graph.all_nodes.add(self) diff --git a/contrib/neo_doxygen/src/model/location.nit b/contrib/neo_doxygen/src/model/location.nit index f025a12..b78a2ef 100644 --- a/contrib/neo_doxygen/src/model/location.nit +++ b/contrib/neo_doxygen/src/model/location.nit @@ -15,7 +15,7 @@ # This module is used to model locations in source files. module location -import neo4j +import json::static # A location inside a source file. class Location diff --git a/contrib/neo_doxygen/src/model/module_compound.nit b/contrib/neo_doxygen/src/model/module_compound.nit index 95ce6cb..188d4c1 100644 --- a/contrib/neo_doxygen/src/model/module_compound.nit +++ b/contrib/neo_doxygen/src/model/module_compound.nit @@ -139,19 +139,19 @@ private class Module # declares it. fun update_name do name = file_compound.basename - redef fun put_in_graph do - super - end - redef fun put_edges do var ns_compound = namespace.seek_in(graph) var self_class = ns_compound.self_class + var class_count = 0 + var last_class: nullable ClassCompound = null graph.add_edge(ns_compound, "DECLARES", self) for c in file_compound.inner_classes do if graph.class_to_ns[c] != ns_compound then continue var class_compound = graph.by_id[c].as(ClassCompound) + last_class = class_compound + class_count += 1 graph.add_edge(self, "INTRODUCES", class_compound) graph.add_edge(self, "DEFINES", class_compound.class_def) end @@ -163,6 +163,13 @@ private class Module graph.add_edge(self, "INTRODUCES", self_class) graph.add_edge(self, "DEFINES", self_class.class_def) end + + if doc.is_empty and class_count == 1 then + doc = last_class.as(not null).doc + end + if doc.is_empty then doc = file_compound.doc + if doc.is_empty then doc = ns_compound.doc + if not doc.is_empty then set_mdoc end end diff --git a/contrib/neo_doxygen/src/model/namespace_members.nit b/contrib/neo_doxygen/src/model/namespace_members.nit index ce73ee3..4c85d5a 100644 --- a/contrib/neo_doxygen/src/model/namespace_members.nit +++ b/contrib/neo_doxygen/src/model/namespace_members.nit @@ -65,4 +65,9 @@ class SelfClass super name = "(self)" end + + redef fun put_in_graph do + if doc.is_empty then doc = namespace.doc + super + end end diff --git a/contrib/neo_doxygen/src/neo_doxygen.nit b/contrib/neo_doxygen/src/neo_doxygen.nit index 46c6be2..431809c 100644 --- a/contrib/neo_doxygen/src/neo_doxygen.nit +++ b/contrib/neo_doxygen/src/neo_doxygen.nit @@ -149,6 +149,7 @@ class NeoDoxygenCommand init do sources["any"] = new DefaultSource sources["java"] = new JavaSource + sources["python"] = new PythonSource var prefix = new OptionText(""" {{{"NAME".bold}}} @@ -185,7 +186,7 @@ class NeoDoxygenCommand var keys = new Array[String].from(sources.keys) opt_src_lang = new OptionEnum(keys, - "The programming language to assume when processing chunk in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.", + "The programming language to assume when processing chunks in the declarations left as-is by Doxygen. Use `any` (the default) to disable any language-specific processing.", keys.index_of("any"), "--src-lang") option_context.add_option(opt_src_lang) end @@ -249,8 +250,8 @@ end # Add handling of multi-line descriptions. # -# Note: The algorithm is naive and do not handle internationalisation and -# escape sequences. +# Note: The algorithm is naive and do not handle internationalisation, +# multi-byte characters and control characters. redef class Option redef fun pretty(off) do diff --git a/contrib/neo_doxygen/src/tests/neo_doxygen_descriptions.nit b/contrib/neo_doxygen/src/tests/neo_doxygen_descriptions.nit new file mode 100644 index 0000000..d277be7 --- /dev/null +++ b/contrib/neo_doxygen/src/tests/neo_doxygen_descriptions.nit @@ -0,0 +1,44 @@ +# 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 model::descriptions + +# Copied from the documentation of `Documentation`. + +var doc = new Documentation + +doc.brief_description = "Do something." +doc.detailed_description = ["Do not lunch a rocket."] +assert doc.brief_description == "Do something." +assert doc.detailed_description == ["Do not lunch a rocket."] +assert doc.to_json == """["Do something.","Do not lunch a rocket."]""" + +doc.brief_description = "" +doc.detailed_description = ["The answer is `42`."] +assert doc.brief_description == "The answer is `42`." +assert doc.detailed_description == ["The answer is `42`."] +assert doc.to_json == """["The answer is `42`."]""" + +doc.detailed_description = ["The answer is `42`."] +doc.brief_description = "" +assert doc.brief_description == "The answer is `42`." +assert doc.detailed_description == ["The answer is `42`."] +assert doc.to_json == """["The answer is `42`."]""" + +doc.detailed_description = new Array[String] +doc.brief_description = "" +assert doc.is_empty +assert doc.brief_description == "" +assert doc.detailed_description == new Array[String] +assert doc.to_json == "[]" diff --git a/contrib/neo_doxygen/src/tests/neo_doxygen_doc_module_class.nit b/contrib/neo_doxygen/src/tests/neo_doxygen_doc_module_class.nit new file mode 100644 index 0000000..48f73d5 --- /dev/null +++ b/contrib/neo_doxygen/src/tests/neo_doxygen_doc_module_class.nit @@ -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. + +import tests +intrude import model::module_compound + +var graph = new ProjectGraph("foo") +var file = new FileCompound(graph) +var bar_class = new ClassCompound(graph) +var a_ns = new Namespace(graph) + +file.full_name = "Baz.java" +file.declare_class("classa_bar", "a::Bar", "public") +file.declare_namespace("namespacea", "a") +file.doc.brief_description = "A file." +file.put_in_graph + +a_ns.full_name = "a" +a_ns.model_id = "namespacea" +a_ns.declare_class("classa_bar", "a::Bar", "public") +a_ns.doc.brief_description = "A namespace." +a_ns.put_in_graph + +bar_class.model_id = "classa_bar" +bar_class.full_name = "a::Bar" +bar_class.doc.brief_description = "A class." +bar_class.put_in_graph + +graph.add_global_modules +graph.put_edges + +assert file.inner_namespaces[0]["mdoc"] == bar_class.doc diff --git a/contrib/neo_doxygen/src/tests/neo_doxygen_file_compound.nit b/contrib/neo_doxygen/src/tests/neo_doxygen_file_compound.nit index 73adc61..c66709e 100644 --- a/contrib/neo_doxygen/src/tests/neo_doxygen_file_compound.nit +++ b/contrib/neo_doxygen/src/tests/neo_doxygen_file_compound.nit @@ -36,6 +36,7 @@ file.location = location file.declare_class("classa_b_bar", "a::b::Bar", "package") file.declare_class("classbaz", "Baz", "") file.declare_namespace("", "a::b") +file.doc.brief_description = "The first file." file.put_in_graph file_2.name = "Bar.java" diff --git a/contrib/neo_doxygen/src/tests/neo_doxygen_namespace_members.nit b/contrib/neo_doxygen/src/tests/neo_doxygen_namespace_members.nit index 346723d..4030df9 100644 --- a/contrib/neo_doxygen/src/tests/neo_doxygen_namespace_members.nit +++ b/contrib/neo_doxygen/src/tests/neo_doxygen_namespace_members.nit @@ -33,6 +33,7 @@ member.put_in_graph ns.model_id = "namespacefoo" ns.name = "foo" ns.declare_member(member) +ns.doc.brief_description = "A documented namespace." ns.put_in_graph root_ns.declare_namespace("namespacefoo", "") diff --git a/contrib/neo_doxygen/tests/inner-class/Doxyfile b/contrib/neo_doxygen/tests/inner-class/Doxyfile index bd06324..46601c2 100644 --- a/contrib/neo_doxygen/tests/inner-class/Doxyfile +++ b/contrib/neo_doxygen/tests/inner-class/Doxyfile @@ -187,7 +187,7 @@ SHORT_NAMES = NO # description.) # The default value is: NO. -JAVADOC_AUTOBRIEF = NO +JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If diff --git a/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class.xml b/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class.xml index 0e516fa..1bd92a8 100644 --- a/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class.xml +++ b/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class.xml @@ -4,9 +4,9 @@ OuterClass OuterClass::InnerClass - +A class with an inner class. -A class with an inner class. + diff --git a/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class_1_1_inner_class.xml b/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class_1_1_inner_class.xml index fdabf85..570babb 100644 --- a/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class_1_1_inner_class.xml +++ b/contrib/neo_doxygen/tests/inner-class/xml/class_outer_class_1_1_inner_class.xml @@ -3,9 +3,9 @@ OuterClass::InnerClass - +An instance (non-static) inner class. -An instance (non-static) inner class. + diff --git a/contrib/neo_doxygen/tests/java-project/Doxyfile b/contrib/neo_doxygen/tests/java-project/Doxyfile index bd06324..46601c2 100644 --- a/contrib/neo_doxygen/tests/java-project/Doxyfile +++ b/contrib/neo_doxygen/tests/java-project/Doxyfile @@ -187,7 +187,7 @@ SHORT_NAMES = NO # description.) # The default value is: NO. -JAVADOC_AUTOBRIEF = NO +JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If diff --git a/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_a.xml b/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_a.xml index 4475075..b380cf7 100644 --- a/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_a.xml +++ b/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_a.xml @@ -18,9 +18,9 @@ y - +Does something... -Does something... + diff --git a/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_b.xml b/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_b.xml index eb1fd6c..b01966b 100644 --- a/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_b.xml +++ b/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_b.xml @@ -49,9 +49,9 @@ baz baz - +Some overriden documentation. -Some overriden documentation. + diff --git a/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_empty_class.xml b/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_empty_class.xml index 9aa6aa2..04854de 100644 --- a/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_empty_class.xml +++ b/contrib/neo_doxygen/tests/java-project/xml/classorg_1_1example_1_1foo_1_1_empty_class.xml @@ -3,9 +3,9 @@ org::example::foo::EmptyClass - +This class is empty and is only visible in this package. -This class is empty and is only visible in this package. + diff --git a/contrib/neo_doxygen/tests/java-project/xml/interfaceorg_1_1example_1_1foo_1_1_c.xml b/contrib/neo_doxygen/tests/java-project/xml/interfaceorg_1_1example_1_1foo_1_1_c.xml index aec6243..09b577a 100644 --- a/contrib/neo_doxygen/tests/java-project/xml/interfaceorg_1_1example_1_1foo_1_1_c.xml +++ b/contrib/neo_doxygen/tests/java-project/xml/interfaceorg_1_1example_1_1foo_1_1_c.xml @@ -11,9 +11,9 @@ THE_ANSWER = 42L - +“Answer to the Ultimate Question of Life, the Universe, and Everything. -“Answer to the Ultimate Question of Life, the Universe, and Everything.“ +“ @@ -27,18 +27,18 @@ baz baz - +A function with implicit modifiers. -A function with implicit modifiers. + - +An interface. -An interface + diff --git a/contrib/neo_doxygen/tests/root-namespace/Doxyfile b/contrib/neo_doxygen/tests/root-namespace/Doxyfile index bd06324..46601c2 100644 --- a/contrib/neo_doxygen/tests/root-namespace/Doxyfile +++ b/contrib/neo_doxygen/tests/root-namespace/Doxyfile @@ -187,7 +187,7 @@ SHORT_NAMES = NO # description.) # The default value is: NO. -JAVADOC_AUTOBRIEF = NO +JAVADOC_AUTOBRIEF = YES # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If diff --git a/contrib/neo_doxygen/tests/root-namespace/xml/class_foo.xml b/contrib/neo_doxygen/tests/root-namespace/xml/class_foo.xml index 3ee1155..86405c8 100644 --- a/contrib/neo_doxygen/tests/root-namespace/xml/class_foo.xml +++ b/contrib/neo_doxygen/tests/root-namespace/xml/class_foo.xml @@ -3,9 +3,9 @@ Foo - +A class in the root namespace. -A class in the root namespace + diff --git a/contrib/nitcc/src/autom.nit b/contrib/nitcc/src/autom.nit index 5ece999..1367e0d 100644 --- a/contrib/nitcc/src/autom.nit +++ b/contrib/nitcc/src/autom.nit @@ -655,7 +655,8 @@ private class DFAGenerator i += 1 end - add "# Lexer generated by nitcc for the grammar {name}" + add "# Lexer generated by nitcc for the grammar {name}\n" + add "module {name}_lexer is no_warning \"missing-doc\"\n" add("import nitcc_runtime\n") var p = parser diff --git a/contrib/nitcc/src/grammar.nit b/contrib/nitcc/src/grammar.nit index eea312f..b9ac2f8 100644 --- a/contrib/nitcc/src/grammar.nit +++ b/contrib/nitcc/src/grammar.nit @@ -640,6 +640,7 @@ private class Generator var gram = autom.grammar add "# Parser generated by nitcc for the grammar {name}" + add "module {name}_parser is no_warning(\"missing-doc\",\"old-init\")" add "import nitcc_runtime" add "class Parser_{name}" diff --git a/contrib/nitcc/src/nitcc.nit b/contrib/nitcc/src/nitcc.nit index 562f5cb..0cc9d2a 100644 --- a/contrib/nitcc/src/nitcc.nit +++ b/contrib/nitcc/src/nitcc.nit @@ -144,9 +144,14 @@ lr.gen_to_nit("{name}_parser.nit", name) f = new OFStream.open("{name}_test_parser.nit") f.write """# Generated by nitcc for the language {{{name}}} + +# Standalone parser tester for the language {{{name}}} +module {{{name}}}_test_parser import nitcc_runtime import {{{name}}}_lexer import {{{name}}}_parser + +# Class to test the parser for the language {{{name}}} class TestParser_{{{name}}} super TestParser redef fun name do return \"{{{name}}}\" diff --git a/contrib/nitiwiki/src/wiki_base.nit b/contrib/nitiwiki/src/wiki_base.nit index d5db709..3dec44f 100644 --- a/contrib/nitiwiki/src/wiki_base.nit +++ b/contrib/nitiwiki/src/wiki_base.nit @@ -54,6 +54,7 @@ class Nitiwiki sys.system "rsync -vr --delete {root}/ {config.rsync_dir}" end + # Pull data from git repository. fun fetch do sys.system "git pull {config.git_origin} {config.git_branch}" end @@ -211,6 +212,9 @@ class Nitiwiki return path.simplify_path end + # Transform an id style name into a pretty printed name. + # + # Used to translate ids in beautiful page names. fun pretty_name(name: String): String do name = name.replace("_", " ") name = name.capitalized diff --git a/contrib/nitiwiki/src/wiki_html.nit b/contrib/nitiwiki/src/wiki_html.nit index 266e1d9..89546ce 100644 --- a/contrib/nitiwiki/src/wiki_html.nit +++ b/contrib/nitiwiki/src/wiki_html.nit @@ -81,6 +81,7 @@ redef class WikiSection end var index = self.index if index isa WikiSectionIndex then + wiki.message("Render auto-index for section {out_path}", 1) index.is_dirty = true add_child index end @@ -110,7 +111,7 @@ redef class WikiSection for child in children.values do if child isa WikiArticle and child.is_index then return child end - return new WikiSectionIndex(wiki, self) + return new WikiSectionIndex(wiki, "index", self) end redef fun tpl_link do return index.tpl_link @@ -322,10 +323,9 @@ class WikiSectionIndex # The section described by `self`. var section: WikiSection - init(wiki: Nitiwiki, section: WikiSection) do - super(wiki, "index") - self.section = section - end + redef fun title do return section.title + + redef fun url do return section.url redef var is_dirty = false @@ -341,11 +341,19 @@ end class TplArticle super Template + # Article title. var title: nullable Streamable = null + + # Article HTML body. var body: nullable Streamable = null + + # Sidebar of this article (if any). var sidebar: nullable TplSidebar = null + + # Breadcrumbs from wiki root to this article. var breadcrumbs: nullable TplBreadcrumbs = null + # Init `self` with a `title`. init with_title(title: Streamable) do self.title = title end diff --git a/contrib/nitiwiki/tests/res/nitiwiki_render.res b/contrib/nitiwiki/tests/res/nitiwiki_render.res index adf25a7..25f17ab 100644 --- a/contrib/nitiwiki/tests/res/nitiwiki_render.res +++ b/contrib/nitiwiki/tests/res/nitiwiki_render.res @@ -1 +1,6 @@ Render section out +Render section out/sec1 +Render section out/sec2 +Render auto-index for section out/sec2 +Render section out/sec2/sub-sec21 +Render section out/sec2/sub-sec22 diff --git a/contrib/nitiwiki/tests/res/nitiwiki_status.res b/contrib/nitiwiki/tests/res/nitiwiki_status.res index 936a764..9eb2262 100644 --- a/contrib/nitiwiki/tests/res/nitiwiki_status.res +++ b/contrib/nitiwiki/tests/res/nitiwiki_status.res @@ -6,5 +6,12 @@ url: http://localhost/ There is modified files: + pages + /pages/index.md + + pages/sec1 + + /pages/sec1/index.md + + pages/sec2 + + pages/sec2/sub-sec21 + + /pages/sec2/sub-sec21/index.md + + pages/sec2/sub-sec22 + + /pages/sec2/sub-sec22/index.md Use nitiwiki --render to render modified files diff --git a/contrib/pep8analysis/src/parser/lexer.nit b/contrib/pep8analysis/src/parser/lexer.nit index f5b910a..96f5430 100644 --- a/contrib/pep8analysis/src/parser/lexer.nit +++ b/contrib/pep8analysis/src/parser/lexer.nit @@ -1,6 +1,6 @@ # Lexer and its tokens. # This file was generated by SableCC (http://www.sablecc.org/). -module lexer +module lexer is no_warning("missing-doc", "old-init") intrude import parser_nodes private import tables diff --git a/contrib/pep8analysis/src/parser/parser.nit b/contrib/pep8analysis/src/parser/parser.nit index 2be9a9a..3fd9171 100644 --- a/contrib/pep8analysis/src/parser/parser.nit +++ b/contrib/pep8analysis/src/parser/parser.nit @@ -1,6 +1,6 @@ # Parser. # This file was generated by SableCC (http://www.sablecc.org/). -module parser +module parser is no_warning("missing-doc", "old-init") intrude import parser_prod import tables diff --git a/contrib/pep8analysis/src/parser/parser_nodes.nit b/contrib/pep8analysis/src/parser/parser_nodes.nit index 97530a4..6aac4bc 100644 --- a/contrib/pep8analysis/src/parser/parser_nodes.nit +++ b/contrib/pep8analysis/src/parser/parser_nodes.nit @@ -1,6 +1,6 @@ # Raw AST node hierarchy. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_nodes +module parser_nodes is no_warning("missing-doc", "old-init") import location diff --git a/contrib/pep8analysis/src/parser/parser_prod.nit b/contrib/pep8analysis/src/parser/parser_prod.nit index f7fb2f7..533cdb0 100644 --- a/contrib/pep8analysis/src/parser/parser_prod.nit +++ b/contrib/pep8analysis/src/parser/parser_prod.nit @@ -1,6 +1,6 @@ # Production AST nodes full definition. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_prod +module parser_prod is no_warning("missing-doc") import lexer intrude import parser_nodes diff --git a/contrib/pep8analysis/src/pep8analysis_web.nit b/contrib/pep8analysis/src/pep8analysis_web.nit index 18ee374..dacc9ff 100644 --- a/contrib/pep8analysis/src/pep8analysis_web.nit +++ b/contrib/pep8analysis/src/pep8analysis_web.nit @@ -46,7 +46,7 @@ in "C++" `{ redef class AnalysisManager - fun run(src: String) + fun run_web(src: String) do sys.suggest_garbage_collection @@ -109,7 +109,7 @@ redef class AnalysisManager end redef class NativeString - fun run_analysis do manager.run to_s + fun run_analysis do manager.run_web to_s end fun dummy_set_callbacks import NativeString.run_analysis in "C++" `{ diff --git a/examples/mnit_ballz/src/ballz_android.nit b/examples/mnit_ballz/src/ballz_android.nit index affb3e9..ace5787 100644 --- a/examples/mnit_ballz/src/ballz_android.nit +++ b/examples/mnit_ballz/src/ballz_android.nit @@ -19,6 +19,8 @@ module ballz_android is app_name("Ballz") end +import android::portrait + import game_logic redef class App diff --git a/examples/mnit_dino/src/dino_android.nit b/examples/mnit_dino/src/dino_android.nit index 1a3459a..aac6d47 100644 --- a/examples/mnit_dino/src/dino_android.nit +++ b/examples/mnit_dino/src/dino_android.nit @@ -21,6 +21,7 @@ end import dino import mnit_android +import android::portrait import android::vibration redef class ImageSet diff --git a/examples/mnit_moles/src/moles_android.nit b/examples/mnit_moles/src/moles_android.nit index ecc8b6e..ad2b1f1 100644 --- a/examples/mnit_moles/src/moles_android.nit +++ b/examples/mnit_moles/src/moles_android.nit @@ -16,8 +16,10 @@ module moles_android -import moles import mnit_android +import android::portrait + +import moles redef class Game redef fun columns do return 3 diff --git a/examples/mnit_simple/assets/sound.ogg b/examples/mnit_simple/assets/testsound.ogg similarity index 100% rename from examples/mnit_simple/assets/sound.ogg rename to examples/mnit_simple/assets/testsound.ogg diff --git a/examples/mnit_simple/res/raw/sound.ogg b/examples/mnit_simple/res/raw/testsound.ogg similarity index 100% rename from examples/mnit_simple/res/raw/sound.ogg rename to examples/mnit_simple/res/raw/testsound.ogg diff --git a/examples/mnit_simple/res/values/dimens.xml b/examples/mnit_simple/res/values/dimens.xml index da43c8f..fa1d020 100644 --- a/examples/mnit_simple/res/values/dimens.xml +++ b/examples/mnit_simple/res/values/dimens.xml @@ -1,5 +1,5 @@ - 25dp - 150dp - + 25px + 150px + diff --git a/examples/mnit_simple/src/simple_android.nit b/examples/mnit_simple/src/simple_android.nit index bf2bd23..b108f8b 100644 --- a/examples/mnit_simple/src/simple_android.nit +++ b/examples/mnit_simple/src/simple_android.nit @@ -18,10 +18,12 @@ module simple_android is android_manifest("""""") end -import simple import mnit_android +import android::portrait import serialization +import simple + in "Java" `{ import android.content.Context; import android.widget.Toast; diff --git a/examples/mnit_simple/src/test_audio.nit b/examples/mnit_simple/src/test_audio.nit index decf7c5..88a2812 100644 --- a/examples/mnit_simple/src/test_audio.nit +++ b/examples/mnit_simple/src/test_audio.nit @@ -21,27 +21,29 @@ import simple_android import android::audio redef class App - var soundsp: Sound - var soundmp: Sound + var soundsp: nullable Sound + var soundmp: nullable Sound + var test_assets = false + var test_ressources = true - redef fun init_window + redef fun window_created do super - - default_mediaplayer.reset - manage_audio_mode - - # Retrieve sound - soundsp = load_sound("sound.ogg") - soundmp = load_music("xylofon.ogg") + if test_assets then + soundsp = load_sound("testsound.ogg") + soundmp = load_music("xylofon.ogg") + end + if test_ressources then + soundsp = load_sound_from_res("testsound") + soundmp = load_music_from_res("xylofon") + end default_mediaplayer.looping = true - default_mediaplayer.prepare soundmp.play end redef fun input( ie ) do - if ie isa PointerEvent and ie.depressed then + if ie isa PointerEvent and ie.depressed then soundsp.play end return super diff --git a/lib/android/README.md b/lib/android/README.md new file mode 100644 index 0000000..1f4e659 --- /dev/null +++ b/lib/android/README.md @@ -0,0 +1,108 @@ +The `android` module provides support for the Android platform + +# Compilation for Android + +The compiler generates an APK file as the output when the `android` +module is imported by the compilation target. The path to the generated +file can be specified using the `-o` and `--dir` options. + +# Host system configuration + +To compile for Android, you must install the Android SDK and NDK. +The tools `android`, `ndk-build` and `ant` must be in your PATH. + +# Configure your Android application + +The `app.nit` framework and this project offers some services to +customized the generated Android application. + +## Module annotations + +* `app_version` specifies the version of the generated APK file. +It takes 3 arguments: the major, minor and revision version numbers. +The special function `git_revision` will use the prefix of the hash of the +latest git commit. The default version is 1.0. + + Example: `app_version(1, 0, git_revision)` + +* `app_name` takes a single argument, the visible name of the Android +application. By default, the compiler would use the name of the target +module. This name will be used as the name of the main activity and +as the launcher name. + + Example: `app_name "My App"` + +* `java_package` specifies the package used by the generated Java +classes and the APK file. Once the application is published, this +value should not be changed. By default, the compiler will use +the package `org.nitlanguage.{module_name}`. + +* Custom information can be added to the Android manifest file +using the annotations `android_manifest`, `android_manifest_application` +and `android_manifest_activity`. + + Example usage to specify an extra permission: + + ~~~ + android_manifest """""" + ~~~ + +* The API version target can be specified with `min_api_version`, +`max_api_version` and `target_api_version`. These take a single +integer as argument. They are applied in the Android manifest as +`minSdkVesion`, `targetSdkVersion` and `maxSdkVersion`. + + See http://developer.android.com/guide/topics/manifest/uses-sdk-element.html + +## Project entry points + +Importing `android::landscape` or `android::portrait` locks the generated +application in the specified orientation. This can be useful for games and +other multimedia applications. + +# Compilation modes + +There are two compilation modes for the Android platform, debug and release. +Theses modes are also applied to the generated Android projects. +The compilation mode is specified as an argument to `nitc`, only +`--release` can be specified as debug is the default behavior. + +## Debug mode + +Debug mode enables compiling to an APK file without handling signing keys +and their password. The APK file can be installed to a local device with +USB debugging enabled, but it cannot be published on the Play Store. + +By default, `nitc` will compile Android applications in debug mode. + +## Release mode + +Building in release mode will use your private key to sign the +APK file, it can then be published on the Play Store. + +1. Have a keystore with a valid key to sign your APK file. + + To create a new keystore, avoid using the default values of `jarsigner` +as they change between versions of the Java SDK. You should instead use a +command similar to the following, replacing `KEYSTORE_PATH` and `KEY_ALIAS` +with the desired values. + + ~~~ + keytool -genkey -keystore KEYSTORE_PATH -alias KEY_ALIAS -sigalg MD5withRSA -keyalg RSA -keysize 1024 -validity 10000 + ~~~ + +2. Set the environment variables used by `nitc`: `KEYSTORE`, `KEY_ALIAS` and +optionally `TSA_SERVER`. These settings can be set in a startup script such as +`~/.bashrc` or in a local Makefile. + + You can use the following commands by replacing the right hand values +to your own configuration. + + ~~~ + export KEYSTORE=keystore_path + export KEY_ALIAS=key_alias + export TSA_SERVER=timestamp_authority_server_url # Optional + ~~~ + +3. Call `nitc` with the `--release` options. You will be prompted for the +required passwords as needed by `jarsigner`. diff --git a/lib/android/android.nit b/lib/android/android.nit index 5523f30..584b89b 100644 --- a/lib/android/android.nit +++ b/lib/android/android.nit @@ -16,10 +16,6 @@ # Android services and implementation of app.nit # -# To use this module and compile for Android, you must install the -# Android SDK (with API level 10) and NDK (with the API level 9). -# The tools `android`, `ndk-build` and `ant` must be in your PATH. -# # This module provides basic logging facilities, advanced logging can be # achieved by importing `android::log`. module android diff --git a/lib/android/assets_and_resources.nit b/lib/android/assets_and_resources.nit index 82ae3d1..8195e2c 100644 --- a/lib/android/assets_and_resources.nit +++ b/lib/android/assets_and_resources.nit @@ -102,6 +102,13 @@ extern class NativeAssetManager in "Java" `{ android.content.res.AssetManager `} } return afd; `} + + # HACK for bug #845 + redef fun new_global_ref import sys, Sys.jni_env `{ + Sys sys = NativeResources_sys(recv); + JNIEnv *env = Sys_jni_env(sys); + return (*env)->NewGlobalRef(env, recv); + `} end # Assets manager using a `NativeAssetManager` to manage android assets @@ -138,14 +145,18 @@ class AssetManager # Open an asset using ACCESS_STREAMING mode, returning a NativeInputStream fun open(file_name: String): NativeInputStream do + sys.jni_env.push_local_frame(1) var return_value = native_assets_manager.open(file_name.to_java_string) + sys.jni_env.pop_local_frame return return_value end # Open an asset using it's name and returning a NativeAssetFileDescriptor # `file_name` is fun open_fd(file_name: String): NativeAssetFileDescriptor do - var return_value = native_assets_manager.open_fd(file_name.to_java_string) + sys.jni_env.push_local_frame(1) + var return_value = native_assets_manager.open_fd(file_name.to_java_string).new_global_ref + sys.jni_env.pop_local_frame return return_value end @@ -158,7 +169,7 @@ class AssetManager # Return a bitmap from the assets fun bitmap(name: String): NativeBitmap do sys.jni_env.push_local_frame(1) - var return_value = new NativeBitmap.from_stream(native_assets_manager.open(name.to_java_string)) + var return_value = new NativeBitmap.from_stream(native_assets_manager.open(name.to_java_string)).new_global_ref sys.jni_env.pop_local_frame return return_value end @@ -291,6 +302,14 @@ extern class NativeBitmap in "Java" `{ android.graphics.Bitmap `} new from_resources(res: NativeResources, id: Int) in "Java" `{ return BitmapFactory.decodeResource(res, (int)id); `} fun width: Int in "Java" `{ return recv.getWidth(); `} fun height: Int in "Java" `{ return recv.getHeight(); `} + + # HACK for bug #845 + redef fun new_global_ref import sys, Sys.jni_env `{ + Sys sys = NativeResources_sys(recv); + JNIEnv *env = Sys_jni_env(sys); + return (*env)->NewGlobalRef(env, recv); + `} + end # Android AssetFileDescriptor, can be retrieve by AssetManager and used to load a sound in a SoundPool @@ -337,6 +356,13 @@ extern class NativeAssetFileDescriptor in "Java" `{ android.content.res.AssetFil fun length: Int in "Java" `{ return (int)recv.getLength(); `} fun start_offset: Int in "Java" `{ return (int)recv.getStartOffset(); `} redef fun to_s: String import JavaString.to_s in "Java" `{ return JavaString_to_s(recv.toString()); `} + + # HACK for bug #845 + redef fun new_global_ref import sys, Sys.jni_env `{ + Sys sys = NativeResources_sys(recv); + JNIEnv *env = Sys_jni_env(sys); + return (*env)->NewGlobalRef(env, recv); + `} end # Native class representing something drawable, can be retrieved from the resources diff --git a/lib/android/audio.nit b/lib/android/audio.nit index 366c7d4..2301bd0 100644 --- a/lib/android/audio.nit +++ b/lib/android/audio.nit @@ -1,4 +1,4 @@ -# this file is part of NIT ( http://www.nitlanguage.org ). +# This file is part of NIT ( http://www.nitlanguage.org ). # # Copyright 2014 Romain Chanoir # @@ -14,54 +14,88 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Android audio services +# Android audio services, wraps a part of android audio API # -# You can get a sound by loading it with a `SoundPool` or a `MediaPlayer` -# the recommended way to load a sound is by it's resource ID or it's name -# other ways are for advanced use +# For this example, the sounds "test_sound" and "test_music" are located in the "assets/sounds" folder, +# they both have ".ogg" extension. "test_sound" is a short sound and "test_music" a music track +# ~~~nitish +# # Note that you need to specify the path from "assets" folder and the extension +# var s = app.load_sound("sounds/test_sound.ogg") +# var m = app.load_music("sounds/test_music.ogg") +# s.play +# m.play +# ~~~ +# +# Now, the sounds are in "res/raw" +# ~~~nitish +# s = app.load_sound_from_res("test_sound") +# m = app.load_music_from_res("test_sound") +# s.play +# m.play +# ~~~ +# +# See http://developer.android.com/reference/android/media/package-summary.html for more infos module audio import java import java::io import assets_and_resources -import app +import app::audio in "Java" `{ import android.media.MediaPlayer; import android.media.SoundPool; import java.io.IOException; import android.media.AudioManager; + import android.media.AudioManager.OnAudioFocusChangeListener; import android.content.Context; import android.util.Log; `} +# FIXME: This listener is not working at the moment, but is needed to gain or give up the audio focus +# of the application +in "Java inner" `{ + static OnAudioFocusChangeListener afChangeListener = new OnAudioFocusChangeListener() { + public void onAudioFocusChange(int focusChange) { + if(focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) { + }else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) { + }else if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { + } + } + }; +`} + # AudioManager of the application, used to manage the audio mode extern class NativeAudioManager in "Java" `{ android.media.AudioManager `} super JavaObject + # Current audio mode. + # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 ) fun mode: Int in "Java" `{ return recv.getMode(); `} + + # Sets the audio mode. + # ( MODE_NORMAL = 0, MODE_RINGTONE = 1, MODE_IN_CALL = 2 or MODE_IN_COMMUNICATION = 3 ) fun mode=(i: Int) in "Java" `{ recv.setMode((int)i); `} - fun wired_headset_on: Bool in "Java" `{ return recv.isWiredHeadsetOn(); `} - fun wired_headset_on=(b: Bool) in "Java" `{ recv.setWiredHeadsetOn(b); `} - fun speakerphone_on: Bool in "Java" `{ return recv.isSpeakerphoneOn(); `} - fun speakerphone_on=(b: Bool) in "Java" `{ recv.setSpeakerphoneOn(b); `} - fun manage_audio_mode in "Java" `{ - recv.setMode(0); - if (recv.isWiredHeadsetOn()) { - recv.setSpeakerphoneOn(false); - } else { - recv.setSpeakerphoneOn(true); - } + + # Sends a request to obtain audio focus + fun request_audio_focus: Int in "Java" `{ + return recv.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); `} + # Gives up audio focus + fun abandon_audio_focus: Int in "Java" `{ return recv.abandonAudioFocus(afChangeListener); `} end # Media Player from Java, used to play long sounds or musics, not simultaneously # This is a low-level class, use `MediaPlater` instead -extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `} +private extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `} super JavaObject - new in "Java" `{ return new MediaPlayer(); `} + new in "Java" `{ + MediaPlayer mp = new MediaPlayer(); + mp.setAudioStreamType(AudioManager.STREAM_MUSIC); + return mp; + `} fun start in "Java" `{ recv.start(); `} fun prepare in "Java" `{ try { @@ -104,7 +138,7 @@ end # Sound Pool from Java, used to play sounds simultaneously # This is a low-level class, use `SoundPool`instead -extern class NativeSoundPool in "Java" `{ android.media.SoundPool `} +private extern class NativeSoundPool in "Java" `{ android.media.SoundPool `} super JavaObject new(max_streams, stream_type, src_quality: Int) in "Java" `{ @@ -165,7 +199,7 @@ class SoundPool return new SoundSP(null, nsoundpool.load_asset_fd(afd, priority), self) end - # Load the sound from it's resource id + # Load the sound from its resource id fun load_id(context: NativeActivity, id:Int): Sound do return new SoundSP(null, nsoundpool.load_id(context, id, priority), self) end @@ -184,7 +218,7 @@ class SoundPool return nsoundpool.play(id, left_volume, right_volume, priority, looping, rate) end - # Load a sound by it's name in the resources, the sound must be in the `res/raw` folder + # Load a sound by its name in the resources, the sound must be in the `res/raw` folder fun load_name(resource_manager: ResourcesManager, context: NativeActivity, sound: String): Sound do var id = resource_manager.raw_id(sound) return new SoundSP(id, nsoundpool.load_id(context, id, priority), self) @@ -231,8 +265,11 @@ end class MediaPlayer private var nmedia_player: NativeMediaPlayer is noinit + # Used to control the state of the mediaplayer + private var is_prepared = false is writable + # The sound associated with this mediaplayer - var sound: nullable Sound = null + var sound: nullable Sound = null is writable # Create a new MediaPlayer, but no sound is attached, you'll need # to use `load_sound` before using it @@ -249,20 +286,21 @@ class MediaPlayer fun load_sound(id: Int, context: NativeActivity): Sound do self.nmedia_player = self.nmedia_player.create(context, id) self.sound = new SoundMP(id, self) + self.is_prepared = true return self.sound.as(not null) end - # Starts or resume playback + # Starts or resumes playback # REQUIRE `self.sound != null` fun start do - assert sound != null + if not is_prepared then prepare nmedia_player.start end # Stops playback after playback has been stopped or paused # REQUIRE `self.sound != null` fun stop do - assert sound != null + is_prepared = false nmedia_player.stop end @@ -271,6 +309,7 @@ class MediaPlayer fun prepare do assert sound != null nmedia_player.prepare + is_prepared = true end # Pauses playback @@ -292,7 +331,7 @@ class MediaPlayer # Reset MediaPlayer to its initial state fun reset do nmedia_player.reset - # Sets the datasource (file-pathor http/rtsp URL) to use + # Sets the datasource (file-path or http/rtsp URL) to use fun data_source(path: String): Sound do sys.jni_env.push_local_frame(1) nmedia_player.data_source_path(path.to_java_string) @@ -323,14 +362,10 @@ class MediaPlayer fun stream_type=(stream_type: Int) do nmedia_player.stream_type = stream_type end -# Represents an android sound that can be played by a SoundPool or a MediaPlayer -# The only way to get a sound is by a MediaPlayer, a SoundPool, or the App -abstract class Sound +redef class Sound - # The resource ID of this sound + # Resource ID of this sound var id: nullable Int - - fun play is abstract end # Sound implemented with a SoundPool @@ -350,6 +385,8 @@ class SoundSP end redef fun play do soundpool.play(soundpool_id) + redef fun pause do soundpool.pause_stream(soundpool_id) + redef fun resume do soundpool.resume(soundpool_id) end # Sound Implemented with a MediaPlayer @@ -364,53 +401,81 @@ class SoundMP self.media_player = media_player end - redef fun play do self.media_player.start + redef fun play do media_player.start + redef fun pause do media_player.pause + redef fun resume do play end redef class App + # Sounds handled by the application, when you load a sound, it's added to this list. + # This array is used in `pause` and `resume` + private var sounds = new Array[Sound] + + # Returns the default MediaPlayer of the application. + # When you load a music, it goes in this MediaPlayer. + # Use it for advanced sound management fun default_mediaplayer: MediaPlayer is cached do return new MediaPlayer + + # Returns the default MediaPlayer of the application. + # When you load a short sound (not a music), it's added to this soundpool. + # Use it for advanced sound management. fun default_soundpool: SoundPool is cached do return new SoundPool # Get the native audio manager - private fun audio_manager: NativeAudioManager import native_activity in "Java" `{ + fun audio_manager: NativeAudioManager import native_activity in "Java" `{ return (AudioManager)App_native_activity(recv).getSystemService(Context.AUDIO_SERVICE); `} - # Manages whether the app sound need to be in headphones mode or not - # TODO: this method is not ideal, need to find a better way - fun manage_audio_mode import native_activity in "Java" `{ - AudioManager manager = (AudioManager)App_native_activity(recv).getSystemService(Context.AUDIO_SERVICE); - manager.setMode(0); - if (manager.isWiredHeadsetOn()) { - manager.setSpeakerphoneOn(false); - } else { - manager.setSpeakerphoneOn(true); - } + # Sets the stream of the app to STREAM_MUSIC. + # STREAM_MUSIC is the default stream used by android apps. + private fun manage_audio_stream import native_activity, native_app_glue in "Java" `{ + App_native_activity(recv).setVolumeControlStream(AudioManager.STREAM_MUSIC); `} - # Retrieve a sound with a soundpool in the `assets` folder using it's name + # Retrieves a sound with a soundpool in the `assets` folder using its name. # Used to play short songs, can play multiple sounds simultaneously - fun load_sound(path: String): Sound do - return default_soundpool.load_asset_fd(asset_manager.open_fd(path)) + redef fun load_sound(path: String): Sound do + return add_to_sounds(default_soundpool.load_asset_fd(asset_manager.open_fd(path))) end - # Retrieve a music with a media player in the `assets`folder using it's name + # Retrieves a music with a media player in the `assets` folder using its name. # Used to play long sounds or musics, can't play multiple sounds simultaneously fun load_music(path: String): Sound do var fd = asset_manager.open_fd(path) - var sound = default_mediaplayer.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length) - return sound + return add_to_sounds(default_mediaplayer.data_source_fd(fd.file_descriptor, fd.start_offset, fd.length)) end - # same as `load_sound` but load the sound from the `res\raw` folder + # Same as `load_sound` but load the sound from the `res/raw` folder fun load_sound_from_res(sound_name: String): Sound do - return default_soundpool.load_name(resource_manager,self.native_activity, sound_name) + return add_to_sounds(default_soundpool.load_name(resource_manager,self.native_activity, sound_name)) end - # same as `load_music` but load the sound from the `res\raw` folder + # Same as `load_music` but load the sound from the `res/raw` folder fun load_music_from_res(music: String): Sound do - return default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity) + return add_to_sounds(default_mediaplayer.load_sound(resource_manager.raw_id(music), self.native_activity)) end + # Factorizes `sounds.add` to use it in `load_music`, `load_sound`, `load_music_from_res` and `load_sound_from_res` + private fun add_to_sounds(sound: Sound): Sound do + sounds.add(sound) + return sound + end + + redef fun pause do + for s in sounds do s.pause + audio_manager.abandon_audio_focus + end + + redef fun init_window do + super + audio_manager.request_audio_focus + manage_audio_stream + end + + redef fun resume do + super + audio_manager.request_audio_focus + for s in sounds do s.resume + end end diff --git a/lib/android/cardboard.nit b/lib/android/cardboard.nit new file mode 100644 index 0000000..30ec5eb --- /dev/null +++ b/lib/android/cardboard.nit @@ -0,0 +1,70 @@ +# 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. + +# Services from the Google Cardboard SDK for virtual reality on Android +# +# Projects using this module should keep the `cardboard.jar` archive in the +# `libs` folder at the root of the project. +# +# External resources: +# * Download `cardboard.jar` from +# https://raw.githubusercontent.com/googlesamples/cardboard-java/master/CardboardSample/libs/cardboard.jar +# * Read about Cardboard at +# https://developers.google.com/cardboard/ +# * Find the Cardboard SDK documentation at +# https://developers.google.com/cardboard/android/latest/reference/com/google/vrtoolkit/cardboard/package-summary +module cardboard + +import java::collections +import native_app_glue + +in "Java" `{ + import com.google.vrtoolkit.cardboard.CardboardActivity; + import com.google.vrtoolkit.cardboard.sensors.HeadTracker; +`} + +# Provides head tracking information from the device IMU +# +# The corresponding Java class is no longer documented, but it is still useful. +extern class NativeHeadTracker in "Java" `{ com.google.vrtoolkit.cardboard.sensors.HeadTracker `} + super JavaObject + + # Instantiate a new `NativeHeadTracker` for the given `context` + new (context: NativeContext) in "Java" `{ + return HeadTracker.createFromContext(context); + `} + + # Start tracking head movement + fun start_tracking in "Java" `{ recv.startTracking(); `} + + # Stop tracking head movement + fun stop_tracking in "Java" `{ recv.stopTracking(); `} + + # Apply correction to the gyroscope values + fun gyro_bias=(matrix: JavaFloatArray) in "Java" `{ + recv.setGyroBias(matrix); + `} + + # Enable finer analysis using the neck as center of movement + fun neck_model_enabled=(value: Bool) in "Java" `{ + recv.setNeckModelEnabled(value); + `} + + # Fill `matrix` with the last rotation matrix calculated from head movements + # + # Require: matrix.length >= offset + 16 + fun last_head_view(matrix: JavaFloatArray, offset: Int) in "Java" `{ + recv.getLastHeadView(matrix, (int)offset); + `} +end diff --git a/lib/android/input_events.nit b/lib/android/input_events.nit new file mode 100644 index 0000000..c766a16 --- /dev/null +++ b/lib/android/input_events.nit @@ -0,0 +1,252 @@ +# This file is part of NIT (http://www.nitlanguage.org). +# +# Copyright 2012-2014 Alexis Laferrière +# +# 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. + +# Pointer and hardware key events +module input_events + +import mnit_input +import android + +in "C header" `{ + #include + #include + + #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__)) + #ifdef DEBUG + #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__)) + #else + #define LOGI(...) (void)0 + #endif +`} + +in "C" `{ + /* Handle inputs from the Android platform and sort them before + sending them in the Nit App */ + static int32_t mnit_handle_input(struct android_app* app, AInputEvent* event) { + App nit_app = app->userData; + LOGI("handle input %i", (int)pthread_self()); + if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { + LOGI("key"); + return App_native_input_key(nit_app, event); + } + else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { + LOGI("motion"); + return App_native_input_motion(nit_app, event); + } + + return 0; + } +`} + +private extern class NativeAndroidMotionEvent `{AInputEvent *`} + + fun pointers_count: Int `{ + return AMotionEvent_getPointerCount(recv); + `} + + # Did this motion event just started? + fun just_went_down: Bool `{ + return (AMotionEvent_getAction(recv) & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN; + `} + + fun edge: Int `{ + return AMotionEvent_getEdgeFlags(recv); + `} + + # Get the non-primary pointer id that just went down (returns -1 or > 0) + fun index_down_pointer: Int `{ + int a = AMotionEvent_getAction(recv); + if ((a & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) + return (a & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; + else return -1; + `} + + fun action: AMotionEventAction `{ return AMotionEvent_getAction(recv); `} +end + +private extern class AMotionEventAction `{ int32_t `} + fun action: Int `{ return recv & AMOTION_EVENT_ACTION_MASK; `} + + fun is_down: Bool do return action == 0 + fun is_up: Bool do return action == 1 + fun is_move: Bool do return action == 2 + fun is_cancel: Bool do return action == 3 + fun is_outside: Bool do return action == 4 + fun is_pointer_down: Bool do return action == 5 + fun is_pointer_up: Bool do return action == 6 +end + +# An input event on Android +interface AndroidInputEvent + super InputEvent +end + +# A motion event concerning a single or more `pointers` +class AndroidMotionEvent + super AndroidInputEvent + super MotionEvent + + private var native: NativeAndroidMotionEvent + + private var pointers_cache: nullable Array[AndroidPointerEvent] = null + + # Pointers (or fingers) composing this motion event + fun pointers: Array[AndroidPointerEvent] + do + if pointers_cache != null then + return pointers_cache.as(not null) + else + var pointers = new Array[AndroidPointerEvent] + var pointers_count = native.pointers_count + for i in [0 .. pointers_count [do + var pointer_event = new AndroidPointerEvent(self, i) + pointers.add(pointer_event) + end + pointers_cache = pointers + return pointers + end + end + + redef fun just_went_down: Bool do return native.just_went_down + + # Was the top edge of the screen intersected by this event? + fun touch_to_edge: Bool do return native.edge == 1 + + # Was the bottom edge of the screen intersected by this event? + fun touch_bottom_edge: Bool do return native.edge == 2 + + # Was the left edge of the screen intersected by this event? + fun touch_left_edge: Bool do return native.edge == 4 + + # Was the right edge of the screen intersected by this event? + fun touch_right_edge: Bool do return native.edge == 8 + + redef fun down_pointer: nullable AndroidPointerEvent + do + if just_went_down then + # The primary pointer went down + return pointers[0] + end + + var i = native.index_down_pointer + if i > 0 then + # A secondary pointer went down + return pointers[i] + else + return null + end + end +end + +# A pointer event +class AndroidPointerEvent + super PointerEvent + super AndroidInputEvent + + private var motion_event: AndroidMotionEvent + + private var pointer_id: Int + + redef fun x: Float do return native_x(motion_event.native, pointer_id) + + private fun native_x(motion_event: NativeAndroidMotionEvent, pointer_id: Int): Float `{ + return AMotionEvent_getX(motion_event, pointer_id); + `} + + redef fun y: Float do return native_y(motion_event.native, pointer_id) + + private fun native_y(motion_event: NativeAndroidMotionEvent, pointer_id: Int): Float `{ + return AMotionEvent_getY(motion_event, pointer_id); + `} + + # Pressure applied by this pointer + fun pressure: Float do return native_pressure(motion_event.native, pointer_id) + + private fun native_pressure(motion_event: NativeAndroidMotionEvent, pointer_id: Int): Float `{ + return AMotionEvent_getPressure(motion_event, pointer_id); + `} + + redef fun pressed + do + var action = motion_event.native.action + return action.is_down or action.is_move + end + + redef fun depressed do return not pressed + + # Does this pointer just began touching the screen? + fun just_went_down: Bool + do + return motion_event.down_pointer == self + end +end + +# An hardware key event +extern class AndroidKeyEvent `{AInputEvent *`} + super KeyEvent + super AndroidInputEvent + + private fun action: Int `{ return AKeyEvent_getAction(recv); `} + + redef fun is_down: Bool do return action == 0 + redef fun is_up: Bool do return action == 1 + + # Hardware code of the key raising this event + fun key_code: Int `{ return AKeyEvent_getKeyCode(recv); `} + + redef fun to_c `{ + int code = AKeyEvent_getKeyCode(recv); + if (code >= AKEYCODE_0 && code <= AKEYCODE_9) + return '0'+code-AKEYCODE_0; + if (code >= AKEYCODE_A && code <= AKEYCODE_Z) + return 'a'+code-AKEYCODE_A; + return 0; + `} + + # Was this event raised by the back key? + fun is_back_key: Bool do return key_code == 4 + + # Was this event raised by the menu key? + fun is_menu_key: Bool do return key_code == 82 + + # Was this event raised by the search key? + fun is_search_key: Bool do return key_code == 84 + + # Was this event raised by the volume up key? + fun is_volume_up: Bool do return key_code == 24 + + # Was this event raised by the volume down key? + fun is_volume_down: Bool do return key_code == 25 +end + +redef class App + + redef fun init_window + do + set_as_input_handler native_app_glue + super + end + + private fun set_as_input_handler(app_glue: NativeAppGlue) + import native_input_key, native_input_motion `{ + app_glue->onInputEvent = mnit_handle_input; + `} + + # these are used as a callback from native to type incoming events + private fun native_input_key(event: AndroidKeyEvent): Bool is abstract + + private fun native_input_motion(event: NativeAndroidMotionEvent): Bool is abstract +end diff --git a/lib/android/landscape.nit b/lib/android/landscape.nit new file mode 100644 index 0000000..a3fceaf --- /dev/null +++ b/lib/android/landscape.nit @@ -0,0 +1,20 @@ +# 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. + +# Config to set the landscape orientation +module landscape is android_manifest_activity """ + android:screenOrientation="landscape" +""" + +import platform diff --git a/lib/android/portrait.nit b/lib/android/portrait.nit new file mode 100644 index 0000000..e8f5720 --- /dev/null +++ b/lib/android/portrait.nit @@ -0,0 +1,20 @@ +# 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. + +# Config to set the portrait orientation +module portrait is android_manifest_activity """ + android:screenOrientation="portrait" +""" + +import platform diff --git a/lib/app/audio.nit b/lib/app/audio.nit new file mode 100644 index 0000000..de68bb9 --- /dev/null +++ b/lib/app/audio.nit @@ -0,0 +1,43 @@ +# This file is part of NIT ( http://www.nitlanguage.org ). +# +# Copyright 2014 Romain Chanoir +# +# 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. + +# App audio abstraction +# +# Once the application has started (after `App.setup`) +# use `App.load_sound` to get a sound +# then `Sound.play` to play it +module audio + +import app_base + +# Abstraction of a sound +abstract class Sound + + # Plays the sound + fun play is abstract + + # Pauses the sound + fun pause is abstract + + # Resumes the sound + fun resume is abstract +end + +redef class App + + # Loads a sound from the assets of the app, returns `null` if the loading failed + fun load_sound(name: String): nullable Sound do return null +end diff --git a/lib/array_debug.nit b/lib/array_debug.nit index f112d7a..4d2edb4 100644 --- a/lib/array_debug.nit +++ b/lib/array_debug.nit @@ -21,6 +21,7 @@ redef class Sys # Keeps the average length of an Array when calling to_s var arr_len = new Counter[Int] + # Compute the average array length. fun avg_arr_len: Float do var total = 0 var sum = 0 @@ -31,6 +32,7 @@ redef class Sys return sum.to_f / total.to_f end + # Compute the average string length. fun avg_s_len: Float do var total = 0 var sum = 0 @@ -41,6 +43,7 @@ redef class Sys return sum.to_f / total.to_f end + # Display statistics in standard output. fun print_stats do if arr_len.sum == 0 then print "*** No Array stats ***" diff --git a/lib/bucketed_game.nit b/lib/bucketed_game.nit index 703ff5c..ae0d792 100644 --- a/lib/bucketed_game.nit +++ b/lib/bucketed_game.nit @@ -179,8 +179,6 @@ class Game # but cannot be used to add new Events. var last_turn: nullable ThinGameTurn[G] = null - init do end - # Execute and return a new GameTurn. # # This method calls `do_pre_turn` before executing the GameTurn diff --git a/lib/c.nit b/lib/c.nit index d94ae8e..ae1b875 100644 --- a/lib/c.nit +++ b/lib/c.nit @@ -70,10 +70,10 @@ extern class NativeCArray `{ void * `} type E: nullable Object # Get element at `index`. - fun [](index: E): E is abstract + fun [](index: Int): E is abstract # Set `val` at `index`. - fun []=(index: E, val: E) is abstract + fun []=(index: Int, val: E) is abstract # Return pointer to the address to the second element of this array # diff --git a/lib/cocoa/examples/cocoa_message_box.nit b/lib/cocoa/examples/cocoa_message_box.nit index 72e35d7..224c7ab 100644 --- a/lib/cocoa/examples/cocoa_message_box.nit +++ b/lib/cocoa/examples/cocoa_message_box.nit @@ -19,10 +19,6 @@ module cocoa_message_box import cocoa -in "ObjC" `{ - #import -`} - fun dialog in "ObjC" `{ NSAlert *alert = [[[NSAlert alloc] init] autorelease]; [alert setMessageText:@"Hello world!"]; diff --git a/lib/cocoa/examples/hello_cocoa.nit b/lib/cocoa/examples/hello_cocoa.nit index 3a2b168..960b001 100644 --- a/lib/cocoa/examples/hello_cocoa.nit +++ b/lib/cocoa/examples/hello_cocoa.nit @@ -19,10 +19,6 @@ module hello_cocoa import cocoa::foundation -in "ObjC" `{ - #import -`} - # Print `"Hello world!"` to the log fun hello_world in "ObjC" `{ @autoreleasepool { diff --git a/lib/curl/curl.nit b/lib/curl/curl.nit index bca105c..c087642 100644 --- a/lib/curl/curl.nit +++ b/lib/curl/curl.nit @@ -85,8 +85,7 @@ class CurlHTTPRequest curl.prim_curl.easy_setopt(new CURLOption.user_agent, name) end - init (url: String, curl: nullable Curl) - do + init (url: String, curl: nullable Curl) is old_style_init do self.url = url self.curl = curl end @@ -211,8 +210,7 @@ class CurlMailRequest var body: nullable String = "" is writable private var supported_outgoing_protocol: Array[String] = ["smtp", "smtps"] - init (curl: nullable Curl) - do + init (curl: nullable Curl) is old_style_init do self.curl = curl end @@ -351,12 +349,6 @@ class CurlResponseFailed var error_code: Int var error_msg: String - - init (err_code: Int, err_msg: String) - do - self.error_code = err_code - self.error_msg = err_msg - end end # Success Abstract Response Success Class @@ -467,7 +459,7 @@ class HeaderMapIterator super MapIterator[String, String] private var iterator: Iterator[Couple[String, String]] - init(map: HeaderMap) do self.iterator = map.arr.iterator + init(map: HeaderMap) is old_style_init do self.iterator = map.arr.iterator redef fun is_ok do return self.iterator.is_ok redef fun next do self.iterator.next diff --git a/lib/filter_stream.nit b/lib/filter_stream.nit index 2797b67..8fb57fd 100644 --- a/lib/filter_stream.nit +++ b/lib/filter_stream.nit @@ -5,7 +5,7 @@ # # 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 +# 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 @@ -83,8 +83,8 @@ class StreamCat do _streams = streams.iterator end - init(streams: IStream ...) - do + + init(streams: IStream ...) is old_style_init do _streams = streams.iterator end end @@ -133,8 +133,7 @@ class StreamDemux _streams = streams end - init(streams: OStream ...) - do + init(streams: OStream ...) is old_style_init do _streams = streams end end diff --git a/lib/github/events.nit b/lib/github/events.nit new file mode 100644 index 0000000..b6e5af3 --- /dev/null +++ b/lib/github/events.nit @@ -0,0 +1,302 @@ +# 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. + +# Events are emitted by Github Hooks. +# +# See +module events + +import api + +# Github event stub. +class GithubEvent + + # Github API client. + var api: GithubAPI + + # Json representation of `self`. + var json: JsonObject is noinit + + init do + json = new JsonObject + end + + # Init `self` from a `json` object. + init from_json(api: GithubAPI, json: JsonObject) do + self.api = api + self.json = json + end + + # Action performed by the event. + fun action: String do return json["action"].to_s + + # Repo where this event occured. + fun repo: Repo do + return new Repo.from_json(api, json["repository"].as(JsonObject)) + end +end + +# Triggered when a commit comment is created. +class CommitCommentEvent + super GithubEvent + + # The `Comment` itself. + fun comment: CommitComment do + return new CommitComment.from_json(api, repo, json["comment"].as(JsonObject)) + end +end + +# Triggered when a repository, branch, or tag is created. +class CreateEvent + super GithubEvent + + # Oject type that was created. + # + # Can be one of `repository`, `branch`, or `tag`. + fun ref_type: String do return json["ref_type"].to_s + + # Git ref (or null if only a repository was created). + fun ref: String do return json["ref"].to_s + + # Name of the repo's default branch (usually master). + fun master_branch: String do return json["master_branch"].to_s + + # Repo's current description. + fun description: String do return json["description"].to_s +end + +# Triggered when a branch or a tag is deleted. +class DeleteEvent + super GithubEvent + + # Object type that was deleted. + # + # Can be one of `repository`, `branch`, or `tag`. + fun ref_type: String do return json["ref_type"].to_s + + # Git ref (or null if only a repository was deleted). + fun ref: String do return json["ref"].to_s +end + +# Triggered when a new snapshot is deployed. +# +# Deployement are mainly used with integration testing servers. +class DeploymentEvent + super GithubEvent + + # Commit SHA for which this deployment was created. + fun sha: String do return json["sha"].to_s + + # Name of repository for this deployment, formatted as :owner/:repo. + fun name: String do return json["name"].to_s + + # Optional extra information for this deployment. + fun payload: nullable String do + if not json.has_key("payload") then return null + return json["payload"].to_s + end + + # Optional environment to deploy to. + # Default: "production" + fun environment: nullable String do + if not json.has_key("environment") then return null + return json["environment"].to_s + end + + # Optional human-readable description added to the deployment. + fun description: nullable String do + if not json.has_key("description") then return null + return json["description"].to_s + end +end + +# Triggered when a deployement's status changes. +class DeploymentStatusEvent + super GithubEvent + + # New deployment state. + # + # Can be `pending`, `success`, `failure`, or `error`. + fun state: String do return json["state"].to_s + + # Optional link added to the status. + fun target_url: nullable String do + if not json.has_key("target_url") then return null + return json["target_url"].to_s + end + + # Deployment hash that this status is associated with. + fun deployment: String do return json["deployment"].to_s + + # Optional human-readable description added to the status. + fun description: nullable String do + if not json.has_key("description") then return null + return json["description"].to_s + end +end + +# Triggered when a user forks a repository. +class ForkEvent + super GithubEvent + + # Created repository. + fun forkee: Repo do return new Repo.from_json(api, json["forkee"].as(JsonObject)) +end + +# Triggered when an issue comment is created. +class IssueCommentEvent + super GithubEvent + + # `Issue` the comment belongs to. + fun issue: Issue do + return new Issue.from_json(api, repo, json["issue"].as(JsonObject)) + end + + # The `Comment` itself. + fun comment: IssueComment do + return new IssueComment.from_json(api, repo, json["comment"].as(JsonObject)) + end +end + +# Triggered when an event occurs on an issue. +# +# Triggered when an issue is assigned, unassigned, labeled, unlabeled, +# opened, closed or reopened. +class IssuesEvent + super GithubEvent + + # The `Issue` itself. + fun issue: Issue do return new Issue.from_json(api, repo, json["issue"].as(JsonObject)) + + # Optional `Label` that was added or removed from the issue. + fun lbl: nullable Label do + if not json.has_key("label") then return null + return new Label.from_json(api, repo, json["label"].as(JsonObject)) + end + + # Optional `User` that was assigned or unassigned from the issue. + fun assignee: nullable User do + if not json.has_key("assignee") then return null + return new User.from_json(api, json["assignee"].as(JsonObject)) + end +end + +# Triggered when a user is added as a collaborator to a repository. +class MemberEvent + super GithubEvent + + # `User` that was added. + fun member: User do return new User.from_json(api, json["member"].as(JsonObject)) +end + +# Triggered when an event occurs on a pull request. +# +# Triggered when a pull request is assigned, unassigned, +# labeled, unlabeled, opened, closed, reopened, or synchronized. +class PullRequestEvent + super GithubEvent + + # The pull request number. + fun number: Int do return json["number"].as(Int) + + # The `PullRequest` itself. + fun pull: PullRequest do + return new PullRequest.from_json(api, repo, json["pull_request"].as(JsonObject)) + end +end + +# Triggered when a comment is created on a pull request diff. +class PullRequestReviewCommentEvent + super GithubEvent + + # The `Comment` itself. + fun comment: ReviewComment do + return new ReviewComment.from_json(api, repo, json["comment"].as(JsonObject)) + end + + # `PullRequest` the `comment` belongs to. + fun pull: PullRequest do + return new PullRequest.from_json(api, repo, json["pull_request"].as(JsonObject)) + end +end + +# Triggered when a repository branch is pushed to. +class PushEvent + super GithubEvent + + # SHA of the HEAD commit on the repository. + fun head: String do return json["head"].to_s + + # Full Git ref that was pushed. + # + # Example: “refs/heads/master” + fun ref: String do return json["ref"].to_s + + # Number of commits in the push. + fun size: Int do return json["size"].as(Int) + + # Array of pushed commits. + fun commits: Array[Commit] do + var res = new Array[Commit] + var arr = json["commits"].as(JsonArray) + for obj in arr do + if not obj isa JsonObject then continue + res.add api.load_commit(repo, obj["sha"].to_s).as(not null) + end + return res + end +end + +# Triggered when the status of a Git commit changes. +class StatusEvent + super GithubEvent + + # The `Commit` itself. + fun commit: Commit do + return api.load_commit(repo, json["sha"].to_s).as(not null) + end + + # New state. + # + # Can be `pending`, `success`, `failure`, or `error`. + fun state: String do return json["state"].to_s + + # Optional human-readable description added to the status. + fun description: nullable String do + if not json.has_key("description") then return null + return json["description"].to_s + end + + # Optional link added to the status. + fun target_url: nullable String do + if not json.has_key("target_url") then return null + return json["target_url"].to_s + end + + # Array of branches containing the status' SHA. + # + # Each branch contains the given SHA, + # but the SHA may or may not be the head of the branch. + # + # The array includes a maximum of 10 branches. + fun branches: Array[Branch] do + var res = new Array[Branch] + var arr = json["branches"].as(JsonArray) + for obj in arr do + if not obj isa JsonObject then continue + res.add api.load_branch(repo, obj["name"].to_s).as(not null) + end + return res + end +end diff --git a/lib/glesv2/examples/opengles2_hello_triangle.nit b/lib/glesv2/examples/opengles2_hello_triangle.nit index d62153f..cbf851f 100644 --- a/lib/glesv2/examples/opengles2_hello_triangle.nit +++ b/lib/glesv2/examples/opengles2_hello_triangle.nit @@ -22,7 +22,7 @@ module opengles2_hello_triangle import glesv2 import egl -import mnit_linux # for sdl +import mnit_linux::sdl import x11 if "NIT_TESTING".environ == "true" then exit(0) @@ -106,16 +106,16 @@ assert egl_bind_opengl_es_api else print "eglBingAPI failed: {egl_display.error} ## GLESv2 # -print "Can compile shaders? {gl_shader_compiler}" +print "Can compile shaders? {gl.shader_compiler}" assert_no_gl_error -assert gl_shader_compiler else print "Cannot compile shaders" +assert gl.shader_compiler else print "Cannot compile shaders" # gl program -print gl_error.to_s +print gl.error.to_s var program = new GLProgram if not program.is_ok then - print "Program is not ok: {gl_error.to_s}\nLog:" + print "Program is not ok: {gl.error.to_s}\nLog:" print program.info_log abort end @@ -123,27 +123,28 @@ assert_no_gl_error # vertex shader var vertex_shader = new GLVertexShader -assert vertex_shader.is_ok else print "Vertex shader is not ok: {gl_error}" +assert vertex_shader.is_ok else print "Vertex shader is not ok: {gl.error}" vertex_shader.source = """ attribute vec4 vPosition; void main() { gl_Position = vPosition; -} """ +} +""".to_cstring vertex_shader.compile assert vertex_shader.is_compiled else print "Vertex shader compilation failed with: {vertex_shader.info_log} {program.info_log}" assert_no_gl_error # fragment shader var fragment_shader = new GLFragmentShader -assert fragment_shader.is_ok else print "Fragment shader is not ok: {gl_error}" +assert fragment_shader.is_ok else print "Fragment shader is not ok: {gl.error}" fragment_shader.source = """ precision mediump float; void main() { gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } -""" +""".to_cstring fragment_shader.compile assert fragment_shader.is_compiled else print "Fragment shader compilation failed with: {fragment_shader.info_log}" assert_no_gl_error @@ -159,12 +160,12 @@ assert_no_gl_error var vertices = [0.0, 0.5, 0.0, -0.5, -0.5, 0.0, 0.5, -0.5, 0.0] var vertex_array = new VertexArray(0, 3, vertices) vertex_array.attrib_pointer -gl_clear_color(0.5, 0.0, 0.5, 1.0) +gl.clear_color(0.5, 0.0, 0.5, 1.0) for i in [0..10000[ do printn "." assert_no_gl_error - gl_viewport(0, 0, width, height) - gl_clear_color_buffer + gl.viewport(0, 0, width, height) + gl.clear((new GLBuffer).color) program.use vertex_array.enable vertex_array.draw_arrays_triangles diff --git a/lib/glesv2/glesv2.nit b/lib/glesv2/glesv2.nit index 8d5ec6a..06803ae 100644 --- a/lib/glesv2/glesv2.nit +++ b/lib/glesv2/glesv2.nit @@ -146,15 +146,21 @@ extern class GLProgram `{GLuint`} return active_attrib_name_native(index, max_size).to_s end private fun active_attrib_name_native(index, max_size: Int): NativeString `{ + // We get more values than we need, for compatibility. At least the + // NVidia driver tries to fill them even if NULL. + char *name = malloc(max_size); - glGetActiveAttrib(recv, index, max_size, NULL, NULL, NULL, name); + int size; + GLenum type; + glGetActiveAttrib(recv, index, max_size, NULL, &size, &type, name); return name; `} # Size of the active attribute at `index` fun active_attrib_size(index: Int): Int `{ int size; - glGetActiveAttrib(recv, index, 0, NULL, NULL, &size, NULL); + GLenum type; + glGetActiveAttrib(recv, index, 0, NULL, &size, &type, NULL); return size; `} @@ -162,8 +168,9 @@ extern class GLProgram `{GLuint`} # # May only be float related data types (single float, vectors and matrix). fun active_attrib_type(index: Int): GLFloatDataType `{ + int size; GLenum type; - glGetActiveAttrib(recv, index, 0, NULL, &type, NULL, NULL); + glGetActiveAttrib(recv, index, 0, NULL, &size, &type, NULL); return type; `} @@ -175,14 +182,17 @@ extern class GLProgram `{GLuint`} end private fun active_uniform_name_native(index, max_size: Int): NativeString `{ char *name = malloc(max_size); - glGetActiveUniform(recv, index, max_size, NULL, NULL, NULL, name); + int size; + GLenum type; + glGetActiveUniform(recv, index, max_size, NULL, &size, &type, name); return name; `} # Size of the active uniform at `index` fun active_uniform_size(index: Int): Int `{ int size; - glGetActiveUniform(recv, index, 0, NULL, NULL, &size, NULL); + GLenum type; + glGetActiveUniform(recv, index, 0, NULL, &size, &type, NULL); return size; `} @@ -190,8 +200,9 @@ extern class GLProgram `{GLuint`} # # May be any data type supported by OpenGL ES 2.0 shaders. fun active_uniform_type(index: Int): GLDataType `{ - GLenum type; - glGetActiveUniform(recv, index, 0, NULL, &type, NULL, NULL); + int size; + GLenum type = 0; + glGetActiveUniform(recv, index, 0, NULL, &size, &type, NULL); return type; `} end @@ -199,9 +210,8 @@ end # Abstract OpenGL ES shader object, implemented by `GLFragmentShader` and `GLVertexShader` extern class GLShader `{GLuint`} # Set the source of the shader - fun source=(code: String) import String.to_cstring, String.length `{ - GLchar *c_code = String_to_cstring(code); - glShaderSource(recv, 1, (const GLchar * const*)&c_code, NULL); + fun source=(code: NativeString) `{ + glShaderSource(recv, 1, (GLchar const **)&code, NULL); `} # Source of the shader, if available @@ -318,10 +328,24 @@ extern class GLfloatArray `{GLfloat *`} `} end +# General type for OpenGL enumerations +extern class GLEnum `{ GLenum `} + + redef fun hash `{ return recv; `} + + redef fun ==(o) do return o != null and is_same_type(o) and o.hash == self.hash +end + # An OpenGL ES 2.0 error code -extern class GLError `{ GLenum `} +extern class GLError + super GLEnum + + # Is there no error? fun is_ok: Bool do return is_no_error + + # Is this not an error? fun is_no_error: Bool `{ return recv == GL_NO_ERROR; `} + fun is_invalid_enum: Bool `{ return recv == GL_INVALID_ENUM; `} fun is_invalid_value: Bool `{ return recv == GL_INVALID_VALUE; `} fun is_invalid_operation: Bool `{ return recv == GL_INVALID_OPERATION; `} @@ -340,65 +364,280 @@ extern class GLError `{ GLenum `} end end -# Clear the color buffer with `r`, `g`, `b`, `a` -protected fun gl_clear_color(r, g, b, a: Float) `{ glClearColor(r, g, b, a); `} - -# Set the viewport -protected fun gl_viewport(x, y, width, height: Int) `{ glViewport(x, y, width, height); `} - -# Direct call to `glClear`, call with a combinaison of `gl_clear_color_buffer`, -# `gl_stencil_buffer_bit` and `gl_color_buffer_bit`. -private fun gl_clear(flag: Int) `{ glClear(flag); `} - -protected fun gl_depth_buffer_bit: Int do return 0x0100 -protected fun gl_stencil_buffer_bit: Int do return 0x0400 -protected fun gl_color_buffer_bit: Int do return 0x4000 - -protected fun gl_clear_color_buffer do gl_clear(gl_color_buffer_bit) -protected fun gl_clear_depth_buffer do gl_clear(gl_depth_buffer_bit) -protected fun gl_clear_stencil_buffer do gl_clear(gl_stencil_buffer_bit) - -protected fun gl_error: GLError `{ return glGetError(); `} protected fun assert_no_gl_error do - var error = gl_error + var error = gl.error if not error.is_ok then print "GL error: {error}" abort end end -# Query the boolean value at `key` -private fun gl_get_bool(key: Int): Bool `{ - GLboolean val; - glGetBooleanv(key, &val); - return val == GL_TRUE; -`} +# Texture minifying function +# +# Used by: `GLES::tex_parameter_min_filter` +extern class GLTextureMinFilter + super GLEnum -# Query the floating point value at `key` -private fun gl_get_float(key: Int): Float `{ - GLfloat val; - glGetFloatv(key, &val); - return val; -`} + new nearest `{ return GL_NEAREST; `} + new linear `{ return GL_LINEAR; `} +end -# Query the integer value at `key` -private fun gl_get_int(key: Int): Int `{ - GLint val; - glGetIntegerv(key, &val); - return val; -`} +# Texture magnification function +# +# Used by: `GLES::tex_parameter_mag_filter` +extern class GLTextureMagFilter + super GLEnum + + new nearest `{ return GL_NEAREST; `} + new linear `{ return GL_LINEAR; `} + new nearest_mipmap_nearest `{ return GL_NEAREST_MIPMAP_NEAREST; `} + new linear_mipmap_nearest `{ return GL_LINEAR_MIPMAP_NEAREST; `} + new nearest_mipmap_linear `{ return GL_NEAREST_MIPMAP_LINEAR; `} + new linear_mipmap_linear `{ return GL_LINEAR_MIPMAP_LINEAR; `} +end -# Does this driver support shader compilation? +# Wrap parameter of a texture # -# Should always return `true` in OpenGL ES 2.0 and 3.0. -fun gl_shader_compiler: Bool do return gl_get_bool(0x8DFA) +# Used by: `tex_parameter_wrap_*` +extern class GLTextureWrap + super GLEnum + + new clamp_to_edge `{ return GL_CLAMP_TO_EDGE; `} + new mirrored_repeat `{ return GL_MIRRORED_REPEAT; `} + new repeat `{ return GL_REPEAT; `} +end + +# Target texture +# +# Used by: `tex_parameter_*` +extern class GLTextureTarget + super GLEnum + + new flat `{ return GL_TEXTURE_2D; `} + new cube_map `{ return GL_TEXTURE_CUBE_MAP; `} +end + +# A server-side capability +class GLCap + + # TODO private init + + # Internal OpenGL integer for this capability + private var val: Int + + # Enable this server-side capability + fun enable do enable_native(val) + private fun enable_native(cap: Int) `{ glEnable(cap); `} + + # Disable this server-side capability + fun disable do disable_native(val) + private fun disable_native(cap: Int) `{ glDisable(cap); `} + + redef fun hash do return val + redef fun ==(o) do return o != null and is_same_type(o) and o.hash == self.hash +end +redef class Sys + private var gles = new GLES is lazy +end + +# Entry points to OpenGL ES 2.0 services +fun gl: GLES do return sys.gles + +# OpenGL ES 2.0 services +class GLES + + # Clear the color buffer to `red`, `green`, `blue` and `alpha` + fun clear_color(red, green, blue, alpha: Float) `{ + glClearColor(red, green, blue, alpha); + `} + + # Set the viewport + fun viewport(x, y, width, height: Int) `{ glViewport(x, y, width, height); `} + + # Specify mapping of depth values from normalized device coordinates to window coordinates + # + # Default at `gl_depth_range(0.0, 1.0)` + fun depth_range(near, far: Float) `{ glDepthRangef(near, far); `} + + # Define front- and back-facing polygons + # + # Front-facing polygons are clockwise if `value`, counter-clockwise otherwise. + fun front_face=(value: Bool) `{ glFrontFace(value? GL_CW: GL_CCW); `} + + # Specify whether front- or back-facing polygons can be culled, default is `back` only + # + # One or both of `front` or `back` must be `true`. If you want to deactivate culling + # use `(new GLCap.cull_face).disable`. + # + # Require: `front or back` + fun cull_face(front, back: Bool) + do + assert not (front or back) + cull_face_native(front, back) + end + + private fun cull_face_native(front, back: Bool) `{ + glCullFace(front? back? GL_FRONT_AND_BACK: GL_BACK: GL_FRONT); + `} + + # Clear the `buffer` + fun clear(buffer: GLBuffer) `{ glClear(buffer); `} + + # Last error from OpenGL ES 2.0 + fun error: GLError `{ return glGetError(); `} + + # Query the boolean value at `key` + private fun get_bool(key: Int): Bool `{ + GLboolean val; + glGetBooleanv(key, &val); + return val == GL_TRUE; + `} + + # Query the floating point value at `key` + private fun get_float(key: Int): Float `{ + GLfloat val; + glGetFloatv(key, &val); + return val; + `} + + # Query the integer value at `key` + private fun get_int(key: Int): Int `{ + GLint val; + glGetIntegerv(key, &val); + return val; + `} + + # Does this driver support shader compilation? + # + # Should always return `true` in OpenGL ES 2.0 and 3.0. + fun shader_compiler: Bool do return get_bool(0x8DFA) + + # Enable or disable writing into the depth buffer + fun depth_mask(value: Bool) `{ glDepthMask(value); `} + + # Set the scale and units used to calculate depth values + fun polygon_offset(factor, units: Float) `{ glPolygonOffset(factor, units); `} + + # Specify the width of rasterized lines + fun line_width(width: Float) `{ glLineWidth(width); `} + + # Set the pixel arithmetic for the blending operations + # + # Defaultvalues before assignation: + # * `src_factor`: `GLBlendFactor::one` + # * `dst_factor`: `GLBlendFactor::zero` + fun blend_func(src_factor, dst_factor: GLBlendFactor) `{ + glBlendFunc(src_factor, dst_factor); + `} + + # Specify the value used for depth buffer comparisons + # + # Default value is `GLDepthFunc::less` + # + # Foreign: glDepthFunc + fun depth_func(func: GLDepthFunc) `{ glDepthFunc(func); `} + + # Copy a block of pixels from the framebuffer of `fomat` and `typ` at `data` + # + # Foreign: glReadPixel + fun read_pixels(x, y, width, height: Int, format: GLPixelFormat, typ: GLPixelType, data: Pointer) `{ + glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, data); + `} + + # Set the texture minifying function + # + # Foreign: glTexParameter with GL_TEXTURE_MIN_FILTER + fun tex_parameter_min_filter(target: GLTextureTarget, value: GLTextureMinFilter) `{ + glTexParameteri(target, GL_TEXTURE_MIN_FILTER, value); + `} + + # Set the texture magnification function + # + # Foreign: glTexParameter with GL_TEXTURE_MAG_FILTER + fun tex_parameter_mag_filter(target: GLTextureTarget, value: GLTextureMagFilter) `{ + glTexParameteri(target, GL_TEXTURE_MAG_FILTER, value); + `} + + # Set the texture wrap parameter for coordinates _s_ + # + # Foreign: glTexParameter with GL_TEXTURE_WRAP_S + fun tex_parameter_wrap_s(target: GLTextureTarget, value: GLTextureWrap) `{ + glTexParameteri(target, GL_TEXTURE_WRAP_S, value); + `} + + # Set the texture wrap parameter for coordinates _t_ + # + # Foreign: glTexParameter with GL_TEXTURE_WRAP_T + fun tex_parameter_wrap_t(target: GLTextureTarget, value: GLTextureWrap) `{ + glTexParameteri(target, GL_TEXTURE_WRAP_T, value); + `} + + # Render primitives from array data + # + # Foreign: glDrawArrays + fun draw_arrays(mode: GLDrawMode, from, count: Int) `{ glDrawArrays(mode, from, count); `} + + # OpenGL server-side capabilities + var capabilities = new GLCapabilities is lazy +end + +# Entry point to OpenGL server-side capabilities +class GLCapabilities + + # GL capability: blend the computed fragment color values + # + # Foreign: GL_BLEND + fun blend: GLCap is lazy do return new GLCap(0x0BE2) + + # GL capability: cull polygons based of their winding in window coordinates + # + # Foreign: GL_CULL_FACE + fun cull_face: GLCap is lazy do return new GLCap(0x0B44) + + # GL capability: do depth comparisons and update the depth buffer + # + # Foreign: GL_DEPTH_TEST + fun depth_test: GLCap is lazy do return new GLCap(0x0B71) + + # GL capability: dither color components or indices before they are written to the color buffer + # + # Foreign: GL_DITHER + fun dither: GLCap is lazy do return new GLCap(0x0BE2) + + # GL capability: add an offset to depth values of a polygon fragment before depth test + # + # Foreign: GL_POLYGON_OFFSET_FILL + fun polygon_offset_fill: GLCap is lazy do return new GLCap(0x8037) + + # GL capability: compute a temporary coverage value where each bit is determined by the alpha value at the corresponding location + # + # Foreign: GL_SAMPLE_ALPHA_TO_COVERAGE + fun sample_alpha_to_coverage: GLCap is lazy do return new GLCap(0x809E) + + # GL capability: AND the fragment coverage with the temporary coverage value + # + # Foreign: GL_SAMPLE_COVERAGE + fun sample_coverage: GLCap is lazy do return new GLCap(0x80A0) + + # GL capability: discard fragments that are outside the scissor rectangle + # + # Foreign: GL_SCISSOR_TEST + fun scissor_test: GLCap is lazy do return new GLCap(0x0C11) + + # GL capability: do stencil testing and update the stencil buffer + # + # Foreign: GL_STENCIL_TEST + fun stencil_test: GLCap is lazy do return new GLCap(0x0B90) +end # Float related data types of OpenGL ES 2.0 shaders # # Only data types supported by shader attributes, as seen with # `GLProgram::active_attrib_type`. -extern class GLFloatDataType `{ GLenum `} +extern class GLFloatDataType + super GLEnum + fun is_float: Bool `{ return recv == GL_FLOAT; `} fun is_float_vec2: Bool `{ return recv == GL_FLOAT_VEC2; `} fun is_float_vec3: Bool `{ return recv == GL_FLOAT_VEC3; `} @@ -406,6 +645,12 @@ extern class GLFloatDataType `{ GLenum `} fun is_float_mat2: Bool `{ return recv == GL_FLOAT_MAT2; `} fun is_float_mat3: Bool `{ return recv == GL_FLOAT_MAT3; `} fun is_float_mat4: Bool `{ return recv == GL_FLOAT_MAT4; `} + + # Instances of `GLFloatDataType` can be equal to instances of `GLDataType` + redef fun ==(o) + do + return o != null and o isa GLFloatDataType and o.hash == self.hash + end end # All data types of OpenGL ES 2.0 shaders @@ -426,3 +671,100 @@ extern class GLDataType fun is_sampler_2d: Bool `{ return recv == GL_SAMPLER_2D; `} fun is_sampler_cube: Bool `{ return recv == GL_SAMPLER_CUBE; `} end + +# Kind of primitives to render with `GLES::draw_arrays` +extern class GLDrawMode + super GLEnum + + new points `{ return GL_POINTS; `} + new line_strip `{ return GL_LINE_STRIP; `} + new line_loop `{ return GL_LINE_LOOP; `} + new lines `{ return GL_LINES; `} + new triangle_strip `{ return GL_TRIANGLE_STRIP; `} + new triangle_fan `{ return GL_TRIANGLE_FAN; `} + new triangles `{ return GL_TRIANGLES; `} +end + +# Pixel arithmetic for blending operations +# +# Used by `GLES::blend_func` +extern class GLBlendFactor + super GLEnum + + new zero `{ return GL_ZERO; `} + new one `{ return GL_ONE; `} + new src_color `{ return GL_SRC_COLOR; `} + new one_minus_src_color `{ return GL_ONE_MINUS_SRC_COLOR; `} + new dst_color `{ return GL_DST_COLOR; `} + new one_minus_dst_color `{ return GL_ONE_MINUS_DST_COLOR; `} + new src_alpha `{ return GL_SRC_ALPHA; `} + new one_minus_src_alpha `{ return GL_ONE_MINUS_SRC_ALPHA; `} + new dst_alpha `{ return GL_DST_ALPHA; `} + new one_minus_dst_alpha `{ return GL_ONE_MINUS_DST_ALPHA; `} + new constant_color `{ return GL_CONSTANT_COLOR; `} + new one_minus_constant_color `{ return GL_ONE_MINUS_CONSTANT_COLOR; `} + new constant_alpha `{ return GL_CONSTANT_ALPHA; `} + new one_minus_constant_alpha `{ return GL_ONE_MINUS_CONSTANT_ALPHA; `} + + # Used for destination only + new src_alpha_saturate `{ return GL_SRC_ALPHA_SATURATE; `} +end + +# Condition under which a pixel will be drawn +# +# Used by `GLES::depth_func` +extern class GLDepthFunc + super GLEnum + + new never `{ return GL_NEVER; `} + new less `{ return GL_LESS; `} + new equal `{ return GL_EQUAL; `} + new lequal `{ return GL_LEQUAL; `} + new greater `{ return GL_GREATER; `} + new not_equal `{ return GL_NOTEQUAL; `} + new gequal `{ return GL_GEQUAL; `} + new always `{ return GL_ALWAYS; `} +end + +# Format of pixel data +# +# Used by `GLES::read_pixels` +extern class GLPixelFormat + super GLEnum + + new alpha `{ return GL_ALPHA; `} + new rgb `{ return GL_RGB; `} + new rgba `{ return GL_RGBA; `} +end + +# Data type of pixel data +# +# Used by `GLES::read_pixels` +extern class GLPixelType + super GLEnum + + new unsigned_byte `{ return GL_UNSIGNED_BYTE; `} + new unsigned_short_5_6_5 `{ return GL_UNSIGNED_SHORT_5_6_5; `} + new unsigned_short_4_4_4_4 `{ return GL_UNSIGNED_SHORT_4_4_4_4; `} + new unsigned_short_5_5_5_1 `{ return GL_UNSIGNED_SHORT_5_5_5_1; `} +end + +# Set of buffers as a bitwise OR mask, used by `GLES::clear` +# +# ~~~ +# var buffers = (new GLBuffer).color.depth +# gl.clear buffers +# ~~~ +extern class GLBuffer `{ GLbitfield `} + # Get an empty set of buffers + new `{ return 0; `} + + # Add the color buffer to the returned buffer set + fun color: GLBuffer `{ return recv | GL_COLOR_BUFFER_BIT; `} + + # Add the depth buffer to the returned buffer set + fun depth: GLBuffer `{ return recv | GL_DEPTH_BUFFER_BIT; `} + + # Add the stencil buffer to the returned buffer set + fun stencil: GLBuffer `{ return recv | GL_STENCIL_BUFFER_BIT; `} +end diff --git a/lib/gtk/v3_4/gtk_core.nit b/lib/gtk/v3_4/gtk_core.nit index 561139b..f6f10aa 100644 --- a/lib/gtk/v3_4/gtk_core.nit +++ b/lib/gtk/v3_4/gtk_core.nit @@ -116,6 +116,17 @@ extern class GtkWidget `{GtkWidget *`} fun visible_self: Bool is extern `{ return gtk_widget_get_visible(recv); `} + + # Destroy the widget + fun destroy `{ gtk_widget_destroy(recv); `} + + # Show the widget on screen + # + # See: `show_all` to recursively show this widget and contained widgets. + fun show `{ gtk_widget_show(recv); `} + + # Hide the widget (reverse the effects of `show`) + fun hide `{ gtk_widget_hide(recv); `} end #Base class for widgets which contain other widgets @@ -348,6 +359,13 @@ extern class GtkGrid `{GtkGrid *`} `} end +# A container box +# +# @https://developer.gnome.org/gtk3/3.4/GtkBox.html +extern class GtkBox `{ GtkBox * `} + super GtkContainer +end + #The tree interface used by GtkTreeView #@https://developer.gnome.org/gtk3/stable/GtkTreeModel.html extern class GtkTreeModel `{GtkTreeModel *`} @@ -386,7 +404,7 @@ extern class GtkEntry `{GtkEntry *`} return (GtkEntry *)gtk_entry_new(); `} - fun text : String is extern import String.to_cstring`{ + fun text : String is extern import NativeString.to_s `{ return NativeString_to_s( (char *)gtk_entry_get_text( recv ) ); `} diff --git a/lib/gtk/v3_4/gtk_dialogs.nit b/lib/gtk/v3_4/gtk_dialogs.nit index 7da083f..079eb71 100644 --- a/lib/gtk/v3_4/gtk_dialogs.nit +++ b/lib/gtk/v3_4/gtk_dialogs.nit @@ -119,8 +119,8 @@ extern class GtkAboutDialog `{GtkAboutDialog *`} # gtk_about_dialog_set_authors( recv, String_to_cstring( authors_list ) ); #`} - fun show ( parent : GtkWindow, params : String ) is extern import String.to_cstring`{ - gtk_show_about_dialog( parent, String_to_cstring( params ), NULL); + fun show_about_dialog(parent: GtkWindow, params: String) import String.to_cstring `{ + gtk_show_about_dialog(parent, String_to_cstring(params), NULL); `} end diff --git a/lib/html/html.nit b/lib/html/html.nit index 83ff255..8d2de2b 100644 --- a/lib/html/html.nit +++ b/lib/html/html.nit @@ -84,10 +84,13 @@ class HTMLPage end end +# An HTML element. class HTMLTag super Streamable - # HTML tagname: 'div' for
+ # HTML element type. + # + # `"div"` for `
`. var tag: String init do self.is_void = (once ["area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"]).has(tag) @@ -99,6 +102,7 @@ class HTMLTag # assert (new HTMLTag("p")).is_void == false var is_void: Bool is noinit + # Create a HTML elements with the specifed type and attributes. init with_attrs(tag: String, attrs: Map[String, String]) do self.tag = tag self.attrs = attrs diff --git a/lib/ini.nit b/lib/ini.nit index 60d2f12..71868b6 100644 --- a/lib/ini.nit +++ b/lib/ini.nit @@ -37,10 +37,7 @@ class ConfigTree # The ini file used to read/store data var ini_file: String - init(file: String) do - self.ini_file = file - if file.file_exists then load - end + init do if ini_file.file_exists then load # Get the config value for `key` # @@ -103,7 +100,6 @@ class ConfigTree # assert config.has_key("foo.bar") # assert not config.has_key("zoo") fun has_key(key: String): Bool do - var children = roots var parts = key.split(".").reversed var node = get_root(parts.pop) if node == null then return false @@ -223,7 +219,6 @@ class ConfigTree private var roots = new Array[ConfigNode] private fun set_node(key: String, value: nullable String) do - var children = roots var parts = key.split(".").reversed var k = parts.pop var root = get_root(k) @@ -250,7 +245,6 @@ class ConfigTree end private fun get_node(key: String): nullable ConfigNode do - var children = roots var parts = key.split(".").reversed var node = get_root(parts.pop) while not parts.is_empty do @@ -283,14 +277,11 @@ class ConfigTree end private class ConfigNode - var parent: nullable ConfigNode + + var parent: nullable ConfigNode = null var children = new HashMap[String, ConfigNode] var name: String is writable - var value: nullable String - - init(name: String) do - self.name = name - end + var value: nullable String = null fun key: String do if parent == null then diff --git a/lib/java/collections.nit b/lib/java/collections.nit new file mode 100644 index 0000000..4da0250 --- /dev/null +++ b/lib/java/collections.nit @@ -0,0 +1,130 @@ +# 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. + +# Basic Java collections +# +# ~~~ +# var coll = new JavaArray(2) +# +# assert coll[0].is_java_null +# coll[0] = "zero".to_java_string +# coll[1] = "one".to_java_string +# +# assert coll.length == 2 +# assert coll.first.to_s == "zero" +# assert coll[1].to_s == "one" +# assert [for e in coll do e.to_s] == ["zero", "one"] +# ~~~ +module collections + +import java + +# Java primitive array +# +# These have fixed size so they offer the same services as `SequenceRead` and +# `[]=`, but would not support `Sequence::add`. +extern class AbstractJavaArray[E: Object] + super SequenceRead[E] + super JavaObject + + # Set the `value` at `key` + fun []=(key: Int, value: E) is abstract + + redef fun iterator do return new JavaArrayIterator[E](self) + + redef fun reverse_iterator do return new JavaArrayReverseIterator[E](self) +end + +# Java primitive array `float[]` +# +# Note that Nit `Float` is the size of a double, so storing them in a +# `JavaFloatArray` may lead to a loss of precision. +extern class JavaFloatArray in "Java" `{ float[] `} + super AbstractJavaArray[Float] + + # Get a new array of the given `size` + new(size: Int) in "Java" `{ return new float[(int)size]; `} + + redef fun [](i) in "Java" `{ return (double)recv[(int)i]; `} + + redef fun []=(i, e) in "Java" `{ recv[(int)i] = (float)e; `} + + redef fun length in "Java" `{ return recv.length; `} +end + +# Java primitive array `double[]` +extern class JavaDoubleArray in "Java" `{ double[] `} + super AbstractJavaArray[Float] + + # Get a new array of the given `size` + new(size: Int) in "Java" `{ return new double[(int)size]; `} + + redef fun [](i) in "Java" `{ return recv[(int)i]; `} + + redef fun []=(i, e) in "Java" `{ recv[(int)i] = (float)e; `} + + redef fun length in "Java" `{ return recv.length; `} +end + +# Java primitive array `Object[]` +extern class JavaArray in "Java" `{ java.lang.Object[] `} + super AbstractJavaArray[JavaObject] + + # Get a new array of the given `size` + new(size: Int) in "Java" `{ return new Object[(int)size]; `} + + redef fun [](i) in "Java" `{ return recv[(int)i]; `} + + redef fun []=(i, e) in "Java" `{ recv[(int)i] = e; `} + + redef fun length in "Java" `{ return recv.length; `} +end + +# TODO other primitive arrays: +# * Java primitive array `byte[]` +# * Java primitive array `short[]` +# * Java primitive array `int[]` +# * Java primitive array `long[]` +# * Java primitive array `boolean[]` +# * Java primitive array `char[]` + +# An `Iterator` on Java primitive arrays +private class JavaArrayIterator[E: Object] + super IndexedIterator[E] + + var array: AbstractJavaArray[E] + + redef fun item do return array[index] + + redef fun is_ok do return index < array.length + + redef fun next do index += 1 + + redef var index = 0 +end + +# A reverse `Iterator` on Java primitive arrays +private class JavaArrayReverseIterator[E: Object] + super IndexedIterator[E] + + var array: AbstractJavaArray[E] + + redef fun item do return array[index] + + redef fun is_ok do return index >= 0 + + redef fun next do index -= 1 + + redef var index = array.length - 1 +end diff --git a/lib/json/json_lexer.nit b/lib/json/json_lexer.nit index 3273525..162e529 100644 --- a/lib/json/json_lexer.nit +++ b/lib/json/json_lexer.nit @@ -1,4 +1,6 @@ -# Lexer generated by nitcc for the grammar jsonimport nitcc_runtime +# Lexer generated by nitcc for the grammar json +module json_lexer is no_warning "missing-doc" +import nitcc_runtime import json_parser class Lexer_json super Lexer diff --git a/lib/json/json_parser.nit b/lib/json/json_parser.nit index f95a3cb..581551f 100644 --- a/lib/json/json_parser.nit +++ b/lib/json/json_parser.nit @@ -1,4 +1,5 @@ # Parser generated by nitcc for the grammar json +module json_parser is no_warning("missing-doc","old-init") import nitcc_runtime class Parser_json super Parser diff --git a/lib/json/static.nit b/lib/json/static.nit index bb578fd..12dbc7c 100644 --- a/lib/json/static.nit +++ b/lib/json/static.nit @@ -34,12 +34,28 @@ interface Jsonable # SEE: `append_json` fun to_json: String is abstract + # Use `append_json` to implement `to_json`. + # + # Therefore, one that redefine `append_json` may use the following + # redefinition to link `to_json` and `append_json`: + # + # ~~~nitish + # redef fun to_json do return to_json_by_append + # ~~~ + # + # Note: This is not the default implementation of `to_json` in order to + # avoid cyclic references between `append_json` and `to_json` when none are + # implemented. + protected fun to_json_by_append: String do + var buffer = new RopeBuffer + append_json(buffer) + return buffer.write_to_string + end + # Append the JSON representation of `self` to the specified buffer. # # SEE: `to_json` - fun append_json(buffer: Buffer) do - buffer.append(to_json) - end + fun append_json(buffer: Buffer) do buffer.append(to_json) end redef class Text @@ -82,11 +98,7 @@ redef class Text # # 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 + redef fun to_json do return to_json_by_append # Parse `self` as JSON. # @@ -211,11 +223,7 @@ interface JsonMapRead[K: String, V: nullable Jsonable] # obj = new JsonObject # obj["baz"] = null # assert obj.to_json == "\{\"baz\":null\}" - redef fun to_json do - var buffer = new FlatBuffer - append_json(buffer) - return buffer.write_to_string - end + redef fun to_json do return to_json_by_append private fun append_json_entry(iterator: MapIterator[String, nullable Jsonable], buffer: Buffer) do @@ -259,11 +267,7 @@ class JsonSequenceRead[E: nullable Jsonable] # assert arr.to_json =="[\"foo\"]" # arr.pop # assert arr.to_json =="[]" - redef fun to_json do - var buffer = new FlatBuffer - append_json(buffer) - return buffer.write_to_string - end + redef fun to_json do return to_json_by_append private fun append_json_entry(iterator: Iterator[nullable Jsonable], buffer: Buffer) do diff --git a/lib/json/store.nit b/lib/json/store.nit index 23a176c..8ea6e84 100644 --- a/lib/json/store.nit +++ b/lib/json/store.nit @@ -14,37 +14,76 @@ # # ## Usage # -# Create a new JsonStore or reuse an existing one. +# ### Initialization +# +# JsonStore use the file system to store and load json files. +# +# For initialization you need to give the directory used in the +# file system to save objects. +# # ~~~ # var store = new JsonStore("store_dir") # ~~~ # -# JsonStore can store json of type JsonObject and JsonArray. +# ### Documents +# +# With JsonStore you manage *documents*. +# Documents are simple json files that can be stored and loaded from json store. +# +# JsonStore can store documents of type JsonObject and JsonArray. # ~~~ -# var red = "red" -# var obj = new JsonObject -# obj["color"] = red -# obj["code"] = "FF0000" +# var red = new JsonObject +# red["name"] = "red" +# red["code"] = "FF0000" # ~~~ # -# Data are stored under a key. +# Data are stored under a *key*. +# This is the path to the document from `JsonStore::store_dir` +# without the `.json` extension. +# +# Examples: +# +# * key `document` will store data under `store_dir / "document.json"` +# * key `collection/data` will store data under `store_dir / "collection/data.json"` +# # ~~~ -# var key = "colors/{red}" +# var key = "colors/red" # ~~~ # # Store the object. # ~~~ -# store.store_object(key, obj) -# assert store.has_key(key) +# store.store_object(key, red) # ~~~ # # Load the object. # ~~~ -# var res = store.load_object(key) -# assert res["name"] == red +# assert store.has_key(key) +# var obj = store.load_object(key) +# assert obj["name"] == obj["name"] +# ~~~ +# +# ### Collections +# +# A collection is a set of documents stored under the same path. +# +# ~~~ +# var green = new JsonObject +# green["name"] = "green" +# green["code"] = "00FF00" +# store.store_object("colors/green", green) +# +# assert store.has_collection("colors") +# +# var col = store.list_collection("colors") +# assert col.length == 2 +# assert col.has("green") +# assert col.has("red") # ~~~ # -# Clear all stored data. +# ### Clearing store +# +# You can delete all the data contained in the `JsonStore::store_dir` with `clear`. +# # ~~~ # store.clear # ~~~ @@ -53,15 +92,6 @@ module store import static # A JsonStore can save and load json data from file system. -# -# Json files are stored under a `key`. -# This key represents the path to the json file from `store_dir` -# without the `.json` extension. -# -# Examples: -# -# * key `document` will store data under `store_dir / "document.json"` -# * key `collection/data` will store data under `store_dir / "collection/data.json"` class JsonStore # Directory where data are stored. @@ -126,4 +156,23 @@ class JsonStore file.close return text.parse_json end + + # Get the list of keys stored under the collection `key`. + fun list_collection(key: String): JsonArray do + var res = new JsonArray + var coll = (store_dir / "{key}").to_path + if not coll.exists or not coll.stat.is_dir then return res + for file in coll.files do + if file.to_s.has_suffix(".json") then + res.add(file.to_s.basename(".json")) + end + end + return res + end + + # Does `key` matches a collection? + fun has_collection(key: String): Bool do + var path = (store_dir / "{key}").to_path + return path.exists and path.stat.is_dir + end end diff --git a/lib/mnit/opengles1.nit b/lib/mnit/opengles1.nit index ebdd8b6..eb1061b 100644 --- a/lib/mnit/opengles1.nit +++ b/lib/mnit/opengles1.nit @@ -29,7 +29,6 @@ in "C header" `{ EGLConfig mnit_config; int32_t mnit_width; int32_t mnit_height; - float mnit_zoom; struct mnit_opengles_Texture { GLuint texture; @@ -207,7 +206,6 @@ class Opengles1Display mnit_config = config; mnit_width = w; mnit_height = h; - mnit_zoom = 1.0f; LOGI("surface", (int)surface); LOGI("display", (int)display); @@ -263,7 +261,6 @@ class Opengles1Display glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrthof(x, x+w, y+h, y, 0.0f, 1.0f); - mnit_zoom = ((float)w)/mnit_width; glMatrixMode(GL_MODELVIEW); glFrontFace( GL_CW ); `} diff --git a/lib/mnit_android/android_app.nit b/lib/mnit_android/android_app.nit index 87afb4a..9d0d6c4 100644 --- a/lib/mnit_android/android_app.nit +++ b/lib/mnit_android/android_app.nit @@ -15,37 +15,18 @@ # limitations under the License. # Impements the services of `mnit:app` using the API from the Android ndk -module android_app is - android_manifest_activity """ +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 import mnit::opengles1 - -in "C header" `{ - #include - #include - #include - #include - - #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "mnit", __VA_ARGS__)) - #ifdef DEBUG - #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "mnit", __VA_ARGS__)) - #else - #define LOGI(...) (void)0 - #endif -`} +intrude import ::android::input_events in "C" `{ #include - #include - #define GL_GLEXT_PROTOTYPES 1 - #include - #include extern EGLDisplay mnit_display; extern EGLSurface mnit_surface; @@ -53,189 +34,28 @@ in "C" `{ extern EGLConfig mnit_config; extern int32_t mnit_width; extern int32_t mnit_height; - extern float mnit_zoom; - - //int mnit_orientation_changed; - float mnit_zoom; - - /* Handle inputs from the Android platform and sort them before - sending them in the Nit App */ - static int32_t mnit_handle_input(struct android_app* app, AInputEvent* event) { - App nit_app = app->userData; - LOGI("handle input %i", (int)pthread_self()); - if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_KEY) { - LOGI("key"); - return App_extern_input_key(nit_app, event); - } - else if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { - LOGI("motion"); - return App_extern_input_motion(nit_app, event); - } - - return 0; - } `} - -extern class InnerAndroidMotionEvent in "C" `{AInputEvent *`} - super Pointer - private fun pointers_count: Int is extern `{ - return AMotionEvent_getPointerCount(recv); - `} - private fun just_went_down: Bool is extern `{ - return (AMotionEvent_getAction(recv) & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_DOWN; - `} - private fun edge: Int is extern `{ - return AMotionEvent_getEdgeFlags(recv); - `} - private fun index_down_pointer: Int is extern `{ - int a = AMotionEvent_getAction(recv); - if ((a & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) - return (a & AMOTION_EVENT_ACTION_POINTER_INDEX_MASK) >> AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT; - else return -1; - `} - - private fun action: AMotionEventAction `{ return AMotionEvent_getAction(recv); `} -end - -extern class AMotionEventAction `{ int32_t `} - protected fun action: Int `{ return recv & AMOTION_EVENT_ACTION_MASK; `} - fun is_down: Bool do return action == 0 - fun is_up: Bool do return action == 1 - fun is_move: Bool do return action == 2 - fun is_cancel: Bool do return action == 3 - fun is_outside: Bool do return action == 4 - fun is_pointer_down: Bool do return action == 5 - fun is_pointer_up: Bool do return action == 6 -end - -interface AndroidInputEvent - super InputEvent -end - -class AndroidMotionEvent - super AndroidInputEvent - super MotionEvent - - private init(ie: InnerAndroidMotionEvent) do inner_event = ie - private var inner_event: InnerAndroidMotionEvent - - private var pointers_cache: nullable Array[AndroidPointerEvent] = null - fun pointers: Array[AndroidPointerEvent] - do - if pointers_cache != null then - return pointers_cache.as(not null) - else - var pointers = new Array[AndroidPointerEvent] - var pointers_count = inner_event.pointers_count - for i in [0 .. pointers_count [do - var pointer_event = new AndroidPointerEvent(self, i) - pointers.add(pointer_event) - end - pointers_cache = pointers - return pointers - end - end - - redef fun just_went_down: Bool do return inner_event.just_went_down - fun edge: Int do return inner_event.edge - - redef fun down_pointer: nullable AndroidPointerEvent - do - var i = inner_event.index_down_pointer - if i > 0 then - return pointers[i] - else - return null - end - end -end - -class AndroidPointerEvent - super PointerEvent - super AndroidInputEvent - - protected var motion_event: AndroidMotionEvent - protected var pointer_id: Int - - redef fun x: Float do return extern_x(motion_event.inner_event, pointer_id) - private fun extern_x(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{ - return ((int) AMotionEvent_getX(motion_event, pointer_id)); - `} - - redef fun y: Float do return extern_y(motion_event.inner_event, pointer_id) - private fun extern_y(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{ - return ((int) AMotionEvent_getY(motion_event, pointer_id)); - `} - - fun pressure: Float do return extern_pressure(motion_event.inner_event, pointer_id) - private fun extern_pressure(motion_event: InnerAndroidMotionEvent, pointer_id: Int): Float is extern `{ - return AMotionEvent_getPressure(motion_event, pointer_id); - `} - - redef fun pressed - do - var action = motion_event.inner_event.action - return action.is_down or action.is_move - end - - redef fun depressed do return not pressed -end - -extern class AndroidKeyEvent in "C" `{AInputEvent *`} - super KeyEvent - super AndroidInputEvent - - fun action: Int is extern `{ - return AKeyEvent_getAction(recv); - `} - redef fun is_down: Bool do return action == 0 - redef fun is_up: Bool do return action == 1 - - fun key_code: Int is extern `{ - return AKeyEvent_getKeyCode(recv); - `} - - redef fun to_c `{ - int code = AKeyEvent_getKeyCode(recv); - if (code >= AKEYCODE_0 && code <= AKEYCODE_9) - return '0'+code-AKEYCODE_0; - if (code >= AKEYCODE_A && code <= AKEYCODE_Z) - return 'a'+code-AKEYCODE_A; - return 0; - `} - - fun is_back_key: Bool do return key_code == 4 - fun is_menu_key: Bool do return key_code == 82 - fun is_search_key: Bool do return key_code == 84 - - fun is_volume_up: Bool do return key_code == 24 - fun is_volume_down: Bool do return key_code == 25 -end - redef class App redef type D: Opengles1Display redef fun init_window do - set_as_input_handler native_app_glue display = new Opengles1Display super end - private fun set_as_input_handler(app_glue: NativeAppGlue) import extern_input_key, extern_input_motion `{ - app_glue->onInputEvent = mnit_handle_input; - `} - redef fun full_frame do if not paused then super - # these are used as a callback from native to type incoming events - private fun extern_input_key(event: AndroidKeyEvent): Bool + redef fun generate_input do poll_looper 0 + + redef fun native_input_key(event) do return input(event) end - private fun extern_input_motion(event: InnerAndroidMotionEvent): Bool + + redef fun native_input_motion(event) do var ie = new AndroidMotionEvent(event) var handled = input(ie) @@ -248,6 +68,4 @@ redef class App return handled end - - redef fun generate_input do poll_looper 0 end diff --git a/lib/more_collections.nit b/lib/more_collections.nit index 1bcc514..6396ec2 100644 --- a/lib/more_collections.nit +++ b/lib/more_collections.nit @@ -29,7 +29,7 @@ module more_collections # assert m.has_key("four") # assert m["four"] == ['i', 'i', 'i', 'i'] # assert m["zzz"] == new Array[Char] -class MultiHashMap[K: Object, V] +class MultiHashMap[K, V] super HashMap[K, Array[V]] # Add `v` to the array associated with `k`. @@ -59,7 +59,7 @@ end # assert hm2[1, "one"] == 1.0 # assert hm2[2, "not-two"] == null # ~~~~ -class HashMap2[K1: Object, K2: Object, V] +class HashMap2[K1, K2, V] private var level1 = new HashMap[K1, HashMap[K2, V]] # Return the value associated to the keys `k1` and `k2`. @@ -97,7 +97,7 @@ end # assert hm3[1, "one", 11] == 1.0 # assert hm3[2, "not-two", 22] == null # ~~~~ -class HashMap3[K1: Object, K2: Object, K3: Object, V] +class HashMap3[K1, K2, K3, V] private var level1 = new HashMap[K1, HashMap2[K2, K3, V]] # Return the value associated to the keys `k1`, `k2`, and `k3`. @@ -165,7 +165,7 @@ end # assert dma["b"] == [65, 66] # assert dma.default == [65] # ~~~~ -class DefaultMap[K: Object, V] +class DefaultMap[K, V] super HashMap[K, V] # The default value. diff --git a/lib/mpd.nit b/lib/mpd.nit index 97c38f2..e814b56 100644 --- a/lib/mpd.nit +++ b/lib/mpd.nit @@ -23,7 +23,7 @@ import socket class MPDConnection # Socket connection to server. - var socket: nullable Socket = null + var socket: nullable TCPStream = null # Server hostname. var host: String @@ -40,9 +40,10 @@ class MPDConnection # Connect to the MPD server fun connect do - var p: nullable Socket = null + var p: nullable TCPStream = null - p = new Socket.client(host, port) + p = new TCPStream.connect(host, port) + assert p.connected sys.nanosleep(0,5000) diff --git a/lib/neo4j/graph/graph.nit b/lib/neo4j/graph/graph.nit new file mode 100644 index 0000000..39063f2 --- /dev/null +++ b/lib/neo4j/graph/graph.nit @@ -0,0 +1,278 @@ +# 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. + +# Provides an interface for services on a Neo4j graphs. +module neo4j::graph::graph + +import neo4j +import progression + +# A Neo4j graph with a local identification scheme for its nodes. +# +# An identification scheme can be defined by subclassing `NeoNodeCollection`. +# +# `GraphStore` can be subclassed to add ways to save or load a graph. The +# storing mechanisms may use `nodes.id_of` to identify the nodes in the graph +# while encoding the relationships. +class NeoGraph + # All the nodes in the graph. + var nodes: NeoNodeCollection + + # All the relationships in the graph. + var edges: SimpleCollection[NeoEdge] = new Array[NeoEdge] + + # Add a new node to the graph and return it. + # + # Set the local ID of the node before returning it. + # + # SEE: `NeoNodeCollection.add` + # SEE: `NeoNodeCollection.create_node` + # SEE: `NeoNodeCollection.register` + fun create_node: NeoNode do return nodes.create_node +end + +# All the nodes in a `NeoGraph`. +# +# An identification scheme can be defined throught the `register` and `add` +# methods. The `id_property` attribute defines where the local ID (that is the +# ID managed by the collection) is stored in each node. +abstract class NeoNodeCollection + super SimpleCollection[NeoNode] + + # The type of the local IDs. + type ID_TYPE: Jsonable + + # The property of the nodes that hold the local ID. + var id_property: String + + # Retrieve the node that has the specified local id. + # + # Note: The default implementation uses `get_or_null`. + fun [](id: ID_TYPE): NeoNode do + var n = get_or_null(id) + assert n isa NeoNode + return n + end + + # Retrieve the node that has the specified local id, or return `null`. + # + # Note: The default implementation uses `iterator`. + fun get_or_null(id: ID_TYPE): nullable NeoNode do + for n in self do + if id_of(n) == id then return n + end + return null + end + + # There is a node that has the specified local id? + # + # Note: The default implementation uses `get_or_null`. + fun has_id(id: ID_TYPE): Bool do return get_or_null(id) isa NeoNode + + # Return the local ID of the node. + fun id_of(node: NeoNode): ID_TYPE do return node[id_property].as(ID_TYPE) + + # Set the local ID of the specified node. + # + # Just update the property at `property_id`. Do not check anything. + protected fun id_of=(node: NeoNode, id: ID_TYPE) do + node[id_property] = id + end + + # Enlarge the collection to have at least the specified capacity. + # + # The capacity is specified in number of nodes. Used to minimize the + # number of times the collection need to be resized when adding nodes + # in batches. + # + # Do nothing by default. + fun enlarge(cap: Int) do end + + # Add the specified node to the graph and set its local ID. + # + # SEE: `add` + # SEE: `create_node` + fun register(node: NeoNode) is abstract + + # Add the specified node to the graph assuming that its local ID is already set. + # + # SEE: `create_node` + # SEE: `register` + redef fun add(node: NeoNode) is abstract + + # Add a new node to the graph and return it. + # + # Set the local ID of the node before returning it. + # + # SEE: `add` + # SEE: `register` + fun create_node: NeoNode do + var node = new NeoNode + register(node) + return node + end + + # Remove the node with the specified local ID. + fun remove_at(id: ID_TYPE) is abstract + + # Remove the specified node. + # + # The local ID is used instead of `==` to seek the node. + fun remove_node(node: NeoNode) do + remove_at(id_of(node)) + end + + redef fun clear do + for node in self do remove_node(node) + end + + redef fun remove(node: NeoNode) do + for n in self do + if node == n then + remove_node(n) + return + end + end + end + + redef fun remove_all(node: NeoNode) do + for n in self do + if node == n then remove_node(n) + end + end + + # Optimize the collection, possibly by rewritting it. + # + # The local ID of the elements may be changed by this method. + fun compact do end +end + +# A mean to save and load a Neo4j graph. +abstract class GraphStore + super Trackable + + # The graph to save or load. + var graph: NeoGraph + + # Can we save the graph without conflict? + fun isolated_save: Bool is abstract + + # Load the graph (or a part of it). + # + # Do not reset the graph. + fun load is abstract + + # Save the graph. + fun save do save_part(graph.nodes, graph.edges) + + # Save the specified part of the graph. + # + # Assume that for each relationship specified, both ends are already saved + # or are specified in the same call to this method. + fun save_part(nodes: Collection[NeoNode], + edges: Collection[NeoEdge]) is abstract +end + +# Save or load a graph using an actual Neo4j database. +class Neo4jGraphStore + super GraphStore + + # The maximum number of entities saved in one request. + # + # Also defines the granulity of the reported progression. + # + # TODO Also honor this limit in `load`. + var batch_max_size = 512 is writable + + # The Neo4j client to use. + var client: Neo4jClient + + # The label to use to retrieve the nodes. + var node_label: String + + private var done_part = 0 + private var total = 0 + + # Is the database already contains at least one node with the specified label? + fun has_node_label(name: String): Bool do + var query = new CypherQuery.from_string( + "match n where \{name\} in labels(n) return count(n)") + query.params["name"] = name + var data = client.cypher(query).as(JsonObject)["data"] + var result = data.as(JsonArray).first.as(JsonArray).first.as(Int) + return result > 0 + end + + redef fun isolated_save do return not has_node_label(node_label) + + redef fun load do + assert batch_max_size > 0 + fire_started + var db_nodes = client.nodes_with_label(node_label) + var nodes = graph.nodes + var edges = graph.edges + var i = 0 + + total = nodes.length * 2 + done_part = nodes.length + fire_progressed(done_part, total) + for node in db_nodes do + nodes.add(node) + edges.add_all(node.out_edges) + i += 1 + if i >= batch_max_size then + done_part += batch_max_size + fire_progressed(done_part, total) + end + end + fire_done + end + + redef fun save_part(nodes, edges) do + assert batch_max_size > 0 + fire_started + total = nodes.length + edges.length + done_part = 0 + + save_entities(nodes) + save_entities(edges) + fire_done + end + + # Save the specified entities. + private fun save_entities(neo_entities: Collection[NeoEntity]) do + var batch = new NeoBatch(client) + var batch_length = 0 + + for nentity in neo_entities do + batch.save_entity(nentity) + batch_length += 1 + if batch_length >= batch_max_size then + do_batch(batch) + done_part += batch_max_size + fire_progressed(done_part, total) + batch = new NeoBatch(client) + batch_length = 0 + end + end + do_batch(batch) + done_part += batch_length + end + + # Execute `batch` and check for errors. + # + # Abort if `batch.execute` returns errors. + private fun do_batch(batch: NeoBatch) do + var errors = batch.execute + assert errors.is_empty else + for e in errors do sys.stderr.write("{e}\n") + end + end +end diff --git a/lib/neo4j/graph/json_graph_store.nit b/lib/neo4j/graph/json_graph_store.nit new file mode 100644 index 0000000..9c97216 --- /dev/null +++ b/lib/neo4j/graph/json_graph_store.nit @@ -0,0 +1,321 @@ +# 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. + +# Provides JSON as a mean to store graphs. +module neo4j::graph::json_graph_store + +import graph + +# Save or load a graph using a JSON document. +# +# The graph (or the specified part of it) is stored as a JSON object with the +# following properties: +# +# * `"nodes"`: An array with all nodes. Each node is an object with the +# following properties: +# * `"labels"`: An array of all applied labels. +# * `"properties"`: An object mapping each defined property to its value. +# * `"edges"`: An array with all relationships. Each relationship is an object +# with the following properties: +# * `"type"`: The type (`String`) of the relationship. +# * `"properties"`: An object mapping each defined property to its value. +# * `"from"`: The local ID of the source node. +# * `"to"`: The local ID of the destination node. +# +# ~~~nit +# import neo4j::graph::sequential_id +# +# var graph = new NeoGraph(new SequentialNodeCollection("nid")) +# var a = new NeoNode +# a.labels.add "Foo" +# a["answer"] = 42 +# a["Ultimate question of"] = new JsonArray.from(["life", +# "the Universe", "and Everything."]) +# graph.nodes.register a +# var b = graph.create_node +# b.labels.add "Foo" +# b.labels.add "Bar" +# graph.edges.add new NeoEdge(a, "BAZ", b) +# +# var ostream = new StringOStream +# var store = new JsonGraphStore(graph) +# store.ostream = ostream +# store.save +# assert ostream.to_s == """{"nodes":[""" + """ +# {"labels":["Foo"],"properties":{"answer":42,""" + """ +# "Ultimate question of":["life","the Universe","and Everything."],""" + """ +# "nid":1}},""" + """ +# {"labels":["Foo","Bar"],"properties":{"nid":2}}],""" + """ +# "edges":[{"type":"BAZ","properties":{},"from":1,"to":2}]}""" +# +# graph.nodes.clear +# graph.edges.clear +# store.istream = new StringIStream(ostream.to_s) +# store.load +# assert 1 == graph.edges.length +# for edge in graph.edges do +# assert "BAZ" == edge.rel_type +# assert a.labels == edge.from.labels +# for k, v in a.properties do assert v == edge.from.properties[k] +# assert b.labels == edge.to.labels +# for k, v in b.properties do assert v == edge.to.properties[k] +# end +# assert 2 == graph.nodes.length +# ~~~ +class JsonGraphStore + super GraphStore + + # The stream to use for `load`. + var istream: nullable IStream = null is writable + + # The stream to use for `save` and `save_part`. + var ostream: nullable OStream = null is writable + + # Use the specified `IOStream`. + init from_io(graph: NeoGraph, iostream: IOStream) do + init(graph) + istream = iostream + ostream = iostream + end + + # Use the specified string to load the graph. + init from_string(graph: NeoGraph, string: String) do + init(graph) + istream = new StringIStream(string) + end + + redef fun isolated_save do return true + + redef fun load do + var istream = self.istream + assert istream isa IStream + fire_started + graph.load_json(istream.read_all) + fire_done + end + + redef fun save_part(nodes, edges) do + var ostream = self.ostream + assert ostream isa OStream + fire_started + ostream.write(graph.to_json) + fire_done + end +end + +redef class NeoGraph + super Jsonable + + # Retrieve the graph from the specified JSON document. + # + # For the expected format, see `JsonGraphStore`. + # + # ~~~nit + # import neo4j::graph::sequential_id + # + # var graph = new NeoGraph(new SequentialNodeCollection("node_id")) + # var a = new NeoNode + # a.labels.add "Foo" + # a["answer"] = 42 + # a["Ultimate question of"] = new JsonArray.from(["life", + # "the Universe", "and Everything."]) + # graph.nodes.register a + # var b = graph.create_node + # b.labels.add "Foo" + # b.labels.add "Bar" + # graph.edges.add new NeoEdge(a, "BAZ", b) + # + # graph = new NeoGraph.from_json( + # new SequentialNodeCollection("node_id"), graph.to_json) + # assert 1 == graph.edges.length + # for edge in graph.edges do + # assert "BAZ" == edge.rel_type + # assert a.labels == edge.from.labels + # for k, v in a.properties do assert v == edge.from.properties[k] + # assert b.labels == edge.to.labels + # for k, v in b.properties do assert v == edge.to.properties[k] + # end + # assert 2 == graph.nodes.length + # ~~~ + init from_json(nodes: NeoNodeCollection, t: Text) do + from_json_object(nodes, t.parse_json.as(JsonObject)) + end + + # Retrieve the graph from the specified JSON object. + # + # For the expected format, see `JsonGraphStore`. + init from_json_object(nodes: NeoNodeCollection, o: JsonObject) do + init(nodes) + load_json_object(o) + end + + # Retrieve a part of the graph from the specified JSON document. + # + # For the expected format, see `JsonGraphStore`. + fun load_json(t: Text) do + load_json_object(t.parse_json.as(JsonObject)) + end + + # Retrieve a part of the graph from the specified JSON object. + # + # For the expected format, see `JsonGraphStore`. + fun load_json_object(o: JsonObject) do + var json_nodes = o["nodes"].as(JsonArray) + var nodes = self.nodes + nodes.enlarge(nodes.length) + for json_node in json_nodes do + assert json_node isa JsonObject + var node = new NeoNode.from_json_object(json_node) + nodes.add node + end + + var json_edges = o["edges"].as(JsonArray) + var edges = self.edges + if edges isa AbstractArray[NeoEdge] then edges.enlarge(edges.length) + for json_edge in json_edges do + assert json_edge isa JsonObject + var from = nodes[nodes.id_from_jsonable(json_edge["from"])] + var to = nodes[nodes.id_from_jsonable(json_edge["to"])] + var rel_type = json_edge["type"].as(String) + var json_properties = json_edge["properties"].as(JsonObject) + var edge = new NeoEdge(from, rel_type, to) + edge.properties.recover_with(json_properties) + edges.add edge + end + end + + redef fun to_json do return to_json_by_append + + # Append the JSON representation of `self` to the specified buffer. + # + # For a description of the format, see `JsonGraphStore`. + # + # SEE: `to_json` + redef fun append_json(b) do + b.append "\{\"nodes\":[" + append_entities_json(nodes, b) + b.append "],\"edges\":[" + append_entities_json(edges, b) + b.append "]\}" + end + + # Encode `self` in JSON. + # + # For a description of the format, see `JsonGraphStore`. + # + # SEE: `append_json` + private fun append_entities_json(entities: Collection[NeoEntity], + b: Buffer) do + var i = entities.iterator + if i.is_ok then + i.item.append_json_for(self, b) + i.next + for entity in i do + b.add ',' + entity.append_json_for(self, b) + end + end + end +end + +redef class NeoNodeCollection + # Convert the specified JSON value into a local ID. + fun id_from_jsonable(id: nullable Jsonable): ID_TYPE do return id.as(ID_TYPE) +end + +redef class NeoEntity + + # Append the JSON representation of the entity to the specified buffer. + fun append_json_for(graph: NeoGraph, buffer: Buffer) is abstract +end + +# Make `NeoNode` `Jsonable`. +redef class NeoNode + super Jsonable + + # Retrieve the node from the specified JSON value. + # + # Note: Here, the `"id"` is optional and ignored. + # + # SEE: `JsonGraph` + # + # var node = new NeoNode.from_json(""" + # { + # "labels": ["foo", "Bar"], + # "properties": { + # "baz": 42 + # } + # } + # """) + # assert ["foo", "Bar"] == node.labels + # assert 42 == node["baz"] + init from_json(t: Text) do + from_json_object(t.parse_json.as(JsonObject)) + end + + # Retrieve the node from the specified JSON value. + # + # Note: Here, the `"id"` is optional and ignored. + # + # SEE: `JsonGraph` + init from_json_object(o: JsonObject) do + init + var labels = o["labels"].as(JsonArray) + for lab in labels do self.labels.add(lab.as(String)) + var json_properties = o["properties"].as(JsonObject) + properties.recover_with(json_properties) + end + + redef fun to_json do return to_json_by_append + + # Append the JSON representation of the node to the specified buffer. + # + # SEE: `JsonGraph` + redef fun append_json(b) do + b.append "\{\"labels\":[" + var i = labels.iterator + if i.is_ok then + i.item.append_json(b) + i.next + for lab in i do + b.add ',' + lab.append_json(b) + end + end + b.append "],\"properties\":" + properties.append_json(b) + b.add '}' + end + + redef fun to_s do return to_json + + # Append the JSON representation of the node to the specified buffer. + redef fun append_json_for(graph: NeoGraph, buffer: Buffer) do + append_json(buffer) + end +end + +redef class NeoEdge + + # Append the JSON representation of the relationship to the specified buffer. + # + # Use the IDs specfied by `graph.nodes`. + redef fun append_json_for(graph: NeoGraph, buffer: Buffer) do + buffer.append "\{\"type\":" + rel_type.append_json(buffer) + buffer.append ",\"properties\":" + properties.append_json(buffer) + buffer.append ",\"from\":" + graph.nodes.id_of(from).append_json(buffer) + buffer.append ",\"to\":" + graph.nodes.id_of(to).append_json(buffer) + buffer.append "}" + end +end diff --git a/lib/neo4j/graph/sequential_id.nit b/lib/neo4j/graph/sequential_id.nit new file mode 100644 index 0000000..fb6129f --- /dev/null +++ b/lib/neo4j/graph/sequential_id.nit @@ -0,0 +1,116 @@ +# 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. + +# Provides a sequential identification scheme for Neo4j nodes. +module neo4j::graph::sequential_id + +import graph +private import pipeline + + +# A Neo4j node collection using a sequential identification scheme. +# +# The local IDs are sequential numbers (integers) starting at `1`. +# +# Note: When loading nodes, the local IDs should forms a mostly contiguous +# range starting at `1`. Else, this collection will consume a lot of memory. +# Futhermore, the local IDs **must** be positive. +# +# ~~~nit +# var nodes = new SequentialNodeCollection("id") +# var a = nodes.create_node +# var b = new NeoNode +# var c = new NeoNode +# +# nodes.register b +# c["id"] = 4 +# nodes.add c +# assert a["id"] == 1 +# assert b["id"] == 2 +# assert c["id"] == 4 +# assert nodes.to_a == [a, b, c] +# assert nodes.length == 3 +# +# nodes.compact +# assert a["id"] == 1 +# assert b["id"] == 2 +# assert c["id"] == 3 +# assert nodes.to_a == [a, b, c] +# assert nodes.length == 3 +# ~~~ +class SequentialNodeCollection + super NeoNodeCollection + + redef type ID_TYPE: Int + + private var nodes = new Array[nullable NeoNode] + + redef var length = 0 + + redef fun iterator do return new NullSkipper[NeoNode](self.nodes.iterator) + + redef fun [](id) do return nodes[id].as(NeoNode) + + redef fun get_or_null(id) do + if id < 0 or id > nodes.length then return null + return nodes[id] + end + + redef fun has_id(id: Int): Bool do + return id >= 0 and id < nodes.length and nodes[id] isa NeoNode + end + + redef fun enlarge(cap) do nodes.enlarge(cap) + + redef fun register(node) do + nodes.add node + id_of(node) = nodes.length + length += 1 + end + + redef fun add(node) do + var id = node[id_property] + assert id isa Int else + sys.stderr.write "The local ID must be an `Int`.\n" + end + assert id >= 0 else + sys.stderr.write "The local ID must be greater or equal to 0. Got {id}.\n" + end + # Pad with nulls. + nodes.enlarge(id) + var delta = id - nodes.length + while delta > 0 do + nodes.add null + delta -= 1 + end + nodes[id] = node + length += 1 + end + + redef fun remove_at(id) do + nodes[id] = null + length -= 1 + end + + redef fun clear do + nodes.clear + length = 0 + end + + redef fun compact do + var i = iterator + + nodes = new Array[nullable NeoNode] + for n in i do + nodes.add n + id_of(n) = nodes.length + end + end +end diff --git a/lib/neo4j/json_store.nit b/lib/neo4j/json_store.nit deleted file mode 100644 index cf9f441..0000000 --- a/lib/neo4j/json_store.nit +++ /dev/null @@ -1,199 +0,0 @@ -# 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. - -# Uses JSON as a storage medium for a Neo4j subgraph. -module neo4j::json_store - -import neo4j -private import template - -# A Neo4j graph that uses as a storage medium. -# -# The graph is stored as a JSON object with the following properties: -# -# * `"nodes"`: An array with all nodes. Each node is an object with the -# following properties: -# * `"id"`: The ID (`Int`) that uniquely identifies the node in the current -# graph. -# * `"labels"`: An array of all applied labels. -# * `"properties"`: An object mapping each defined property to its value. -# * `"links"`: An array with all relationships. Each relationship is an object -# with the following properties: -# * `"type"`: The type (`String`) of the relationship. -# * `"properties"`: An object mapping each defined property to its value. -# * `"from"`: The ID (`Int`) of the source node. -# * `"to"`: The ID (`Int`) of the destination node. -# -# TODO Refine the graph API instead when it will be available. -class JsonGraph - super Jsonable - - # All nodes in the graph. - var nodes: SimpleCollection[NeoNode] = new Array[NeoNode] - - # All relationships in the graph. - var links: SimpleCollection[NeoEdge] = new Array[NeoEdge] - - # Create an empty graph. - init do end - - # Retrieve the graph from the specified JSON value. - # - # var graph = new JsonGraph - # var a = new NeoNode - # a.labels.add "Foo" - # a["answer"] = 42 - # a["Ultimate question of"] = new JsonArray.from(["life", - # "the Universe", "and Everything."]) - # graph.nodes.add a - # var b = new NeoNode - # b.labels.add "Foo" - # b.labels.add "Bar" - # graph.nodes.add b - # graph.links.add new NeoEdge(a, "BAZ", b) - # # - # graph = new JsonGraph.from_json(graph.to_json) - # assert 1 == graph.links.length - # for link in graph.links do - # assert "BAZ" == link.rel_type - # assert a.labels == link.from.labels - # for k, v in a.properties do assert v == link.from.properties[k] - # assert b.labels == link.to.labels - # for k, v in b.properties do assert v == link.to.properties[k] - # end - # assert 2 == graph.nodes.length - init from_json(t: Text) do - from_json_object(t.parse_json.as(JsonObject)) - end - - # Retrieve the graph from the specified JSON object. - init from_json_object(o: JsonObject) do - var node_by_id = new HashMap[Int, NeoNode] - var nodes = o["nodes"].as(JsonArray) - for json_node in nodes do - assert json_node isa JsonObject - var node = new NeoNode.from_json_object(json_node) - node_by_id[json_node["id"].as(Int)] = node - self.nodes.add node - end - var links = o["links"].as(JsonArray) - for json_link in links do - assert json_link isa JsonObject - var from = node_by_id[json_link["from"].as(Int)] - var to = node_by_id[json_link["to"].as(Int)] - var rel_type = json_link["type"].as(String) - var json_properties = json_link["properties"].as(JsonObject) - var link = new NeoEdge(from, rel_type, to) - link.properties.recover_with(json_properties) - self.links.add link - end - end - - redef fun to_json do - var t = new Template - t.add "\{\"nodes\":[" - var i = 0 - for n in nodes do - if i > 0 then t.add "," - t.add n.to_json - i += 1 - end - t.add "],\"links\":[" - i = 0 - for link in links do - if i > 0 then t.add "," - t.add link.to_json - i += 1 - end - t.add "]\}" - return t.write_to_string - end -end - -# Make `NeoNode` `Jsonable`. -redef class NeoNode - super Jsonable - - # Retrieve the node from the specified JSON value. - # - # Note: Here, the `"id"` is optional and ignored. - # - # SEE: `JsonGraph` - # - # var node = new NeoNode.from_json(""" - # { - # "labels": ["foo", "Bar"], - # "properties": { - # "baz": 42 - # } - # } - # """) - # assert ["foo", "Bar"] == node.labels - # assert 42 == node["baz"] - init from_json(t: Text) do - from_json_object(t.parse_json.as(JsonObject)) - end - - # Retrieve the node from the specified JSON value. - # - # Note: Here, the `"id"` is optional and ignored. - # - # SEE: `JsonGraph` - init from_json_object(o: JsonObject) do - init - var labels = o["labels"].as(JsonArray) - for lab in labels do self.labels.add(lab.as(String)) - var json_properties = o["properties"].as(JsonObject) - properties.recover_with(json_properties) - end - - # Get the JSON representation of `self`. - # - # SEE: `JsonGraph` - redef fun to_json do - var t = new Template - t.add "\{\"id\":" - t.add object_id.to_json - t.add ",\"labels\":[" - var i = 0 - for lab in labels do - if i > 0 then t.add "," - t.add lab.to_json - i += 1 - end - t.add "],\"properties\":" - t.add properties.to_json - t.add "}" - return t.write_to_string - end - - redef fun to_s do return to_json -end - -# Make `NeoEdge` `Jsonable`. -redef class NeoEdge - super Jsonable - - redef fun to_json do - var t = new Template - t.add "\{\"type\":" - t.add rel_type.to_json - t.add ",\"properties\":" - t.add properties.to_json - t.add ",\"from\":" - t.add from.object_id.to_json - t.add ",\"to\":" - t.add to.object_id.to_json - t.add "}" - return t.write_to_string - end - - redef fun to_s do return to_json -end diff --git a/lib/nitcc_runtime.nit b/lib/nitcc_runtime.nit index 9c5ec01..31c0a45 100644 --- a/lib/nitcc_runtime.nit +++ b/lib/nitcc_runtime.nit @@ -374,10 +374,10 @@ end private class DephIterator super Iterator[Node] + var stack = new List[Iterator[nullable Node]] - init(i: Iterator[nullable Node]) - do + init(i: Iterator[nullable Node]) is old_style_init do stack.add i end diff --git a/lib/opts.nit b/lib/opts.nit index 8c8f9ea..fa5ce15 100644 --- a/lib/opts.nit +++ b/lib/opts.nit @@ -44,11 +44,13 @@ abstract class Option var default_value: VALUE is writable # Create a new option - init(help: String, default: VALUE, names: nullable Array[String]) - do + init(help: String, default: VALUE, names: nullable Array[String]) is old_style_init do init_opt(help, default, names) end + # Init option `helptext`, `default_value` and `names`. + # + # Also set current `value` to `default`. fun init_opt(help: String, default: VALUE, names: nullable Array[String]) do if names == null then @@ -80,6 +82,7 @@ abstract class Option return text.to_s end + # Pretty print the default value. fun pretty_default: String do var dv = default_value @@ -97,7 +100,9 @@ end # Not really an option. Just add a line of text when displaying the usage class OptionText super Option - init(text: String) do super(text, null, null) + + # Init a new OptionText with `text`. + init(text: String) is old_style_init do super(text, null, null) redef fun pretty(off) do return to_s @@ -109,7 +114,8 @@ class OptionBool super Option redef type VALUE: Bool - init(help: String, names: String...) do super(help, false, names) + # Init a new OptionBool with a `help` message and `names`. + init(help: String, names: String...) is old_style_init do super(help, false, names) redef fun read_param(it) do @@ -123,7 +129,8 @@ class OptionCount super Option redef type VALUE: Int - init(help: String, names: String...) do super(help, 0, names) + # Init a new OptionCount with a `help` message and `names`. + init(help: String, names: String...) is old_style_init do super(help, 0, names) redef fun read_param(it) do @@ -135,6 +142,8 @@ end # Option with one parameter (mandatory by default) abstract class OptionParameter super Option + + # Convert `str` to a value of type `VALUE`. protected fun convert(str: String): VALUE is abstract # Is the parameter mandatory? @@ -159,7 +168,8 @@ class OptionString super OptionParameter redef type VALUE: nullable String - init(help: String, names: String...) do super(help, null, names) + # Init a new OptionString with a `help` message and `names`. + init(help: String, names: String...) is old_style_init do super(help, null, names) redef fun convert(str) do return str end @@ -170,10 +180,14 @@ end class OptionEnum super OptionParameter redef type VALUE: Int + + # Values in the enumeration. var values: Array[String] - init(values: Array[String], help: String, default: Int, names: String...) - do + # Init a new OptionEnum from `values` with a `help` message and `names`. + # + # `default` is the index of the default value in `values`. + init(values: Array[String], help: String, default: Int, names: String...) is old_style_init do assert values.length > 0 self.values = values.to_a super("{help} <{values.join(", ")}>", default, names) @@ -190,6 +204,7 @@ class OptionEnum return id end + # Get the value name from `values`. fun value_name: String do return values[value] redef fun pretty_default @@ -203,7 +218,10 @@ class OptionInt super OptionParameter redef type VALUE: Int - init(help: String, default: Int, names: String...) do super(help, default, names) + # Init a new OptionInt with a `help` message, a `default` value and `names`. + init(help: String, default: Int, names: String...) is old_style_init do + super(help, default, names) + end redef fun convert(str) do return str.to_i end @@ -213,7 +231,10 @@ class OptionFloat super OptionParameter redef type VALUE: Float - init(help: String, default: Float, names: String...) do super(help, default, names) + # Init a new OptionFloat with a `help` message, a `default` value and `names`. + init(help: String, default: Float, names: String...) is old_style_init do + super(help, default, names) + end redef fun convert(str) do return str.to_f end @@ -224,8 +245,8 @@ class OptionArray super OptionParameter redef type VALUE: Array[String] - init(help: String, names: String...) - do + # Init a new OptionArray with a `help` message and `names`. + init(help: String, names: String...) is old_style_init do values = new Array[String] super(help, values, names) end @@ -352,6 +373,7 @@ class OptionContext end end + # Options parsing errors. fun get_errors: Array[String] do var errors = new Array[String] diff --git a/lib/pipeline.nit b/lib/pipeline.nit index be553ad..0ca08b2 100644 --- a/lib/pipeline.nit +++ b/lib/pipeline.nit @@ -159,6 +159,45 @@ redef interface Iterator[E] end end +# Wraps an iterator to skip nulls. +# +# ~~~nit +# var i: Iterator[Int] +# +# i = new NullSkipper[Int]([null, 1, null, 2, null: nullable Int].iterator) +# assert i.to_a == [1, 2] +# +# i = new NullSkipper[Int]([1, null, 2, 3: nullable Int].iterator) +# assert i.to_a == [1, 2, 3] +# ~~~ +class NullSkipper[E: Object] + super Iterator[E] + + # The inner iterator. + var inner: Iterator[nullable E] + + redef fun finish do inner.finish + + redef fun is_ok do + skip_nulls + return inner.is_ok + end + + redef fun item do + skip_nulls + return inner.item.as(E) + end + + redef fun next do + inner.next + skip_nulls + end + + private fun skip_nulls do + while inner.is_ok and inner.item == null do inner.next + end +end + # Interface that reify a function. # Concrete subclasses must implements the `apply` method. # diff --git a/lib/progression.nit b/lib/progression.nit new file mode 100644 index 0000000..b5d7f96 --- /dev/null +++ b/lib/progression.nit @@ -0,0 +1,68 @@ +# 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. + +# Event-based interface to track the progression of an operation. +module progression + +# An operation that is trackable using a `ProgressionListener`. +abstract class Trackable + + # Listen to the progression of the operation. + var progression_listeners: SimpleCollection[ProgressionListener] = + new Array[ProgressionListener] + + # Notice the registered `ProgessionListener` that the operation started. + protected fun fire_started do + for l in progression_listeners do + l.started + l.progressed(0) + end + end + + # Notice the registered `ProgessionListener` that the operation progressed. + # + # Parameter: + # + # * `done_part`: Indicates what is done. + # * `total`: Indicates what need to be done, `done_part` included. + protected fun fire_progressed(done_part: Int, total: Int) do + for l in progression_listeners do + l.progressed(done_part * l.progression_max / total) + end + end + + # Notice the registered `ProgessionListener` that the operation is done. + protected fun fire_done do + for l in progression_listeners do + l.progressed(l.progression_max) + l.done + end + end +end + +# Listens to the progression of a possibly long-running operation. +interface ProgressionListener + # The number that represents a completed operation. + fun progression_max: Int do return 100 + + # The operation started. + fun started do end + + # The operation progressed. + # + # Parameter: + # + # * `progression`: Indicator of the progession, between `0` and + # `progression_max`. + fun progressed(progression: Int) do end + + # The operation is done. + fun done do end +end diff --git a/lib/scene2d.nit b/lib/scene2d.nit index b01c3e5..aa567e8 100644 --- a/lib/scene2d.nit +++ b/lib/scene2d.nit @@ -48,9 +48,16 @@ class Sprite # height of the sprite var height: Int = 100 is writable + # X coordinate of left side. fun left: Int do return x - width/2 + + # X coordinate of right side. fun right: Int do return x + width/2 + + # Y coordinate of top. fun top: Int do return y - height/2 + + # Y coordinate of bottom. fun bottom: Int do return y + height/2 # x velocity (applied by `update') @@ -102,10 +109,6 @@ class LiveGroup[E: LiveObject] super LiveObject super List[E] - init - do - end - # Recursively update each live objects that `exists' redef fun update do diff --git a/lib/sdl.nit b/lib/sdl.nit index 5225f9f..16695e2 100644 --- a/lib/sdl.nit +++ b/lib/sdl.nit @@ -21,6 +21,7 @@ module sdl is end import mnit_display +import c in "C header" `{ #include @@ -160,7 +161,26 @@ extern class SDLDisplay `{SDL_Surface *`} fun warp_mouse(x,y: Int) `{ SDL_WarpMouse(x, y); `} # Show or hide the cursor - fun show_cursor(show: Bool) `{ SDL_ShowCursor(show); `} + fun show_cursor=(val: Bool) `{ SDL_ShowCursor(val? SDL_ENABLE: SDL_DISABLE); `} + + # Is the cursor visible? + fun show_cursor: Bool `{ SDL_ShowCursor(SDL_QUERY); `} + + # Grab or release the input + fun grab_input=(val: Bool) `{ SDL_WM_GrabInput(val? SDL_GRAB_ON: SDL_GRAB_OFF); `} + + # Is the input grabbed? + fun grab_input: Bool `{ SDL_WM_GrabInput(SDL_GRAB_QUERY) == SDL_GRAB_ON; `} + + # Are instances of `SDLMouseMotionEvent` ignored? + fun ignore_mouse_motion_events: Bool `{ + return SDL_EventState(SDL_MOUSEMOTION, SDL_QUERY); + `} + + # Do not raise instances of `SDLMouseMotionEvent` if `val` + fun ignore_mouse_motion_events=(val: Bool) `{ + SDL_EventState(SDL_MOUSEMOTION, val? SDL_IGNORE: SDL_ENABLE); + `} end # Basic Drawing figures @@ -225,6 +245,12 @@ extern class SDLImage redef fun height: Int `{ return recv->h; `} fun is_ok: Bool do return not address_is_null + + # Returns a reference to the pixels of the texture + fun pixels: NativeCByteArray `{ return recv->pixels; `} + + # Does this texture has an alpha mask? + fun amask: Bool `{ return recv->format->Amask; `} end # A simple rectangle @@ -284,10 +310,10 @@ class SDLMouseButtonEvent fun is_left_button: Bool do return button == 1 # Is this event raised by the right button? - fun is_right_button: Bool do return button == 2 + fun is_right_button: Bool do return button == 3 # Is this event raised by the middle button? - fun is_middle_button: Bool do return button == 3 + fun is_middle_button: Bool do return button == 2 # Is this event raised by the wheel going down? fun is_down_wheel: Bool do return button == 4 diff --git a/lib/socket/examples/socket_client.nit b/lib/socket/examples/socket_client.nit index b488028..3f38ab4 100644 --- a/lib/socket/examples/socket_client.nit +++ b/lib/socket/examples/socket_client.nit @@ -21,18 +21,23 @@ import socket if args.length < 2 then print "Usage : socket_client " - return + exit 1 end -var s = new Socket.client(args[0], args[1].to_i) -print "[HOST ADDRESS] : {s.address}" -print "[HOST] : {s.host or else "unamed"}" -print "[PORT] : {s.port}" -print "Connecting ... {s.connected}" +var address = args[0] +var port = args[1].to_i + +# Open a conection with the server +var s = new TCPStream.connect(address, port) +printn "Connecting to {s.host}:{s.port} at {s.address}... " +print if s.connected then "Connected" else "Connection failed" + if s.connected then - print "Writing ... Hello server !" - s.write("Hello server !") - print "[Response from server] : {s.read(100)}" - print "Closing ..." + # Communicate + s.write "Hello server!\n" + print s.read_line + s.write "Bye server!\n" + print s.read_line + s.close end diff --git a/lib/socket/examples/socket_server.nit b/lib/socket/examples/socket_server.nit index c435320..17bfcab 100644 --- a/lib/socket/examples/socket_server.nit +++ b/lib/socket/examples/socket_server.nit @@ -24,27 +24,31 @@ if args.is_empty then return end -var socket = new Socket.server(args[0].to_i, 1) +var socket = new TCPServer(args[0].to_i) +socket.listen 1 print "[PORT] : {socket.port.to_s}" -var clients = new Array[Socket] +var clients = new Array[TCPStream] var max = socket loop var fs = new SocketObserver(true, true, true) - fs.readset.set(socket) + fs.read_set.add socket - for c in clients do fs.readset.set(c) + for c in clients do fs.read_set.add c + printn "." if fs.select(max, 4, 0) == 0 then print "Error occured in select {sys.errno.strerror}" break end - if fs.readset.is_set(socket) then + if fs.read_set.has(socket) then var ns = socket.accept print "Accepting {ns.address} ... " - print "[Message from {ns.address}] : {ns.read(100)}" - ns.write("Goodbye client.") + print "[Message from {ns.address}] : {ns.read_line}" + ns.write "Hello client.\n" + print "[Message from {ns.address}] : {ns.read_line}" + ns.write "Bye client.\n" print "Closing {ns.address} ..." ns.close end diff --git a/lib/socket/examples/socket_simple_server.nit b/lib/socket/examples/socket_simple_server.nit new file mode 100644 index 0000000..54f9ea3 --- /dev/null +++ b/lib/socket/examples/socket_simple_server.nit @@ -0,0 +1,50 @@ +# 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. + +# Simple server example using a non-blocking `TCPServer` +module socket_simple_server + +import socket + +if args.is_empty then + print "Usage : socket_simple_server " + exit 1 +end + +var port = args[0].to_i + +# Open the listening socket +var socket = new TCPServer(port) +socket.listen 4 +socket.blocking = false + +print "Listening on port {socket.port}" + +# Loop until a single client connects +var client: nullable TCPStream = null +while client == null do + printn "." + sys.nanosleep(0, 200000) + + client = socket.accept +end +print " Connected" + +# Communicate +print client.read_line +client.write "Hello client!\n" +print client.read_line +client.write "Bye client!\n" + +client.close diff --git a/lib/socket/socket.nit b/lib/socket/socket.nit index aad6449..d9bc5a3 100644 --- a/lib/socket/socket.nit +++ b/lib/socket/socket.nit @@ -17,82 +17,80 @@ # Socket services module socket -import socket_c +private import socket_c intrude import standard::stream -# Portal for communication between two machines -class Socket - super BufferedIStream - super OStream - super PollableIStream +# A general TCP socket, either a `TCPStream` or a `TCPServer` +abstract class Socket + + # Underlying C socket + private var socket: NativeSocket is noinit - # IPv4 address the socket is connected to - # Formatted as xxx.xxx.xxx.xxx + # Port used by the socket + var port: Int + + # IPv4 address to which `self` is connected + # + # Formatted as xxx.xxx.xxx.xxx. var address: String is noinit - # Hostname of the socket connected to self - # In C : The real canonical host name (e.g. example.org) - var host: nullable String = null + # Is this socket closed? + var closed = false +end - # Port open for the socket - var port: Int is noinit +# Simple communication stream with a remote socket +class TCPStream + super Socket + super BufferedIStream + super OStream + super PollableIStream - # Underlying C socket - private var socket: FFSocket is noinit + # Real canonical name of the host to which `self` is connected + var host: String - # Underlying C socket - private var addrin: FFSocketAddrIn is noinit + private var addrin: NativeSocketAddrIn is noinit redef var end_reached = false - # Creates a socket connection to host `thost` on port `port` - init client(thost: String, tport: Int) + # TODO make init private + + # Creates a socket connection to host `host` on port `port` + init connect(host: String, port: Int) do _buffer = new FlatBuffer _buffer_pos = 0 - socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null ) + socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet, + new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null) if socket.address_is_null then end_reached = true + closed = true return end - socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1) - var hostname = socket.gethostbyname(thost) - addrin = new FFSocketAddrIn.with_hostent(hostname, tport) - address = addrin.address - host = hostname.h_name - port = addrin.port - if not end_reached then end_reached = not connect - end - - # Creates a server socket on port `tport`, with a connection queue of size `max` - init server(tport: Int, max: Int) - do - _buffer = new FlatBuffer - _buffer_pos = 0 - socket = new FFSocket.socket( new FFSocketAddressFamilies.af_inet, new FFSocketTypes.sock_stream, new FFSocketProtocolFamilies.pf_null ) - if socket.address_is_null then + if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then end_reached = true + closed = true return end - socket.setsockopt(new FFSocketOptLevels.socket, new FFSocketOptNames.reuseaddr, 1) - addrin = new FFSocketAddrIn.with(tport, new FFSocketAddressFamilies.af_inet) + var hostname = socket.gethostbyname(host) + addrin = new NativeSocketAddrIn.with_hostent(hostname, port) + address = addrin.address - port = addrin.port - host = null - bind - listen(max) + init(addrin.port, hostname.h_name) + + closed = not internal_connect + end_reached = closed end # Creates a client socket, this is meant to be used by accept only - private init primitive_init(h: FFSocketAcceptResult) + private init server_side(h: SocketAcceptResult) do _buffer = new FlatBuffer _buffer_pos = 0 socket = h.socket - addrin = h.addrIn + addrin = h.addr_in address = addrin.address - port = addrin.port - host = null + + init(addrin.port, address) end redef fun poll_in do return ready_to_read(0) @@ -102,37 +100,31 @@ class Socket # event_types : Combination of several event types to watch # # timeout : Time in milliseconds before stopping listening for events on this socket - # - private fun pollin(event_types: Array[FFSocketPollValues], timeout: Int): Array[FFSocketPollValues] do - if end_reached then return new Array[FFSocketPollValues] + private fun pollin(event_types: Array[NativeSocketPollValues], timeout: Int): Array[NativeSocketPollValues] do + if end_reached then return new Array[NativeSocketPollValues] return socket.socket_poll(new PollFD(socket.descriptor, event_types), timeout) end # Easier use of pollin to check for something to read on all channels of any priority # # timeout : Time in milliseconds before stopping to wait for events - # fun ready_to_read(timeout: Int): Bool do if _buffer_pos < _buffer.length then return true - if eof then return false - var events = new Array[FFSocketPollValues] - events.push(new FFSocketPollValues.pollin) + if end_reached then return false + var events = [new NativeSocketPollValues.pollin] return pollin(events, timeout).length != 0 end # Checks if the socket still is connected - # fun connected: Bool do - if eof then return false - var events = new Array[FFSocketPollValues] - events.push(new FFSocketPollValues.pollhup) - events.push(new FFSocketPollValues.pollerr) + if closed then return false + var events = [new NativeSocketPollValues.pollhup, new NativeSocketPollValues.pollerr] if pollin(events, 0).length == 0 then return true else - end_reached = true + closed = true return false end end @@ -142,16 +134,16 @@ class Socket # Establishes a connection to socket addrin # # REQUIRES : not self.end_reached - private fun connect: Bool + private fun internal_connect: Bool do - assert not end_reached + assert not closed return socket.connect(addrin) >= 0 end # If socket.end_reached, nothing will happen redef fun write(msg: Text) do - if end_reached then return + if closed then return socket.write(msg.to_s) end @@ -175,66 +167,142 @@ class Socket _buffer.append(read) end - redef fun close do - if end_reached then return + redef fun close + do + if closed then return if socket.close >= 0 then + closed = true end_reached = true end end + # Send the data present in the socket buffer + fun flush + do + if not socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 1) or + not socket.setsockopt(new NativeSocketOptLevels.tcp, new NativeSocketOptNames.tcp_nodelay, 0) then + closed = true + end + end +end + +# A socket listening on a given `port` for incomming connections +# +# Create streams to communicate with clients using `accept`. +class TCPServer + super Socket + + private var addrin: NativeSocketAddrIn is noinit + + # Create and bind a listening server socket on port `port` + init + do + socket = new NativeSocket.socket(new NativeSocketAddressFamilies.af_inet, + new NativeSocketTypes.sock_stream, new NativeSocketProtocolFamilies.pf_null) + assert not socket.address_is_null + if not socket.setsockopt(new NativeSocketOptLevels.socket, new NativeSocketOptNames.reuseaddr, 1) then + closed = true + return + end + addrin = new NativeSocketAddrIn.with(port, new NativeSocketAddressFamilies.af_inet) + address = addrin.address + + # Bind it + closed = not bind + end + # Associates the socket to a local address and port # - # Returns : `true` if the socket could be bound, `false` otherwise + # Returns whether the socket has been be bound. private fun bind: Bool do - if end_reached then return false return socket.bind(addrin) >= 0 end # Sets the socket as ready to accept incoming connections, `size` is the maximum number of queued clients # - # Returns : `true` if the socket could be set, `false` otherwise - private fun listen(size: Int): Bool do - if end_reached then return false + # Returns `true` if the socket could be set, `false` otherwise + fun listen(size: Int): Bool do return socket.listen(size) >= 0 end # Accepts an incoming connection from a client - # This creates a new socket that represents the connection to a client # - # Returns : the socket for communication with the client + # Create and return a new socket to the client. May return null if not + # `blocking` and there's no waiting clients, or upon an interruption + # (whether `blocking` or not). # - # REQUIRES : not self.end_reached - fun accept: Socket do - assert not end_reached - return new Socket.primitive_init(socket.accept) + # Require: not closed + fun accept: nullable TCPStream + do + assert not closed + var native = socket.accept + if native == null then return null + return new TCPStream.server_side(native) end + # Set whether calls to `accept` are blocking + fun blocking=(value: Bool) + do + # We use the opposite from the native version as the native API + # is closer to the C API. In the Nity API, we use a positive version + # of the name. + socket.non_blocking = not value + end + + # Close this socket + fun close + do + # FIXME unify with `SocketStream::close` when we can use qualified names + + if closed then return + if socket.close >= 0 then + closed = true + end + end end +# A simple set of sockets used by `SocketObserver` class SocketSet - var sset = new FFSocketSet - fun set(s: Socket) do sset.set(s.socket) end - fun is_set(s: Socket): Bool do return sset.is_set(s.socket) end - fun zero do sset.zero end - fun clear(s: Socket) do sset.clear(s.socket) end + private var native = new NativeSocketSet + + init do clear + + # Add `socket` to this set + fun add(socket: Socket) do native.set(socket.socket) + + # Remove `socket` from this set + fun remove(socket: Socket) do native.clear(socket.socket) + + # Does this set has `socket`? + fun has(socket: Socket): Bool do return native.is_set(socket.socket) + + # Clear all sockets from this set + fun clear do native.zero end +# Service class to manage calls to `select` class SocketObserver - private var observer: FFSocketObserver - var readset: nullable SocketSet = null - var writeset: nullable SocketSet = null - var exceptset: nullable SocketSet = null - init(read :Bool, write :Bool, except: Bool) - do - if read then readset = new SocketSet - if write then writeset = new SocketSet - if except then exceptset = new SocketSet - observer = new FFSocketObserver - end - fun select(max: Socket,seconds: Int, microseconds: Int): Bool + private var native = new NativeSocketObserver + + var read_set: nullable SocketSet = null + + var write_set: nullable SocketSet = null + + var except_set: nullable SocketSet = null + + init(read: Bool, write: Bool, except: Bool) + is old_style_init do + if read then read_set = new SocketSet + if write then write_set = new SocketSet + if except then except_set = new SocketSet + end + + fun select(max: Socket, seconds: Int, microseconds: Int): Bool do - var timeval = new FFTimeval(seconds, microseconds) - return observer.select(max.socket, readset.sset, writeset.sset, readset.sset, timeval) > 0 + # FIXME this implementation (see the call to nullable attributes below) and + # `NativeSockectObserver::select` is not stable. + + var timeval = new NativeTimeval(seconds, microseconds) + return native.select(max.socket, read_set.native, write_set.native, except_set.native, timeval) > 0 end end - diff --git a/lib/socket/socket_c.nit b/lib/socket/socket_c.nit index 241422c..44aff9e 100644 --- a/lib/socket/socket_c.nit +++ b/lib/socket/socket_c.nit @@ -28,28 +28,24 @@ in "C Header" `{ #include #include #include +`} - typedef int S_DESCRIPTOR; - typedef struct sockaddr_in S_ADDR_IN; - typedef struct sockaddr S_ADDR; - typedef struct in_addr S_IN_ADDR; - typedef struct hostent S_HOSTENT; - typedef struct timeval S_TIMEVAL; - typedef struct sockaccept_result { S_ADDR_IN addr_in; S_DESCRIPTOR s_desc; } S_ACCEPT_RESULT; - typedef fd_set S_FD_SET; - typedef socklen_t S_LEN; +in "C" `{ + #include + #include `} # Wrapper for the data structure PollFD used for polling on a socket class PollFD + super FinalizableOnce # The PollFD object - private var poll_struct: FFSocketPollFD + private var poll_struct: NativeSocketPollFD # A collection of the events to be watched - var events: Array[FFSocketPollValues] + var events: Array[NativeSocketPollValues] - init(pid: Int, events: Array[FFSocketPollValues]) + init(pid: Int, events: Array[NativeSocketPollValues]) do assert events.length >= 1 self.events = events @@ -60,13 +56,13 @@ class PollFD events_in_one += events[i] end - self.poll_struct = new FFSocketPollFD(pid, events_in_one) + self.poll_struct = new NativeSocketPollFD(pid, events_in_one) end # Reads the response and returns an array with the type of events that have been found - private fun check_response(response: Int): Array[FFSocketPollValues] + private fun check_response(response: Int): Array[NativeSocketPollValues] do - var resp_array = new Array[FFSocketPollValues] + var resp_array = new Array[NativeSocketPollValues] for i in events do if c_check_resp(response, i) != 0 then resp_array.push(i) @@ -76,49 +72,65 @@ class PollFD end # Checks if the poll call has returned true for a particular type of event - private fun c_check_resp(response: Int, mask: FFSocketPollValues): Int + private fun c_check_resp(response: Int, mask: NativeSocketPollValues): Int `{ return response & mask; `} + redef fun finalize_once + do + poll_struct.free + end end # Data structure used by the poll function -private extern class FFSocketPollFD `{ struct pollfd `} - # File descriptor id - private fun fd: Int `{ return recv.fd; `} +private extern class NativeSocketPollFD `{ struct pollfd * `} + + # File descriptor + fun fd: Int `{ return recv->fd; `} + # List of events to be watched - private fun events: Int `{ return recv.events; `} + fun events: Int `{ return recv->events; `} + # List of events received by the last poll function - private fun revents: Int `{ return recv.revents; `} + fun revents: Int `{ return recv->revents; `} - new (pid: Int, events: FFSocketPollValues) `{ - struct pollfd poll; - poll.fd = pid; - poll.events = events; + new (pid: Int, events: NativeSocketPollValues) `{ + struct pollfd *poll = malloc(sizeof(struct pollfd)); + poll->fd = pid; + poll->events = events; return poll; `} - end -extern class FFSocket `{ S_DESCRIPTOR* `} - new socket(domain: FFSocketAddressFamilies, socketType: FFSocketTypes, protocol: FFSocketProtocolFamilies) `{ - S_DESCRIPTOR *d = NULL; d = (S_DESCRIPTOR*) malloc( sizeof(S_DESCRIPTOR) ); +extern class NativeSocket `{ int* `} + + new socket(domain: NativeSocketAddressFamilies, socketType: NativeSocketTypes, protocol: NativeSocketProtocolFamilies) `{ int ds = socket(domain, socketType, protocol); if(ds == -1){ - free(d); return NULL; } + int *d = malloc(sizeof(int)); memcpy(d, &ds, sizeof(ds)); return d; `} + fun destroy `{ free(recv); `} - fun close: Int `{ return close( *recv ); `} + + fun close: Int `{ return close(*recv); `} + fun descriptor: Int `{ return *recv; `} - fun gethostbyname(n: String): FFSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `} - fun connect(addrIn: FFSocketAddrIn): Int `{ return connect( *recv, (S_ADDR*)addrIn, sizeof(*addrIn) ); `} - fun write(buffer: String): Int import String.to_cstring, String.length `{ return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer)); `} + fun gethostbyname(n: String): NativeSocketHostent import String.to_cstring `{ return gethostbyname(String_to_cstring(n)); `} + + fun connect(addrIn: NativeSocketAddrIn): Int `{ + return connect(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); + `} + + fun write(buffer: String): Int + import String.to_cstring, String.length `{ + return write(*recv, (char*)String_to_cstring(buffer), String_length(buffer)); + `} fun read: String import NativeString.to_s_with_length `{ static char c[1024]; @@ -133,31 +145,34 @@ extern class FFSocket `{ S_DESCRIPTOR* `} `} # Sets an option for the socket - fun setsockopt(level: FFSocketOptLevels, option_name: FFSocketOptNames, option_value: Int) `{ + # + # Returns `true` on success. + fun setsockopt(level: NativeSocketOptLevels, option_name: NativeSocketOptNames, option_value: Int): Bool `{ int err = setsockopt(*recv, level, option_name, &option_value, sizeof(int)); if(err != 0){ - perror("Error on setsockopts : "); - exit(1); + return 0; } + return 1; `} - fun bind(addrIn: FFSocketAddrIn): Int `{ return bind(*recv, (S_ADDR*)addrIn, sizeof(*addrIn)); `} + fun bind(addrIn: NativeSocketAddrIn): Int `{ return bind(*recv, (struct sockaddr*)addrIn, sizeof(*addrIn)); `} + fun listen(size: Int): Int `{ return listen(*recv, size); `} # Checks if the buffer is ready for any event specified when creating the pollfd structure - fun socket_poll(filedesc: PollFD, timeout: Int): Array[FFSocketPollValues] + fun socket_poll(filedesc: PollFD, timeout: Int): Array[NativeSocketPollValues] do - var result = i_poll(filedesc.poll_struct, timeout) + var result = native_poll(filedesc.poll_struct, timeout) assert result != -1 return filedesc.check_response(result) end # Call to the poll function of the C socket # - # Signature : + # Signature: # int poll(struct pollfd fds[], nfds_t nfds, int timeout); # - # Official documentation of the poll function : + # Official documentation of the poll function: # # The poll() function provides applications with a mechanism for multiplexing input/output over a set of file descriptors. # For each member of the array pointed to by fds, poll() shall examine the given file descriptor for the event(s) specified in events. @@ -167,147 +182,205 @@ extern class FFSocket `{ S_DESCRIPTOR* `} # It is a pointer to an array with one member for each open file descriptor of interest. # The array's members are pollfd structures within which fd specifies an open file descriptor and events and revents are bitmasks constructed by # OR'ing a combination of the pollfd flags. - private fun i_poll(filedesc: FFSocketPollFD, timeout: Int): Int `{ - int poll_return = poll(&filedesc, 1, timeout); + private fun native_poll(filedesc: NativeSocketPollFD, timeout: Int): Int `{ + int poll_return = poll(filedesc, 1, timeout); return poll_return; `} - private fun i_accept(addrIn: FFSocketAddrIn): FFSocket `{ - S_LEN s = sizeof(S_ADDR); - S_DESCRIPTOR *d = NULL; - d = malloc(sizeof(S_DESCRIPTOR)); - *d = accept(*recv,(S_ADDR*)addrIn, &s); - return d; + private fun native_accept(addr_in: NativeSocketAddrIn): NativeSocket `{ + socklen_t s = sizeof(struct sockaddr); + int socket = accept(*recv, (struct sockaddr*)addr_in, &s); + if (socket == -1) return NULL; + + int *ptr = malloc(sizeof(int)); + *ptr = socket; + return ptr; `} - fun accept: FFSocketAcceptResult + + fun accept: nullable SocketAcceptResult do - var addrIn = new FFSocketAddrIn - var s = i_accept(addrIn) - return new FFSocketAcceptResult(s, addrIn) + var addrIn = new NativeSocketAddrIn + var s = native_accept(addrIn) + if s.address_is_null then return null + return new SocketAcceptResult(s, addrIn) end -end -extern class FFSocketAcceptResult `{ S_ACCEPT_RESULT* `} - new (socket: FFSocket, addrIn: FFSocketAddrIn) `{ - S_ACCEPT_RESULT *sar = NULL; - sar = malloc( sizeof(S_ACCEPT_RESULT) ); - sar->s_desc = *socket; - sar->addr_in = *addrIn; - return sar; + # Set wether this socket is non blocking + fun non_blocking=(value: Bool) `{ + int flags = fcntl(*recv, F_GETFL, 0); + if (flags == -1) flags = 0; + + if (value) { + flags = flags | O_NONBLOCK; + } else if (flags & O_NONBLOCK) { + flags = flags - O_NONBLOCK; + } else { + return; + } + fcntl(*recv, F_SETFL, flags); `} - fun socket: FFSocket `{ return &recv->s_desc; `} - fun addrIn: FFSocketAddrIn `{ return &recv->addr_in; `} - fun destroy `{ free(recv); `} end -extern class FFSocketAddrIn `{ S_ADDR_IN* `} +# Result of a call to `NativeSocket::accept` +class SocketAcceptResult + + # Opened socket + var socket: NativeSocket + + # Address of the remote client + var addr_in: NativeSocketAddrIn +end + +extern class NativeSocketAddrIn `{ struct sockaddr_in* `} new `{ - S_ADDR_IN *sai = NULL; - sai = malloc( sizeof(S_ADDR_IN) ); + struct sockaddr_in *sai = NULL; + sai = malloc(sizeof(struct sockaddr_in)); return sai; `} - new with(port: Int, family: FFSocketAddressFamilies) `{ - S_ADDR_IN *sai = NULL; - sai = malloc( sizeof(S_ADDR_IN) ); + + new with(port: Int, family: NativeSocketAddressFamilies) `{ + struct sockaddr_in *sai = NULL; + sai = malloc(sizeof(struct sockaddr_in)); sai->sin_family = family; sai->sin_port = htons(port); sai->sin_addr.s_addr = INADDR_ANY; return sai; `} - new with_hostent(hostent: FFSocketHostent, port: Int) `{ - S_ADDR_IN *sai = NULL; - sai = malloc( sizeof(S_ADDR_IN) ); + + new with_hostent(hostent: NativeSocketHostent, port: Int) `{ + struct sockaddr_in *sai = NULL; + sai = malloc(sizeof(struct sockaddr_in)); sai->sin_family = hostent->h_addrtype; sai->sin_port = htons(port); - memcpy( (char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length ); + memcpy((char*)&sai->sin_addr.s_addr, (char*)hostent->h_addr, hostent->h_length); return sai; `} - fun address: String import NativeString.to_s `{ return NativeString_to_s( (char*)inet_ntoa(recv->sin_addr) ); `} - fun family: FFSocketAddressFamilies `{ return recv->sin_family; `} + + fun address: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(recv->sin_addr)); `} + + fun family: NativeSocketAddressFamilies `{ return recv->sin_family; `} + fun port: Int `{ return ntohs(recv->sin_port); `} + fun destroy `{ free(recv); `} end -extern class FFSocketHostent `{ S_HOSTENT* `} - private fun i_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(recv->h_aliases[i]); `} - private fun i_h_aliases_reachable(i: Int): Bool `{ return (recv->h_aliases[i] != NULL); `} +extern class NativeSocketHostent `{ struct hostent* `} + private fun native_h_aliases(i: Int): String import NativeString.to_s `{ return NativeString_to_s(recv->h_aliases[i]); `} + + private fun native_h_aliases_reachable(i: Int): Bool `{ return (recv->h_aliases[i] != NULL); `} + fun h_aliases: Array[String] do var i=0 var d=new Array[String] loop - d.add(i_h_aliases(i)) - if i_h_aliases_reachable(i+1) == false then break + d.add(native_h_aliases(i)) + if native_h_aliases_reachable(i+1) == false then break i += 1 end return d end - fun h_addr: String import NativeString.to_s `{ return NativeString_to_s( (char*)inet_ntoa(*(S_IN_ADDR*)recv->h_addr) ); `} + + fun h_addr: String import NativeString.to_s `{ return NativeString_to_s((char*)inet_ntoa(*(struct in_addr*)recv->h_addr)); `} + fun h_addrtype: Int `{ return recv->h_addrtype; `} + fun h_length: Int `{ return recv->h_length; `} + fun h_name: String import NativeString.to_s `{ return NativeString_to_s(recv->h_name); `} end -extern class FFTimeval `{ S_TIMEVAL* `} +extern class NativeTimeval `{ struct timeval* `} new (seconds: Int, microseconds: Int) `{ - S_TIMEVAL* tv = NULL; - tv = malloc( sizeof(S_TIMEVAL) ); + struct timeval* tv = NULL; + tv = malloc(sizeof(struct timeval)); tv->tv_sec = seconds; tv->tv_usec = microseconds; return tv; `} + fun seconds: Int `{ return recv->tv_sec; `} + fun microseconds: Int `{ return recv->tv_usec; `} - fun destroy `{ free( recv ); `} + + fun destroy `{ free(recv); `} end -extern class FFSocketSet `{ S_FD_SET* `} +extern class NativeSocketSet `{ fd_set* `} new `{ - S_FD_SET *f = NULL; - f = malloc( sizeof(S_FD_SET) ); + fd_set *f = NULL; + f = malloc(sizeof(fd_set)); return f; `} - fun set(s: FFSocket) `{ FD_SET( *s, recv ); `} - fun is_set(s: FFSocket): Bool `{ return FD_ISSET( *s, recv ); `} - fun zero `{ FD_ZERO( recv ); `} - fun clear(s: FFSocket) `{ FD_CLR( *s, recv ); `} - fun destroy `{ free( recv ); `} + + fun set(s: NativeSocket) `{ FD_SET(*s, recv); `} + + fun is_set(s: NativeSocket): Bool `{ return FD_ISSET(*s, recv); `} + + fun zero `{ FD_ZERO(recv); `} + + fun clear(s: NativeSocket) `{ FD_CLR(*s, recv); `} + + fun destroy `{ free(recv); `} end -class FFSocketObserver - fun select(max: FFSocket, reads: nullable FFSocketSet, write: nullable FFSocketSet, - except: nullable FFSocketSet, timeout: FFTimeval): Int `{ - S_FD_SET *rds, *wts, *exs = NULL; - S_TIMEVAL *tm = NULL; - if(reads != NULL) rds = (S_FD_SET*)reads; - if(write != NULL) wts = (S_FD_SET*)write; - if(except != NULL) exs = (S_FD_SET*)except; - if(timeout != NULL) tm = (S_TIMEVAL*)timeout; +class NativeSocketObserver + # FIXME this implementation is broken. `reads`, `write` and `except` + # are boxed objects, passing them to a C function is illegal. + fun select(max: NativeSocket, reads: nullable NativeSocketSet, write: nullable NativeSocketSet, + except: nullable NativeSocketSet, timeout: NativeTimeval): Int `{ + fd_set *rds, *wts, *exs = NULL; + struct timeval *tm = NULL; + if (reads != NULL) rds = (fd_set*)reads; + if (write != NULL) wts = (fd_set*)write; + if (except != NULL) exs = (fd_set*)except; + if (timeout != NULL) tm = (struct timeval*)timeout; return select(*max, rds, wts, exs, tm); `} end -extern class FFSocketTypes `{ int `} +extern class NativeSocketTypes `{ int `} new sock_stream `{ return SOCK_STREAM; `} new sock_dgram `{ return SOCK_DGRAM; `} new sock_raw `{ return SOCK_RAW; `} new sock_seqpacket `{ return SOCK_SEQPACKET; `} end -extern class FFSocketAddressFamilies `{ int `} + +extern class NativeSocketAddressFamilies `{ int `} new af_null `{ return 0; `} - new af_unspec `{ return AF_UNSPEC; `} # unspecified - new af_unix `{ return AF_UNIX; `} # local to host (pipes) - new af_local `{ return AF_LOCAL; `} # backward compatibility - new af_inet `{ return AF_INET; `} # internetwork: UDP, TCP, etc. - new af_sna `{ return AF_SNA; `} # IBM SNA - new af_decnet `{ return AF_DECnet; `} # DECnet - new af_route `{ return AF_ROUTE; `} # Internal Routing Protocol - new af_ipx `{ return AF_IPX; `} # Novell Internet Protocol - new af_isdn `{ return AF_ISDN; `} # Integrated Services Digital Network - new af_inet6 `{ return AF_INET6; `} # IPv6 - new af_max `{ return AF_MAX; `} + + # Unspecified + new af_unspec `{ return AF_UNSPEC; `} + + # Local to host (pipes) + new af_unix `{ return AF_UNIX; `} + + # For backward compatibility + new af_local `{ return AF_LOCAL; `} + + # Internetwork: UDP, TCP, etc. + new af_inet `{ return AF_INET; `} + + # IBM SNA + new af_sna `{ return AF_SNA; `} + + # DECnet + new af_decnet `{ return AF_DECnet; `} + + # Internal Routing Protocol + new af_route `{ return AF_ROUTE; `} + + # Novell Internet Protocol + new af_ipx `{ return AF_IPX; `} + + # IPv6 + new af_inet6 `{ return AF_INET6; `} + + new af_max `{ return AF_MAX; `} end -extern class FFSocketProtocolFamilies `{ int `} + +extern class NativeSocketProtocolFamilies `{ int `} new pf_null `{ return 0; `} new pf_unspec `{ return PF_UNSPEC; `} new pf_local `{ return PF_LOCAL; `} @@ -317,48 +390,91 @@ extern class FFSocketProtocolFamilies `{ int `} new pf_decnet `{ return PF_DECnet; `} new pf_route `{ return PF_ROUTE; `} new pf_ipx `{ return PF_IPX; `} - new pf_isdn `{ return PF_ISDN; `} new pf_key `{ return PF_KEY; `} new pf_inet6 `{ return PF_INET6; `} new pf_max `{ return PF_MAX; `} end + # Level on which to set options -extern class FFSocketOptLevels `{ int `} +extern class NativeSocketOptLevels `{ int `} + # Dummy for IP (As defined in C) new ip `{ return IPPROTO_IP;`} + # Control message protocol new icmp `{ return IPPROTO_ICMP;`} + # Use TCP new tcp `{ return IPPROTO_TCP; `} + # Socket level options new socket `{ return SOL_SOCKET; `} end + # Options for socket, use with setsockopt -extern class FFSocketOptNames `{ int `} +extern class NativeSocketOptNames `{ int `} + # Enables debugging information new debug `{ return SO_DEBUG; `} + # Authorizes the broadcasting of messages new broadcast `{ return SO_BROADCAST; `} + # Authorizes the reuse of the local address new reuseaddr `{ return SO_REUSEADDR; `} + # Authorizes the use of keep-alive packets in a connection new keepalive `{ return SO_KEEPALIVE; `} + + # Disable the Nagle algorithm and send data as soon as possible, in smaller packets + new tcp_nodelay `{ return TCP_NODELAY; `} end + # Used for the poll function of a socket, mix several Poll values to check for events on more than one type of event -extern class FFSocketPollValues `{ int `} - new pollin `{ return POLLIN; `} # Data other than high-priority data may be read without blocking. - new pollrdnorm `{ return POLLRDNORM; `} # Normal data may be read without blocking. - new pollrdband `{ return POLLRDBAND; `} # Priority data may be read without blocking. - new pollpri `{ return POLLPRI; `} # High-priority data may be read without blocking. - new pollout `{ return POLLOUT; `} # Normal data may be written without blocking. - new pollwrnorm `{ return POLLWRNORM; `} # Equivalent to POLLOUT - new pollwrband `{ return POLLWRBAND; `} # Priority data may be written. - new pollerr `{ return POLLERR; `} # An error has occurred on the device or stream. This flag is only valid in the revents bitmask; it shall be ignored in the events member. - new pollhup `{ return POLLHUP; `} # The device has been disconnected. This event and POLLOUT are mutually-exclusive; a stream can never be writable if a hangup has occurred. However, this event and POLLIN, POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive. This flag is only valid in the revents bitmask; it shall be ignored in the events member. - new pollnval `{ return POLLNVAL; `} # The specified fd value is invalid. This flag is only valid in the revents member; it shall ignored in the events member. - - # Combines two FFSocketPollValues - private fun +(other: FFSocketPollValues): FFSocketPollValues `{ +extern class NativeSocketPollValues `{ int `} + + # Data other than high-priority data may be read without blocking. + new pollin `{ return POLLIN; `} + + # Normal data may be read without blocking. + new pollrdnorm `{ return POLLRDNORM; `} + + # Priority data may be read without blocking. + new pollrdband `{ return POLLRDBAND; `} + + # High-priority data may be read without blocking. + new pollpri `{ return POLLPRI; `} + + # Normal data may be written without blocking. + new pollout `{ return POLLOUT; `} + + # Equivalent to POLLOUT + new pollwrnorm `{ return POLLWRNORM; `} + + # Priority data may be written. + new pollwrband `{ return POLLWRBAND; `} + + # An error has occurred on the device or stream. + # + # This flag is only valid in the revents bitmask; it shall be ignored in the events member. + new pollerr `{ return POLLERR; `} + + # The device has been disconnected. + # + # This event and POLLOUT are mutually-exclusive; a stream can never be + # writable if a hangup has occurred. However, this event and POLLIN, + # POLLRDNORM, POLLRDBAND, or POLLPRI are not mutually-exclusive. + # + # This flag is only valid in the revents bitmask; it shall be ignored in the events member. + new pollhup `{ return POLLHUP; `} + + # The specified fd value is invalid. + # + # This flag is only valid in the revents member; it shall ignored in the events member. + new pollnval `{ return POLLNVAL; `} + + # Combines two NativeSocketPollValues + private fun +(other: NativeSocketPollValues): NativeSocketPollValues `{ return recv | other; `} end diff --git a/lib/standard/collection/abstract_collection.nit b/lib/standard/collection/abstract_collection.nit index d5621a9..bb17064 100644 --- a/lib/standard/collection/abstract_collection.nit +++ b/lib/standard/collection/abstract_collection.nit @@ -377,7 +377,7 @@ interface Set[E: Object] end # MapRead are abstract associative collections: `key` -> `item`. -interface MapRead[K: Object, V] +interface MapRead[K, V] # Get the item at `key` # # var x = new HashMap[String, Int] @@ -492,7 +492,7 @@ end # assert map.values.has(1) == true # assert map.values.has(3) == false # -interface Map[K: Object, V] +interface Map[K, V] super MapRead[K, V] # Set the `value` at `key`. @@ -552,7 +552,7 @@ interface Map[K: Object, V] end # Iterators for Map. -interface MapIterator[K: Object, V] +interface MapIterator[K, V] # The current item. # Require `is_ok`. fun item: V is abstract @@ -583,7 +583,7 @@ interface MapIterator[K: Object, V] end # Iterator on a 'keys' point of view of a map -class MapKeysIterator[K: Object, V] +class MapKeysIterator[K, V] super Iterator[K] # The original iterator var original_iterator: MapIterator[K, V] @@ -594,7 +594,7 @@ class MapKeysIterator[K: Object, V] end # Iterator on a 'values' point of view of a map -class MapValuesIterator[K: Object, V] +class MapValuesIterator[K, V] super Iterator[V] # The original iterator var original_iterator: MapIterator[K, V] @@ -941,7 +941,7 @@ end # Associative arrays that internally uses couples to represent each (key, value) pairs. # This is an helper class that some specific implementation of Map may implements. -interface CoupleMap[K: Object, V] +interface CoupleMap[K, V] super Map[K, V] # Return the couple of the corresponding key @@ -968,7 +968,7 @@ end # Iterator on CoupleMap # # Actually it is a wrapper around an iterator of the internal array of the map. -private class CoupleMapIterator[K: Object, V] +private class CoupleMapIterator[K, V] super MapIterator[K, V] redef fun item do return _iter.item.second diff --git a/lib/standard/collection/array.nit b/lib/standard/collection/array.nit index 4b73471..44ea5d3 100644 --- a/lib/standard/collection/array.nit +++ b/lib/standard/collection/array.nit @@ -281,6 +281,32 @@ class Array[E] _items[l] = item end + # Slight optimization for arrays + redef fun add_all(items) + do + var l = _length + var nl = l + items.length + if _capacity < nl then + enlarge nl + end + + if items isa Array[E] then + var k = 0 + while l < nl do + _items[l] = items._items[k] + l += 1 + k += 1 + end + else + for item in items do + _items[l] = item + l += 1 + end + end + + _length = nl + end + redef fun enlarge(cap) do var c = _capacity @@ -348,10 +374,6 @@ class Array[E] # The internal storage. private var items: nullable NativeArray[E] = null - # Do not use this method - # FIXME: Remove it once modules can intrude non local modules - fun intern_items: NativeArray[E] do return _items.as(not null) - # The size of `_items`. private var capacity: Int = 0 @@ -514,7 +536,7 @@ end # Associative arrays implemented with an array of (key, value) pairs. -class ArrayMap[K: Object, E] +class ArrayMap[K, E] super CoupleMap[K, E] # O(n) @@ -596,7 +618,7 @@ class ArrayMap[K: Object, E] end end -private class ArrayMapKeys[K: Object, E] +private class ArrayMapKeys[K, E] super RemovableCollection[K] # The original map var map: ArrayMap[K, E] @@ -616,7 +638,7 @@ private class ArrayMapKeys[K: Object, E] redef fun remove_all(key) do self.remove(key) end -private class ArrayMapValues[K: Object, E] +private class ArrayMapValues[K, E] super RemovableCollection[E] # The original map var map: ArrayMap[K, E] diff --git a/lib/standard/collection/hash_collection.nit b/lib/standard/collection/hash_collection.nit index d389c5a..f3857293 100644 --- a/lib/standard/collection/hash_collection.nit +++ b/lib/standard/collection/hash_collection.nit @@ -10,13 +10,18 @@ # You are allowed to redistribute it and sell it, alone or is a part of # another product. -# Introduce Hashmap and Hashset. +# Introduce `HashMap` and `HashSet`. module hash_collection import array +redef class Map[K, V] + # Get a `HashMap[K, V]` as default implementation + new do return new HashMap[K, V] +end + # A HashCollection is an array of HashNode[K] indexed by the K hash value -private abstract class HashCollection[K: Object] +private abstract class HashCollection[K] type N: HashNode[K] var array: nullable NativeArray[nullable N] = null # Used to store items @@ -35,12 +40,14 @@ private abstract class HashCollection[K: Object] # Return the index of the key k fun index_at(k: K): Int do + if k == null then return 0 + var i = k.hash % _capacity if i < 0 then i = - i return i end - # Return the node assosiated with the key + # Return the node associated with the key fun node_at(k: K): nullable N do # cache: `is` is used instead of `==` because it is a faster filter (even if not exact) @@ -52,7 +59,7 @@ private abstract class HashCollection[K: Object] return res end - # Return the node assosiated with the key (but with the index already known) + # Return the node associated with the key (but with the index already known) fun node_at_idx(i: Int, k: K): nullable N do var c = _array[i] @@ -190,7 +197,7 @@ private abstract class HashCollection[K: Object] end end -private abstract class HashNode[K: Object] +private abstract class HashNode[K] var key: K type N: HashNode[K] var next_item: nullable N = null @@ -199,9 +206,20 @@ private abstract class HashNode[K: Object] var next_in_bucklet: nullable N = null end -# A map implemented with a hash table. -# Keys of such a map cannot be null and require a working `hash` method -class HashMap[K: Object, V] +# A `Map` implemented with a hash table. +# +# ~~~ +# var map = new HashMap[nullable String, Int] +# map[null] = 0 +# map["one"] = 1 +# map["two"] = 2 +# +# assert map[null] == 0 +# assert map["one"] == 1 +# assert map.keys.has("two") +# assert map.values.length == 3 +# ~~~ +class HashMap[K, V] super Map[K, V] super HashCollection[K] @@ -249,7 +267,7 @@ class HashMap[K: Object, V] end # View of the keys of a HashMap -private class HashMapKeys[K: Object, V] +private class HashMapKeys[K, V] super RemovableCollection[K] # The original map var map: HashMap[K, V] @@ -270,7 +288,7 @@ private class HashMapKeys[K: Object, V] end # View of the values of a Map -private class HashMapValues[K: Object, V] +private class HashMapValues[K, V] super RemovableCollection[V] # The original map var map: HashMap[K, V] @@ -340,14 +358,14 @@ private class HashMapValues[K: Object, V] end end -private class HashMapNode[K: Object, V] +private class HashMapNode[K, V] super HashNode[K] redef type N: HashMapNode[K, V] var value: V end # A `MapIterator` over a `HashMap`. -class HashMapIterator[K: Object, V] +class HashMapIterator[K, V] super MapIterator[K, V] redef fun is_ok do return _node != null diff --git a/lib/standard/exec_nit.c b/lib/standard/exec_nit.c index c69ba64..317e909 100644 --- a/lib/standard/exec_nit.c +++ b/lib/standard/exec_nit.c @@ -82,7 +82,7 @@ se_exec_data_t* exec_Process_Process_basic_exec_execute_4(void *s, char *prog, c /* calls */ execvp(prog, arg); - abort(); + _exit(127); } else if (id > 0) { /* father */ diff --git a/lib/standard/gc.nit b/lib/standard/gc.nit index acee1f6..23fa47f 100644 --- a/lib/standard/gc.nit +++ b/lib/standard/gc.nit @@ -36,3 +36,29 @@ class Finalizable # to use attributes of this instances. fun finalize do end end + +# An object to be finalized only once +# +# This is an utility sub-class to `Finalizable` which ensures that `finalized_once` +# is called only once per instance. User classes implementing `FinalizableOnce` +# shoud specialize `finalize_once` and _not_ `finalize`. When manipulating the user +# class, only `finalize` should be called as it protects `finalize_once`. +class FinalizableOnce + super Finalizable + + # Has `self` been finalized? (either by the GC or an explicit call to `finalize`) + var finalized = false + + redef fun finalize + do + if finalized then return + + finalize_once + finalized = true + end + + # Real finalization method of `FinalizableOnce`, will be called only once + # + # See: `Finalizable::finalize` for restrictions on finalizer methods. + protected fun finalize_once do end +end diff --git a/lib/standard/stream.nit b/lib/standard/stream.nit index 46ad73c..ddc01fe 100644 --- a/lib/standard/stream.nit +++ b/lib/standard/stream.nit @@ -493,5 +493,12 @@ class StringIStream source = "" end + redef fun read_all do + var c = cursor + cursor = source.length + if c == 0 then return source + return source.substring_from(c) + end + redef fun eof do return cursor >= source.length end diff --git a/lib/standard/string.nit b/lib/standard/string.nit index c4ae7b0..2c46179 100644 --- a/lib/standard/string.nit +++ b/lib/standard/string.nit @@ -1092,15 +1092,19 @@ class FlatString from = 0 end - var realFrom = index_from + from + var new_from = index_from + from - if (realFrom + count) > index_to then return new FlatString.with_infos(items, index_to - realFrom + 1, realFrom, index_to) + if (new_from + count) > index_to then + var new_len = index_to - new_from + 1 + if new_len <= 0 then return empty + return new FlatString.with_infos(items, new_len, new_from, index_to) + end - if count == 0 then return empty + if count <= 0 then return empty - var to = realFrom + count - 1 + var to = new_from + count - 1 - return new FlatString.with_infos(items, to - realFrom + 1, realFrom, to) + return new FlatString.with_infos(items, to - new_from + 1, new_from, to) end redef fun empty do return "".as(FlatString) @@ -2174,7 +2178,7 @@ redef class Map[K,V] var i = iterator var k = i.key var e = i.item - s.append("{k}{couple_sep}{e or else ""}") + s.append("{k or else ""}{couple_sep}{e or else ""}") # Concat other items i.next @@ -2182,7 +2186,7 @@ redef class Map[K,V] s.append(sep) k = i.key e = i.item - s.append("{k}{couple_sep}{e or else ""}") + s.append("{k or else ""}{couple_sep}{e or else ""}") i.next end return s.to_s diff --git a/lib/string_experimentations/utf8.nit b/lib/string_experimentations/utf8.nit index 83641d1..f5b8786 100644 --- a/lib/string_experimentations/utf8.nit +++ b/lib/string_experimentations/utf8.nit @@ -34,7 +34,7 @@ typedef struct { # UTF-8 char as defined in RFC-3629, e.g. 1-4 Bytes # # A UTF-8 char has its bytes stored in a NativeString (char*) -extern class UnicodeChar `{ UTF8Char* `} +extern class UTF8Char `{ UTF8Char* `} new(pos: Int, ns: NativeString) `{ UTF8Char* u = malloc(sizeof(UTF8Char)); @@ -83,8 +83,8 @@ extern class UnicodeChar `{ UTF8Char* `} # Returns the Unicode code point representing the character # # Note : A unicode character might not be a visible glyph, but it will be used to determine canonical equivalence - fun code_point: Int import UnicodeChar.len `{ - switch(UnicodeChar_len(recv)){ + fun code_point: Int import UTF8Char.len `{ + switch(UTF8Char_len(recv)){ case 1: return (long)(0x7F & (unsigned char)recv->ns[recv->pos]); case 2: @@ -105,8 +105,8 @@ extern class UnicodeChar `{ UTF8Char* `} # # NOTE : Works only on ASCII chars # TODO : Support unicode for to_upper - fun to_upper: UnicodeChar import UnicodeChar.code_point `{ - int cp = UnicodeChar_code_point(recv); + fun to_upper: UTF8Char import UTF8Char.code_point `{ + int cp = UTF8Char_code_point(recv); if(cp < 97 || cp > 122){ return recv; } char* ns = malloc(2); ns[1] = '\0'; @@ -122,8 +122,8 @@ extern class UnicodeChar `{ UTF8Char* `} # # NOTE : Works only on ASCII chars # TODO : Support unicode for to_upper - fun to_lower: UnicodeChar import UnicodeChar.code_point `{ - int cp = UnicodeChar_code_point(recv); + fun to_lower: UTF8Char import UTF8Char.code_point `{ + int cp = UTF8Char_code_point(recv); if(cp < 65 || cp > 90){ return recv; } char* ns = malloc(2); ns[1] = '\0'; @@ -140,15 +140,15 @@ extern class UnicodeChar `{ UTF8Char* `} if o isa Char then if len != 1 then return false if code_point == o.ascii then return true - else if o isa UnicodeChar then + else if o isa UTF8Char then if len != o.len then return false if code_point == o.code_point then return true end return false end - redef fun output import UnicodeChar.code_point `{ - switch(UnicodeChar_len(recv)){ + redef fun output import UTF8Char.code_point `{ + switch(UTF8Char_len(recv)){ case 1: printf("%c", recv->ns[recv->pos]); break; @@ -165,7 +165,7 @@ extern class UnicodeChar `{ UTF8Char* `} `} redef fun to_s import NativeString.to_s_with_length `{ - int len = utf8___UnicodeChar_len___impl(recv); + int len = utf8___UTF8Char_len___impl(recv); char* r = malloc(len + 1); r[len] = '\0'; char* src = (recv->ns + recv->pos); @@ -182,10 +182,10 @@ private extern class StringIndex `{ UTF8Char* `} new(size: Int) `{ return malloc(size*sizeof(UTF8Char)); `} # Sets the character at `index` as `item` - fun []=(index: Int, item: UnicodeChar) `{ recv[index] = *item; `} + fun []=(index: Int, item: UTF8Char) `{ recv[index] = *item; `} # Gets the character at position `id` - fun [](id: Int): UnicodeChar `{ return &recv[id]; `} + fun [](id: Int): UTF8Char `{ return &recv[id]; `} # Copies a part of self starting at index `my_from` of length `length` into `other`, starting at `its_from` fun copy_to(other: StringIndex, my_from: Int, its_from: Int, length: Int)`{ @@ -259,7 +259,7 @@ redef class FlatString var uchar = index[i] var uchar_len = uchar.len ipos -= uchar_len - new_index[pos_index] = new UnicodeChar(ipos, native) + new_index[pos_index] = new UTF8Char(ipos, native) pos_index -= 1 items.copy_to(native, uchar_len, pos, ipos) pos += uchar_len @@ -373,7 +373,7 @@ redef class NativeString # Creates the index for said NativeString # `length` is the size of the CString (in bytes, up to the first \0) # real_len is just a way to store the length (UTF-8 characters) - private fun make_index(length: Int, real_len: Container[Int]): StringIndex import Container[Int].item=, UnicodeChar.len `{ + private fun make_index(length: Int, real_len: Container[Int]): StringIndex import Container[Int].item=, UTF8Char.len `{ int pos = 0; int index_pos = 0; UTF8Char* index = malloc(length*sizeof(UTF8Char)); @@ -381,7 +381,7 @@ redef class NativeString UTF8Char* curr = &index[index_pos]; curr->pos = pos; curr->ns = recv; - pos += UnicodeChar_len(curr); + pos += UTF8Char_len(curr); index_pos ++; } Container_of_Int_item__assign(real_len, index_pos); diff --git a/lib/symbol.nit b/lib/symbol.nit index 1e9fb67..b865017 100644 --- a/lib/symbol.nit +++ b/lib/symbol.nit @@ -32,8 +32,5 @@ end # A symbol is a unique immutable string class Symbol private var string: String - redef fun to_s do return _string.to_s - - # Only used by String::to_symbol - private init(s: String) do _string = s + redef fun to_s do return string.to_s end diff --git a/lib/websocket/examples/websocket_server.nit b/lib/websocket/examples/websocket_server.nit index aa1124a..8b4584c 100644 --- a/lib/websocket/examples/websocket_server.nit +++ b/lib/websocket/examples/websocket_server.nit @@ -19,27 +19,29 @@ module websocket_server import websocket -var sock = new WebSocket(8088, 1) +var sock = new WebSocketListener(8088, 1) var msg: String -if sock.listener.eof then +if sock.listener.closed then print sys.errno.strerror end -sock.accept +var cli: TCPStream -while not sock.listener.eof do - if not sock.connected then sock.accept - if sys.stdin.poll_in then - msg = gets - printn "Received message : {msg}" - if msg == "exit" then sock.close - if msg == "disconnect" then sock.disconnect_client - sock.write(msg) - end - if sock.can_read(10) then - msg = sock.read_line - if msg != "" then print msg +while not sock.closed do + cli = sock.accept + while cli.connected do + if sys.stdin.poll_in then + msg = gets + printn "Received message : {msg}" + if msg == "disconnect" then cli.close + cli.write(msg) + end + if cli.can_read(10) then + msg = "" + while cli.can_read(0) do msg += cli.read(100) + if msg != "" then print msg + end end end diff --git a/lib/websocket/websocket.nit b/lib/websocket/websocket.nit index 9066391..7e4d329 100644 --- a/lib/websocket/websocket.nit +++ b/lib/websocket/websocket.nit @@ -24,51 +24,62 @@ import base64 intrude import standard::stream -# Websocket compatible server, works as an extra layer to the original Sockets -class WebSocket - super BufferedIStream - super OStream - super PollableIStream - - # Client connection to the server - var client: Socket +# Websocket compatible listener +# +# Produces Websocket client-server connections +class WebSocketListener + super Socket # Socket listening to connections on a defined port - var listener: Socket + var listener: TCPServer # Creates a new Websocket server listening on given port with `max_clients` slots available init(port: Int, max_clients: Int) do - _buffer = new FlatBuffer - _buffer_pos = 0 - listener = new Socket.server(port, max_clients) + listener = new TCPServer(port) + listener.listen max_clients end - # Accept an incoming connection and initializes the handshake - fun accept + # Accepts an incoming connection + fun accept: WebsocketConnection do - assert not listener.eof + assert not listener.closed + + var client = listener.accept + assert client != null - client = listener.accept + return new WebsocketConnection(listener.port, "", client) + end + + # Stop listening for incoming connections + fun close + do + listener.close + end +end +# Connection to a websocket client +# +# Can be used to communicate with a client +class WebsocketConnection + super TCPStream + + init do + _buffer = new FlatBuffer + _buffer_pos = 0 var headers = parse_handshake var resp = handshake_response(headers) client.write(resp) end - # Disconnect from a client - fun disconnect_client - do - client.close - end + # Client connection to the server + var client: TCPStream - # Disconnects the client if one is connected - # And stops the server + # Disconnect from a client redef fun close do client.close - listener.close end # Parses the input handshake sent by the client @@ -129,9 +140,9 @@ class WebSocket # Reads an HTTP frame protected fun read_http_frame(buf: Buffer): String do - client.append_line_to(buf) - buf.chars.add('\n') - if buf.has_substring("\r\n\r\n", buf.length - 4) then return buf.to_s + buf.append client.read_line + buf.append("\r\n") + if buf.has_suffix("\r\n\r\n") then return buf.to_s return read_http_frame(buf) end @@ -219,9 +230,9 @@ class WebSocket end # Checks if a connection to a client is available - fun connected: Bool do return client.connected + redef fun connected do return client.connected - redef fun write(msg: Text) + redef fun write(msg) do client.write(frame_message(msg.to_s)) end @@ -235,7 +246,7 @@ class WebSocket unpad_message end - redef fun end_reached do return _buffer_pos >= _buffer.length and client.eof + redef fun end_reached do return client._buffer_pos >= client._buffer.length and client.end_reached # Is there some data available to be read ? fun can_read(timeout: Int): Bool do return client.ready_to_read(timeout) diff --git a/share/man/nit.md b/share/man/nit.md index f1887db..697f2bb 100644 --- a/share/man/nit.md +++ b/share/man/nit.md @@ -102,6 +102,11 @@ Whatever follows it is used as arguments of the interpreted program. ## OTHER OPTIONS +`--vm` +: Run the virtual machine instead of the naive interpreter (experimental) + +The virtual machine is currently under heavy development and, unless you are developing the vm, there is no reason to use this option yet. + `-o` : Does nothing. Used for compatibility. diff --git a/share/man/nitc.md b/share/man/nitc.md index 9c1d562..f98d50d 100644 --- a/share/man/nitc.md +++ b/share/man/nitc.md @@ -329,6 +329,18 @@ Usually you do not need them since they make the generated code slower. `--colo-dead-methods` : Force colorization of dead methods. +`--colors-are-symbols` +: Store colors as symbols instead of static data. + + By default, the various identifiers used to implement OO-mechanisms are stored as genuine constant static variables. + + This option uses linker symbols to encode these identifiers. + This makes the compiled program faster since less indirections are required to get the values. + It also produces executables that are a little bit smaller since static memory does not have to store the colors. + + Warning: the usage of linker symbols is not portable on all toolchains (eg. Mac OS X). + Also, this option does nothing on some platforms (like android). + `--no-gcc-directive` : Disable advanced gcc directives for optimization. diff --git a/src/annotation.nit b/src/annotation.nit index af63ad3..882b645 100644 --- a/src/annotation.nit +++ b/src/annotation.nit @@ -16,7 +16,7 @@ module annotation import modelbuilder -private import literal +import literal import model::mmodule_data redef class Prod @@ -34,29 +34,9 @@ redef class Prod end return res.first end - - # Return all its annotations of a given name in the order of their declaration - # Retun an empty array if no such an annotation. - fun get_annotations(name: String): Array[AAnnotation] - do - var res = new Array[AAnnotation] - var nas = n_annotations - if nas == null then return res - for na in nas.n_items do - if na.name != name then continue - res.add(na) - end - return res - end end redef class AAnnotation - # The name of the annotation - fun name: String - do - return n_atid.n_id.text - end - # Get the single argument of `self` as a `String`. # Raise error and return null on any inconsistency. fun arg_as_string(modelbuilder: ModelBuilder): nullable String @@ -100,37 +80,6 @@ redef class AAnnotation end end -redef class AExpr - # Get `self` as a `String`. - # Return null if not a string. - fun as_string: nullable String - do - if not self isa AStringFormExpr then return null - return self.value.as(not null) - end - - # Get `self` as an `Int`. - # Return null if not an integer. - fun as_int: nullable Int - do - if not self isa AIntExpr then return null - return self.value.as(not null) - end - - # Get `self` as a single identifier. - # Return null if not a single identifier. - fun as_id: nullable String - do - if self isa AMethidExpr then - return self.collect_text - end - if not self isa ACallExpr then return null - if not self.n_expr isa AImplicitSelfExpr then return null - if not self.n_args.n_exprs.is_empty then return null - return self.n_id.text - end -end - redef class ModelBuilder # Collect all annotations by `name` assocated to `mmodule` and its imported modules. # Note that visibility is not considered. @@ -138,8 +87,8 @@ redef class ModelBuilder do var annotations = new Array[AAnnotation] for mmod in mmodule.in_importation.greaters do - if not mmodule2nmodule.keys.has(mmod) then continue - var amod = mmodule2nmodule[mmod] + var amod = mmodule2node(mmod) + if amod == null then continue var module_decl = amod.n_moduledecl if module_decl == null then continue var aas = module_decl.get_annotations(name) @@ -152,8 +101,8 @@ redef class ModelBuilder # Obviously, if there is no ast associated to `mmodule`, then nothing is returned. fun get_mmodule_annotation(name: String, mmodule: MModule): nullable AAnnotation do - if not mmodule2nmodule.keys.has(mmodule) then return null - var amod = mmodule2nmodule[mmodule] + var amod = mmodule2node(mmodule) + if amod == null then return null var module_decl = amod.n_moduledecl if module_decl == null then return null var res = module_decl.get_single_annotation(name, self) diff --git a/src/compiler/abstract_compiler.nit b/src/compiler/abstract_compiler.nit index eea089a..dfe463c 100644 --- a/src/compiler/abstract_compiler.nit +++ b/src/compiler/abstract_compiler.nit @@ -120,20 +120,16 @@ redef class ModelBuilder # Simple indirection to `Toolchain::write_and_make` protected fun write_and_make(compiler: AbstractCompiler) do - var platform = compiler.mainmodule.target_platform - var toolchain - if platform == null then - toolchain = new MakefileToolchain(toolcontext) - else - toolchain = platform.toolchain(toolcontext) - end + var platform = compiler.target_platform + var toolchain = platform.toolchain(toolcontext) compile_dir = toolchain.compile_dir toolchain.write_and_make compiler end end redef class Platform - fun toolchain(toolcontext: ToolContext): Toolchain is abstract + # The specific tool-chain associated to the platform + fun toolchain(toolcontext: ToolContext): Toolchain do return new MakefileToolchain(toolcontext) end class Toolchain @@ -189,10 +185,10 @@ class MakefileToolchain fun write_files(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String]) do - var platform = compiler.mainmodule.target_platform - if self.toolcontext.opt_stacktrace.value == "nitstack" and (platform == null or platform.supports_libunwind) then compiler.build_c_to_nit_bindings + var platform = compiler.target_platform + if self.toolcontext.opt_stacktrace.value == "nitstack" and platform.supports_libunwind then compiler.build_c_to_nit_bindings var cc_opt_with_libgc = "-DWITH_LIBGC" - if platform != null and not platform.supports_libgc then cc_opt_with_libgc = "" + if not platform.supports_libgc then cc_opt_with_libgc = "" # Add gc_choser.h to aditionnal bodies var gc_chooser = new ExternCFile("gc_chooser.c", cc_opt_with_libgc) @@ -311,7 +307,7 @@ class MakefileToolchain fun write_makefile(compiler: AbstractCompiler, compile_dir: String, cfiles: Array[String]) do var mainmodule = compiler.mainmodule - var platform = compiler.mainmodule.target_platform + var platform = compiler.target_platform var outname = outfile(mainmodule) @@ -336,7 +332,7 @@ class MakefileToolchain makefile.write("CC = ccache cc\nCXX = ccache c++\nCFLAGS = -g -O2 -Wno-unused-value -Wno-switch -Wno-attributes\nCINCL =\nLDFLAGS ?= \nLDLIBS ?= -lm {linker_options.join(" ")}\n\n") var ost = toolcontext.opt_stacktrace.value - if (ost == "libunwind" or ost == "nitstack") and (platform == null or platform.supports_libunwind) then makefile.write("NEED_LIBUNWIND := YesPlease\n") + if (ost == "libunwind" or ost == "nitstack") and platform.supports_libunwind then makefile.write("NEED_LIBUNWIND := YesPlease\n") # Dynamic adaptations # While `platform` enable complex toolchains, they are statically applied @@ -371,6 +367,18 @@ class MakefileToolchain dep_rules.add(o) end + # Generate linker script, if any + if not compiler.linker_script.is_empty then + var linker_script_path = "{compile_dir}/linker_script" + ofiles.add "linker_script" + var f = new OFStream.open(linker_script_path) + for l in compiler.linker_script do + f.write l + f.write "\n" + end + f.close + end + var java_files = new Array[ExternFile] var pkgconfigs = new Array[String] @@ -476,11 +484,19 @@ abstract class AbstractCompiler # Is hardening asked? (see --hardening) fun hardening: Bool do return self.modelbuilder.toolcontext.opt_hardening.value + # The targeted specific platform + var target_platform: Platform is noinit + init do self.realmainmodule = mainmodule + target_platform = mainmodule.target_platform or else new Platform end + # Do the full code generation of the program `mainmodule` + # It is the main method usually called after the instantiation + fun do_compilation is abstract + # Force the creation of a new file # The point is to avoid contamination between must-be-compiled-separately files fun new_file(name: String): CodeFile @@ -507,6 +523,10 @@ abstract class AbstractCompiler # Where global declaration are stored (the main .h) var header: CodeWriter is writable, noinit + # Additionnal linker script for `ld`. + # Mainly used to do specific link-time symbol resolution + var linker_script = new Array[String] + # Provide a declaration that can be requested (before or latter) by a visitor fun provide_declaration(key: String, s: String) do @@ -677,11 +697,11 @@ extern void nitni_global_ref_decr( struct nitni_ref *ref ); var v = self.new_visitor v.add_decl("#include ") var ost = modelbuilder.toolcontext.opt_stacktrace.value - var platform = mainmodule.target_platform + var platform = target_platform - if platform != null and not platform.supports_libunwind then ost = "none" + if not platform.supports_libunwind then ost = "none" - var no_main = (platform != null and platform.no_main) or modelbuilder.toolcontext.opt_no_main.value + var no_main = platform.no_main or modelbuilder.toolcontext.opt_no_main.value if ost == "nitstack" or ost == "libunwind" then v.add_decl("#define UNW_LOCAL_ONLY") @@ -1036,8 +1056,8 @@ abstract class AbstractCompilerVisitor # The current visited AST node var current_node: nullable ANode = null is writable - # The current `Frame` - var frame: nullable Frame = null is writable + # The current `StaticFrame` + var frame: nullable StaticFrame = null is writable # Alias for self.compiler.mainmodule.object_type fun object_type: MClassType do return self.compiler.mainmodule.object_type @@ -1458,10 +1478,11 @@ abstract class AbstractCompilerVisitor self.require_declaration(s) end - # look for a needed .h and .c file for a given .nit source-file - # FIXME: bad API, parameter should be a `MModule`, not its source-file - fun add_extern(file: String) + # Look for a needed .h and .c file for a given module + # This is used for the legacy FFI + fun add_extern(mmodule: MModule) do + var file = mmodule.location.file.filename file = file.strip_extension(".nit") var tryfile = file + ".nit.h" if tryfile.file_exists then @@ -1665,8 +1686,8 @@ class RuntimeVariable end end -# A frame correspond to a visited property in a `GlobalCompilerVisitor` -class Frame +# The static context of a visited property in a `AbstractCompilerVisitor` +class StaticFrame type VISITOR: AbstractCompilerVisitor @@ -2151,16 +2172,13 @@ redef class AMethPropdef do var externname var at = self.get_single_annotation("extern", v.compiler.modelbuilder) - if at != null then + if at != null and at.n_args.length == 1 then externname = at.arg_as_string(v.compiler.modelbuilder) if externname == null then return false else return false end - if location.file != null then - var file = location.file.filename - v.add_extern(file) - end + v.add_extern(mpropdef.mclassdef.mmodule) var res: nullable RuntimeVariable = null var ret = mpropdef.msignature.return_mtype if ret != null then @@ -2192,10 +2210,7 @@ redef class AMethPropdef else return false end - if location.file != null then - var file = location.file.filename - v.add_extern(file) - end + v.add_extern(mpropdef.mclassdef.mmodule) v.adapt_signature(mpropdef, arguments) v.unbox_signature_extern(mpropdef, arguments) var ret = arguments.first.mtype @@ -2271,7 +2286,7 @@ redef class AAttrPropdef var oldnode = v.current_node v.current_node = self var old_frame = v.frame - var frame = new Frame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv]) + var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mcasttype.as_notnullable.as(MClassType), [recv]) v.frame = frame var value @@ -2310,7 +2325,7 @@ redef class AAttrPropdef var oldnode = v.current_node v.current_node = self var old_frame = v.frame - var frame = new Frame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv]) + var frame = new StaticFrame(v, self.mpropdef.as(not null), recv.mtype.as(MClassType), [recv]) v.frame = frame # Force read to check the initialization v.read_attribute(self.mpropdef.mproperty, recv) diff --git a/src/compiler/compiler.nit b/src/compiler/compiler.nit index 902a656..ea60c0a 100644 --- a/src/compiler/compiler.nit +++ b/src/compiler/compiler.nit @@ -19,6 +19,6 @@ import separate_erasure_compiler import global_compiler import compiler_ffi -import android_platform -import pnacl_platform -import emscripten_platform +import platform::android +import platform::pnacl +import platform::emscripten diff --git a/src/compiler/compiler_ffi.nit b/src/compiler/compiler_ffi.nit index b6cb920..8466af1 100644 --- a/src/compiler/compiler_ffi.nit +++ b/src/compiler/compiler_ffi.nit @@ -27,8 +27,7 @@ redef class MModule private fun nmodule(v: AbstractCompilerVisitor): nullable AModule do - var m2n = v.compiler.modelbuilder.mmodule2nmodule - return m2n.get_or_null(self) + return v.compiler.modelbuilder.mmodule2node(self) end redef fun finalize_ffi(compiler: AbstractCompiler) @@ -44,7 +43,7 @@ redef class MModule ensure_compile_nitni_base(v) - nitni_ccu.header_c_types.add("#include \"{name}._ffi.h\"\n") + nitni_ccu.header_c_types.add("#include \"{c_name}._ffi.h\"\n") nitni_ccu.header_c_types.add """ extern void nitni_global_ref_incr(void*); extern void nitni_global_ref_decr(void*); @@ -100,7 +99,7 @@ redef class AMethPropdef do var mmodule = mpropdef.mclassdef.mmodule var mainmodule = v.compiler.mainmodule - var amodule = v.compiler.modelbuilder.mmodule2nmodule[mmodule] + var amodule = v.compiler.modelbuilder.mmodule2node(mmodule) var mclass_type = mpropdef.mclassdef.bound_mtype # Declare as extern @@ -256,11 +255,11 @@ end redef class CCompilationUnit fun write_as_nitni(mmodule: MModule, compdir: String) do - var base_name = "{mmodule.name}._nitni" + var base_name = "{mmodule.c_name}._nitni" var h_file = "{base_name}.h" write_header_to_file( mmodule, "{compdir}/{h_file}", new Array[String], - "{mmodule.cname.to_s.to_upper}_NITG_NITNI_H") + "{mmodule.c_name.to_s.to_upper}_NITG_NITNI_H") var c_file = "{base_name}.c" write_body_to_file( mmodule, "{compdir}/{c_file}", ["\"{h_file}\""] ) diff --git a/src/compiler/global_compiler.nit b/src/compiler/global_compiler.nit index 94dda37..7a0f162 100644 --- a/src/compiler/global_compiler.nit +++ b/src/compiler/global_compiler.nit @@ -59,6 +59,43 @@ redef class ModelBuilder self.toolcontext.info("*** GENERATING C ***", 1) var compiler = new GlobalCompiler(mainmodule, self, runtime_type_analysis) + compiler.do_compilation + compiler.display_stats + + var time1 = get_time + self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) + write_and_make(compiler) + end +end + +# Compiler that use global compilation and perform hard optimisations like: +# * customization +# * switch dispatch +# * inlining +class GlobalCompiler + super AbstractCompiler + + redef type VISITOR: GlobalCompilerVisitor + + # The result of the RTA (used to know live types and methods) + var runtime_type_analysis: RapidTypeAnalysis + + init + do + 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 + if t.ctype != "val*" or t.mclass.name == "Pointer" then + self.live_primitive_types.add(t) + end + end + end + + redef fun do_compilation + do + var compiler = self + compiler.compile_header if mainmodule.model.get_mclasses_by_name("Pointer") != null then @@ -89,41 +126,11 @@ redef class ModelBuilder # Compile until all runtime_functions are visited while not compiler.todos.is_empty do var m = compiler.todos.shift - self.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3) + modelbuilder.toolcontext.info("Compile {m} ({compiler.seen.length-compiler.todos.length}/{compiler.seen.length})", 3) m.compile_to_c(compiler) end - self.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2) - - compiler.display_stats - - var time1 = get_time - self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) - write_and_make(compiler) - end -end - -# Compiler that use global compilation and perform hard optimisations like: -# * customization -# * switch dispatch -# * inlining -class GlobalCompiler - super AbstractCompiler - - redef type VISITOR: GlobalCompilerVisitor - - # The result of the RTA (used to know live types and methods) - var runtime_type_analysis: RapidTypeAnalysis + modelbuilder.toolcontext.info("Total methods to compile to C: {compiler.seen.length}", 2) - init - do - 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 - if t.ctype != "val*" or t.mclass.name == "Pointer" then - self.live_primitive_types.add(t) - end - end end # Compile class names (for the class_name and output_class_name methods) @@ -434,7 +441,7 @@ class GlobalCompilerVisitor if args.first.mcasttype isa MNullableType or args.first.mcasttype isa MNullType and consider_null then # The reciever is potentially null, so we have to 3 cases: ==, != or NullPointerException self.add("if ({args.first} == NULL) \{ /* Special null case */") - if m.name == "==" then + if m.name == "==" or m.name == "is_same_instance" then assert res != null if args[1].mcasttype isa MNullableType then self.add("{res} = ({args[1]} == NULL);") @@ -965,7 +972,7 @@ private class CustomizedRuntimeFunction selfvar.is_exact = true end var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) + var frame = new StaticFrame(v, mmethoddef, recv, arguments) v.frame = frame var sig = new FlatBuffer @@ -1024,7 +1031,7 @@ private class CustomizedRuntimeFunction ret = v.resolve_for(ret, arguments.first) end if self.mmethoddef.can_inline(v) then - var frame = new Frame(v, self.mmethoddef, self.recv, arguments) + var frame = new StaticFrame(v, self.mmethoddef, self.recv, arguments) frame.returnlabel = v.get_name("RET_LABEL") if ret != null then frame.returnvar = v.new_var(ret) diff --git a/src/compiler/separate_compiler.nit b/src/compiler/separate_compiler.nit index 13f0bd7..9c03910 100644 --- a/src/compiler/separate_compiler.nit +++ b/src/compiler/separate_compiler.nit @@ -29,6 +29,9 @@ redef class ToolContext var opt_no_union_attribute = new OptionBool("Put primitive attibutes in a box instead of an union", "--no-union-attribute") # --no-shortcut-equate var opt_no_shortcut_equate = new OptionBool("Always call == in a polymorphic way", "--no-shortcut-equal") + # --colors-are-symbols + var opt_colors_are_symbols = new OptionBool("Store colors as symbols (faster)", "--colors-are-symbols") + # --inline-coloring-numbers var opt_inline_coloring_numbers = new OptionBool("Inline colors and ids (semi-global)", "--inline-coloring-numbers") # --inline-some-methods @@ -50,7 +53,7 @@ redef class ToolContext self.option_context.add_option(self.opt_separate) self.option_context.add_option(self.opt_no_inline_intern) self.option_context.add_option(self.opt_no_union_attribute) - self.option_context.add_option(self.opt_no_shortcut_equate) + self.option_context.add_option(self.opt_no_shortcut_equate, opt_colors_are_symbols) self.option_context.add_option(self.opt_inline_coloring_numbers, opt_inline_some_methods, opt_direct_call_monomorph, opt_skip_dead_methods, opt_semi_global) self.option_context.add_option(self.opt_colo_dead_methods) self.option_context.add_option(self.opt_tables_metrics) @@ -90,12 +93,55 @@ redef class ModelBuilder self.toolcontext.info("*** GENERATING C ***", 1) var compiler = new SeparateCompiler(mainmodule, self, runtime_type_analysis) + compiler.do_compilation + compiler.display_stats + + var time1 = get_time + self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) + write_and_make(compiler) + end + + # Count number of invocations by VFT + private var nb_invok_by_tables = 0 + # Count number of invocations by direct call + private var nb_invok_by_direct = 0 + # Count number of invocations by inlining + private var nb_invok_by_inline = 0 +end + +# Singleton that store the knowledge about the separate compilation process +class SeparateCompiler + super AbstractCompiler + + redef type VISITOR: SeparateCompilerVisitor + + # The result of the RTA (used to know live types and methods) + var runtime_type_analysis: nullable RapidTypeAnalysis + + private var undead_types: Set[MType] = new HashSet[MType] + private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]] + + private var type_ids: Map[MType, Int] is noinit + private var type_colors: Map[MType, Int] is noinit + private var opentype_colors: Map[MType, Int] is noinit + protected var method_colors: Map[PropertyLayoutElement, Int] is noinit + protected var attr_colors: Map[MAttribute, Int] is noinit + + init do + var file = new_file("nit.common") + self.header = new CodeWriter(file) + self.compile_box_kinds + end + + redef fun do_compilation + do + var compiler = self compiler.compile_header var c_name = mainmodule.c_name # compile class structures - self.toolcontext.info("Property coloring", 2) + modelbuilder.toolcontext.info("Property coloring", 2) compiler.new_file("{c_name}.classes") compiler.do_property_coloring for m in mainmodule.in_importation.greaters do @@ -113,14 +159,22 @@ redef class ModelBuilder # compile methods for m in mainmodule.in_importation.greaters do - self.toolcontext.info("Generate C for module {m.full_name}", 2) + modelbuilder.toolcontext.info("Generate C for module {m.full_name}", 2) compiler.new_file("{m.c_name}.sep") compiler.compile_module_to_c(m) end # compile live & cast type structures - self.toolcontext.info("Type coloring", 2) + modelbuilder.toolcontext.info("Type coloring", 2) compiler.new_file("{c_name}.types") + compiler.compile_types + end + + # Color and compile type structures and cast information + fun compile_types + do + var compiler = self + var mtypes = compiler.do_type_coloring for t in mtypes do compiler.compile_type_to_c(t) @@ -131,43 +185,6 @@ redef class ModelBuilder compiler.compile_type_to_c(t) end - compiler.display_stats - - var time1 = get_time - self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) - write_and_make(compiler) - end - - # Count number of invocations by VFT - private var nb_invok_by_tables = 0 - # Count number of invocations by direct call - private var nb_invok_by_direct = 0 - # Count number of invocations by inlining - private var nb_invok_by_inline = 0 -end - -# Singleton that store the knowledge about the separate compilation process -class SeparateCompiler - super AbstractCompiler - - redef type VISITOR: SeparateCompilerVisitor - - # The result of the RTA (used to know live types and methods) - var runtime_type_analysis: nullable RapidTypeAnalysis - - private var undead_types: Set[MType] = new HashSet[MType] - private var live_unresolved_types: Map[MClassDef, Set[MType]] = new HashMap[MClassDef, HashSet[MType]] - - private var type_ids: Map[MType, Int] is noinit - private var type_colors: Map[MType, Int] is noinit - private var opentype_colors: Map[MType, Int] is noinit - protected var method_colors: Map[PropertyLayoutElement, Int] is noinit - protected var attr_colors: Map[MAttribute, Int] is noinit - - init do - var file = new_file("nit.common") - self.header = new CodeWriter(file) - self.compile_box_kinds end redef fun compile_header_structs do @@ -238,27 +255,21 @@ class SeparateCompiler fun compile_color_const(v: SeparateCompilerVisitor, m: Object, color: Int) do if color_consts_done.has(m) then return - if m isa MProperty then + if m isa MEntity then if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then self.provide_declaration(m.const_color, "#define {m.const_color} {color}") - else + else if not modelbuilder.toolcontext.opt_colors_are_symbols.value or not v.compiler.target_platform.supports_linker_script then self.provide_declaration(m.const_color, "extern const int {m.const_color};") v.add("const int {m.const_color} = {color};") - end - else if m isa MPropDef then - if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then - self.provide_declaration(m.const_color, "#define {m.const_color} {color}") else - self.provide_declaration(m.const_color, "extern const int {m.const_color};") - v.add("const int {m.const_color} = {color};") - end - else if m isa MType then - if modelbuilder.toolcontext.opt_inline_coloring_numbers.value then - self.provide_declaration(m.const_color, "#define {m.const_color} {color}") - else - self.provide_declaration(m.const_color, "extern const int {m.const_color};") - v.add("const int {m.const_color} = {color};") + # The color 'C' is the ``address'' of a false static variable 'XC' + self.provide_declaration(m.const_color, "#define {m.const_color} ((long)&X{m.const_color})\nextern const void X{m.const_color};") + if color == -1 then color = 0 # Symbols cannot be negative, so just use 0 for dead things + # Teach the linker that the address of 'XC' is `color`. + linker_script.add("X{m.const_color} = {color};") end + else + abort end color_consts_done.add(m) end @@ -549,7 +560,7 @@ class SeparateCompiler var r = pd.separate_runtime_function r.compile_to_c(self) var r2 = pd.virtual_runtime_function - r2.compile_to_c(self) + if r2 != r then r2.compile_to_c(self) end end self.mainmodule = old_module @@ -1063,8 +1074,8 @@ class SeparateCompilerVisitor do var rta = compiler.runtime_type_analysis var mmethod = callsite.mproperty - # TODO: Inlining of new-style constructors - if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and not mmethod.is_root_init then + # TODO: Inlining of new-style constructors with initializers + if compiler.modelbuilder.toolcontext.opt_direct_call_monomorph.value and rta != null and callsite.mpropdef.initializers.is_empty then var tgs = rta.live_targets(callsite) if tgs.length == 1 then # DIRECT CALL @@ -1112,10 +1123,10 @@ class SeparateCompilerVisitor var res: nullable RuntimeVariable = null var recv = arguments.first var consider_null = not self.compiler.modelbuilder.toolcontext.opt_no_check_null.value or mmethod.name == "==" or mmethod.name == "!=" - var maybenull = recv.mcasttype isa MNullableType and consider_null + var maybenull = (recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType) and consider_null if maybenull then self.add("if ({recv} == NULL) \{") - if mmethod.name == "==" then + if mmethod.name == "==" or mmethod.name == "is_same_instance" then res = self.new_var(bool_type) var arg = arguments[1] if arg.mcasttype isa MNullableType then @@ -1142,15 +1153,15 @@ class SeparateCompilerVisitor else self.add("\{") end - if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=") then - if res == null then res = self.new_var(bool_type) - # Recv is not null, thus is arg is, it is easy to conclude (and respect the invariants) + if not self.compiler.modelbuilder.toolcontext.opt_no_shortcut_equate.value and (mmethod.name == "==" or mmethod.name == "!=" or mmethod.name == "is_same_instance") then + # Recv is not null, thus if arg is, it is easy to conclude (and respect the invariants) var arg = arguments[1] if arg.mcasttype isa MNullType then - if mmethod.name == "==" then - self.add("{res} = 0; /* arg is null but recv is not */") - else + if res == null then res = self.new_var(bool_type) + if mmethod.name == "!=" then self.add("{res} = 1; /* arg is null and recv is not */") + else # `==` and `is_same_instance` + self.add("{res} = 0; /* arg is null but recv is not */") end self.add("\}") # closes the null case self.add("if (0) \{") # what follow is useless, CC will drop it @@ -1169,8 +1180,10 @@ class SeparateCompilerVisitor var res0 = before_send(mmethod, arguments) + var runtime_function = mmethod.intro.virtual_runtime_function + var msignature = runtime_function.called_signature + var res: nullable RuntimeVariable - var msignature = mmethod.intro.msignature.resolve_for(mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.bound_mtype, mmethod.intro.mclassdef.mmodule, true) var ret = msignature.return_mtype if ret == null then res = null @@ -1178,10 +1191,8 @@ class SeparateCompilerVisitor res = self.new_var(ret) end - var s = new FlatBuffer var ss = new FlatBuffer - s.append("val*") ss.append("{recv}") for i in [0..msignature.arity[ do var a = arguments[i+1] @@ -1189,16 +1200,12 @@ class SeparateCompilerVisitor if i == msignature.vararg_rank then t = arguments[i+1].mcasttype end - s.append(", {t.ctype}") a = self.autobox(a, t) ss.append(", {a}") end - - var r - if ret == null then r = "void" else r = ret.ctype self.require_declaration(const_color) - var call = "(({r} (*)({s}))({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/" + var call = "(({runtime_function.c_ret} (*){runtime_function.c_sig})({arguments.first}->class->vft[{const_color}]))({ss}) /* {mmethod} on {arguments.first.inspect}*/" if res != null then self.add("{res} = {call};") @@ -1234,7 +1241,7 @@ class SeparateCompilerVisitor (compiler.modelbuilder.toolcontext.opt_inline_some_methods.value and mmethoddef.can_inline(self)) then compiler.modelbuilder.nb_invok_by_inline += 1 if compiler.modelbuilder.toolcontext.opt_invocation_metrics.value then add("count_invoke_by_inline++;") - var frame = new Frame(self, mmethoddef, recvtype, arguments) + var frame = new StaticFrame(self, mmethoddef, recvtype, arguments) frame.returnlabel = self.get_name("RET_LABEL") frame.returnvar = res var old_frame = self.frame @@ -1284,11 +1291,11 @@ class SeparateCompilerVisitor # of the method (ie recv) if the static type is unresolved # This is more complex than usual because the unresolved type must not be resolved # with the current receiver (ie self). - # Therefore to isolate the resolution from self, a local Frame is created. + # Therefore to isolate the resolution from self, a local StaticFrame is created. # One can see this implementation as an inlined method of the receiver whose only # job is to allocate the array var old_frame = self.frame - var frame = new Frame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv]) + var frame = new StaticFrame(self, mpropdef, mpropdef.mclassdef.bound_mtype, [recv]) self.frame = frame #print "required Array[{elttype}] for recv {recv.inspect}. bound=Array[{self.resolve_for(elttype, recv)}]. selfvar={frame.arguments.first.inspect}" var res = self.array_instance(varargs, elttype) @@ -1767,108 +1774,112 @@ class SeparateCompilerVisitor end redef class MMethodDef - fun separate_runtime_function: AbstractRuntimeFunction + # The C function associated to a mmethoddef + fun separate_runtime_function: SeparateRuntimeFunction do var res = self.separate_runtime_function_cache if res == null then - res = new SeparateRuntimeFunction(self) + var recv = mclassdef.bound_mtype + var msignature = msignature.resolve_for(recv, recv, mclassdef.mmodule, true) + res = new SeparateRuntimeFunction(self, recv, msignature, c_name) self.separate_runtime_function_cache = res end return res end private var separate_runtime_function_cache: nullable SeparateRuntimeFunction - fun virtual_runtime_function: AbstractRuntimeFunction + # The C function associated to a mmethoddef, that can be stored into a VFT of a class + # The first parameter (the reciever) is always typed by val* in order to accept an object value + # The C-signature is always compatible with the intro + fun virtual_runtime_function: SeparateRuntimeFunction do var res = self.virtual_runtime_function_cache if res == null then - res = new VirtualRuntimeFunction(self) + # Because the function is virtual, the signature must match the one of the original class + var intromclassdef = mproperty.intro.mclassdef + var recv = intromclassdef.bound_mtype + + res = separate_runtime_function + if res.called_recv == recv then + self.virtual_runtime_function_cache = res + return res + end + + var msignature = mproperty.intro.msignature.resolve_for(recv, recv, intromclassdef.mmodule, true) + + if recv.ctype == res.called_recv.ctype and msignature.c_equiv(res.called_signature) then + self.virtual_runtime_function_cache = res + return res + end + + res = new SeparateRuntimeFunction(self, recv, msignature, "VIRTUAL_{c_name}") self.virtual_runtime_function_cache = res + res.is_thunk = true end return res end - private var virtual_runtime_function_cache: nullable VirtualRuntimeFunction + private var virtual_runtime_function_cache: nullable SeparateRuntimeFunction +end + +redef class MSignature + # Does the C-version of `self` the same than the C-version of `other`? + fun c_equiv(other: MSignature): Bool + do + if self == other then return true + if arity != other.arity then return false + for i in [0..arity[ do + if mparameters[i].mtype.ctype != other.mparameters[i].mtype.ctype then return false + end + if return_mtype != other.return_mtype then + if return_mtype == null or other.return_mtype == null then return false + if return_mtype.ctype != other.return_mtype.ctype then return false + end + return true + end end # The C function associated to a methoddef separately compiled class SeparateRuntimeFunction super AbstractRuntimeFunction - redef fun build_c_name: String do return "{mmethoddef.c_name}" + # The call-side static receiver + var called_recv: MType - redef fun to_s do return self.mmethoddef.to_s + # The call-side static signature + var called_signature: MSignature - redef fun compile_to_c(compiler) - do - var mmethoddef = self.mmethoddef + # The name on the compiled method + redef var build_c_name: String - var recv = self.mmethoddef.mclassdef.bound_mtype - var v = compiler.new_visitor - var selfvar = new RuntimeVariable("self", recv, recv) - var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) - v.frame = frame + # Statically call the original body instead + var is_thunk = false - var msignature = mmethoddef.msignature.resolve_for(mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.bound_mtype, mmethoddef.mclassdef.mmodule, true) + redef fun to_s do return self.mmethoddef.to_s - var sig = new FlatBuffer - var comment = new FlatBuffer - var ret = msignature.return_mtype + # The C return type (something or `void`) + var c_ret: String is lazy do + var ret = called_signature.return_mtype if ret != null then - sig.append("{ret.ctype} ") + return ret.ctype else - sig.append("void ") + return "void" end - sig.append(self.c_name) - sig.append("({selfvar.mtype.ctype} {selfvar}") - comment.append("({selfvar}: {selfvar.mtype}") - arguments.add(selfvar) - for i in [0..msignature.arity[ do - var mtype = msignature.mparameters[i].mtype - if i == msignature.vararg_rank then - mtype = v.get_class("Array").get_mtype([mtype]) + end + + # The C signature (only the parmeter part) + var c_sig: String is lazy do + var sig = new FlatBuffer + sig.append("({called_recv.ctype} self") + for i in [0..called_signature.arity[ do + var mtype = called_signature.mparameters[i].mtype + if i == called_signature.vararg_rank then + mtype = mmethoddef.mclassdef.mmodule.get_primitive_class("Array").get_mtype([mtype]) end - comment.append(", {mtype}") sig.append(", {mtype.ctype} p{i}") - var argvar = new RuntimeVariable("p{i}", mtype, mtype) - arguments.add(argvar) end sig.append(")") - comment.append(")") - if ret != null then - comment.append(": {ret}") - end - compiler.provide_declaration(self.c_name, "{sig};") - - v.add_decl("/* method {self} for {comment} */") - v.add_decl("{sig} \{") - if ret != null then - frame.returnvar = v.new_var(ret) - end - frame.returnlabel = v.get_name("RET_LABEL") - - if recv != arguments.first.mtype then - #print "{self} {recv} {arguments.first}" - end - mmethoddef.compile_inside_to_c(v, arguments) - - v.add("{frame.returnlabel.as(not null)}:;") - if ret != null then - v.add("return {frame.returnvar.as(not null)};") - end - v.add("\}") - if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" + return sig.to_s end -end - -# The C function associated to a methoddef on a primitive type, stored into a VFT of a class -# The first parameter (the reciever) is always typed by val* in order to accept an object value -class VirtualRuntimeFunction - super AbstractRuntimeFunction - - redef fun build_c_name: String do return "VIRTUAL_{mmethoddef.c_name}" - - redef fun to_s do return self.mmethoddef.to_s redef fun compile_to_c(compiler) do @@ -1876,25 +1887,20 @@ class VirtualRuntimeFunction var recv = self.mmethoddef.mclassdef.bound_mtype var v = compiler.new_visitor - var selfvar = new RuntimeVariable("self", v.object_type, recv) + var selfvar = new RuntimeVariable("self", called_recv, recv) var arguments = new Array[RuntimeVariable] - var frame = new Frame(v, mmethoddef, recv, arguments) + var frame = new StaticFrame(v, mmethoddef, recv, arguments) v.frame = frame + var msignature = called_signature + var ret = called_signature.return_mtype + var sig = new FlatBuffer var comment = new FlatBuffer - - # Because the function is virtual, the signature must match the one of the original class - var intromclassdef = self.mmethoddef.mproperty.intro.mclassdef - var msignature = mmethoddef.mproperty.intro.msignature.resolve_for(intromclassdef.bound_mtype, intromclassdef.bound_mtype, intromclassdef.mmodule, true) - var ret = msignature.return_mtype - if ret != null then - sig.append("{ret.ctype} ") - else - sig.append("void ") - end + sig.append(c_ret) + sig.append(" ") sig.append(self.c_name) - sig.append("({selfvar.mtype.ctype} {selfvar}") + sig.append(c_sig) comment.append("({selfvar}: {selfvar.mtype}") arguments.add(selfvar) for i in [0..msignature.arity[ do @@ -1903,11 +1909,9 @@ class VirtualRuntimeFunction mtype = v.get_class("Array").get_mtype([mtype]) end comment.append(", {mtype}") - sig.append(", {mtype.ctype} p{i}") var argvar = new RuntimeVariable("p{i}", mtype, mtype) arguments.add(argvar) end - sig.append(")") comment.append(")") if ret != null then comment.append(": {ret}") @@ -1921,10 +1925,14 @@ class VirtualRuntimeFunction end frame.returnlabel = v.get_name("RET_LABEL") - var subret = v.call(mmethoddef, recv, arguments) - if ret != null then - assert subret != null - v.assign(frame.returnvar.as(not null), subret) + if is_thunk then + var subret = v.call(mmethoddef, recv, arguments) + if ret != null then + assert subret != null + v.assign(frame.returnvar.as(not null), subret) + end + else + mmethoddef.compile_inside_to_c(v, arguments) end v.add("{frame.returnlabel.as(not null)}:;") @@ -1932,27 +1940,22 @@ class VirtualRuntimeFunction v.add("return {frame.returnvar.as(not null)};") end v.add("\}") - if not self.c_name.has_substring("VIRTUAL", 0) then compiler.names[self.c_name] = "{mmethoddef.mclassdef.mmodule.name}::{mmethoddef.mclassdef.mclass.name}::{mmethoddef.mproperty.name} ({mmethoddef.location.file.filename}--{mmethoddef.location.line_start})" + compiler.names[self.c_name] = "{mmethoddef.full_name} ({mmethoddef.location.file.filename}:{mmethoddef.location.line_start})" end - - # TODO ? - redef fun call(v, arguments) do abort end -redef class MType - fun const_color: String do return "COLOR_{c_name}" +redef class MEntity + var const_color: String is lazy do return "COLOR_{c_name}" end interface PropertyLayoutElement end redef class MProperty super PropertyLayoutElement - fun const_color: String do return "COLOR_{c_name}" end redef class MPropDef super PropertyLayoutElement - fun const_color: String do return "COLOR_{c_name}" end redef class AMethPropdef diff --git a/src/compiler/separate_erasure_compiler.nit b/src/compiler/separate_erasure_compiler.nit index 233bf54..2c90e65 100644 --- a/src/compiler/separate_erasure_compiler.nit +++ b/src/compiler/separate_erasure_compiler.nit @@ -65,35 +65,8 @@ redef class ModelBuilder self.toolcontext.info("*** GENERATING C ***", 1) var compiler = new SeparateErasureCompiler(mainmodule, self, runtime_type_analysis) - compiler.compile_header - - var c_name = mainmodule.c_name - - # compile class structures - self.toolcontext.info("Property coloring", 2) - compiler.new_file("{c_name}.tables") - compiler.do_property_coloring - for m in mainmodule.in_importation.greaters do - for mclass in m.intro_mclasses do - compiler.compile_class_to_c(mclass) - end - end - compiler.compile_color_consts(compiler.vt_colors) - - # The main function of the C - compiler.new_file("{c_name}.main") - compiler.compile_nitni_global_ref_functions - compiler.compile_main_function - - # compile methods - for m in mainmodule.in_importation.greaters do - self.toolcontext.info("Generate C for module {m.full_name}", 2) - compiler.new_file("{m.c_name}.sep") - compiler.compile_module_to_c(m) - end - + compiler.do_compilation compiler.display_stats - var time1 = get_time self.toolcontext.info("*** END GENERATING C: {time1-time0} ***", 2) write_and_make(compiler) @@ -262,13 +235,9 @@ class SeparateErasureCompiler v.add_decl("NULL, /* DEAD {mclass.intro_mmodule}:{mclass}:{mpropdef} */") continue end - if true or mpropdef.mclassdef.bound_mtype.ctype != "val*" then - v.require_declaration("VIRTUAL_{mpropdef.c_name}") - v.add_decl("(nitmethod_t)VIRTUAL_{mpropdef.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */") - else - v.require_declaration("{mpropdef.c_name}") - v.add_decl("(nitmethod_t){mpropdef.c_name}, /* pointer to {mclass.intro_mmodule}:{mclass}:{mpropdef} */") - end + var rf = mpropdef.virtual_runtime_function + v.require_declaration(rf.c_name) + v.add_decl("(nitmethod_t){rf.c_name}, /* pointer to {mpropdef.full_name} */") end end v.add_decl("\}") @@ -298,7 +267,7 @@ class SeparateErasureCompiler self.header.add_decl("\};") #Build BOX - self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype});") + self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});") v.add_decl("/* allocate {mtype} */") v.add_decl("val* BOX_{mtype.c_name}({mtype.ctype} value) \{") v.add("struct instance_{c_name}*res = nit_alloc(sizeof(struct instance_{c_name}));") @@ -435,6 +404,11 @@ class SeparateErasureCompiler end end + redef fun compile_types + do + compile_color_consts(vt_colors) + end + redef fun new_visitor do return new SeparateErasureCompilerVisitor(self) # Stats diff --git a/src/doc/doc_down.nit b/src/doc/doc_down.nit index 1c77939..1cdc51a 100644 --- a/src/doc/doc_down.nit +++ b/src/doc/doc_down.nit @@ -143,6 +143,10 @@ private class NitdocDecorator end end +# Decorator for span elements. +# +# Because inline comments can appear as span elements, +# InlineDecorator do not decorate things like paragraphs or headers. private class InlineDecorator super NitdocDecorator @@ -150,6 +154,10 @@ private class InlineDecorator v.emit_in block end + redef fun add_headline(v, block) do + v.emit_in block + end + redef fun add_code(v, block) do # Try to parse code var ast = toolcontext.parse_something(block.block.text.to_s) diff --git a/src/doc/doc_model.nit b/src/doc/doc_model.nit index c25c7dc..2c80ffb 100644 --- a/src/doc/doc_model.nit +++ b/src/doc/doc_model.nit @@ -32,8 +32,8 @@ redef class Location end redef class MEntity - # HTML Escaped name - fun nitdoc_name: String is abstract + # HTML-escaped name. + fun nitdoc_name: String do return name.html_escape # ID used as a HTML unique ID and in file names. # @@ -52,6 +52,7 @@ redef class MEntity # A template link to the mentity `nitdoc_id` fun tpl_anchor: TplLink do var tpl = new TplLink("#{nitdoc_id}", nitdoc_name) + var mdoc = mdoc_or_fallback if mdoc != null then tpl.title = mdoc.short_comment end @@ -61,6 +62,7 @@ redef class MEntity # A template link to the mentity `nitdoc_url` fun tpl_link: TplLink do var tpl = new TplLink(nitdoc_url, nitdoc_name) + var mdoc = mdoc_or_fallback if mdoc != null then tpl.title = mdoc.short_comment end @@ -70,6 +72,7 @@ redef class MEntity # A template article that briefly describe the entity fun tpl_short_article: TplArticle do var tpl = tpl_article + var mdoc = mdoc_or_fallback if mdoc != null then tpl.content = mdoc.tpl_short_comment end @@ -100,6 +103,7 @@ redef class MEntity var lnk = new Template lnk.add new TplLabel.with_classes(tpl_css_classes) lnk.add tpl_link + var mdoc = mdoc_or_fallback if mdoc != null then lnk.add ": " lnk.add mdoc.tpl_short_comment @@ -130,6 +134,7 @@ redef class MConcern private fun tpl_concern_item: TplListItem do var lnk = new Template lnk.add tpl_anchor + var mdoc = mdoc_or_fallback if mdoc != null then lnk.add ": " lnk.add mdoc.tpl_short_comment @@ -140,16 +145,8 @@ end redef class MProject redef var nitdoc_id = name.to_cmangle is lazy - redef fun nitdoc_name do return name.html_escape redef fun nitdoc_url do return root.nitdoc_url - redef fun mdoc do - if root != null then - return root.mdoc - end - return super - end - redef fun tpl_declaration do var tpl = new Template tpl.add "project " @@ -173,8 +170,6 @@ redef class MProject end redef class MGroup - redef fun nitdoc_name do return name.html_escape - redef var nitdoc_id is lazy do if parent != null then return "{parent.nitdoc_id}__{name.to_cmangle}" @@ -213,8 +208,6 @@ redef class MGroup end redef class MModule - redef fun nitdoc_name do return name.html_escape - redef var nitdoc_id is lazy do if mgroup != null then if mgroup.mmodules.length == 1 then @@ -248,6 +241,7 @@ redef class MModule redef fun tpl_definition do var tpl = new TplClassDefinition + var mdoc = mdoc_or_fallback if mdoc != null then tpl.comment = mdoc.tpl_comment end @@ -258,10 +252,9 @@ redef class MModule end redef class MClass - redef fun nitdoc_name do return name.html_escape redef var nitdoc_id = "{intro_mmodule.nitdoc_id}__{name.to_cmangle}" is lazy redef fun nitdoc_url do return "class_{nitdoc_id}.html" - redef fun mdoc do return intro.mdoc + redef fun mdoc_or_fallback do return intro.mdoc redef fun tpl_declaration do return intro.tpl_declaration @@ -290,7 +283,7 @@ redef class MClass tpl.add "[" var parameter_names = new Array[String] for p in mparameters do - parameter_names.add(p.name) + parameter_names.add(p.nitdoc_name) end tpl.add parameter_names.join(", ") tpl.add "]" @@ -312,6 +305,8 @@ redef class MClassDef redef var nitdoc_id = "{mmodule.nitdoc_id}__{name.to_cmangle}" is lazy redef fun nitdoc_url do return "{mclass.nitdoc_url}#{nitdoc_id}" + redef fun mdoc_or_fallback do return mdoc or else mclass.mdoc_or_fallback + redef fun tpl_namespace do var tpl = new Template tpl.add mmodule.tpl_namespace @@ -330,6 +325,7 @@ redef class MClassDef title.add "in " title.add mmodule.tpl_namespace tpl.subtitle = title + var mdoc = mdoc_or_fallback if mdoc != null then tpl.content = mdoc.tpl_comment end @@ -358,7 +354,7 @@ redef class MClassDef if not mparameters.is_empty then tpl.add "[" for i in [0..mparameters.length[ do - tpl.add "{mparameters[i].name}: " + tpl.add "{mparameters[i].nitdoc_name}: " tpl.add bound_mtype.arguments[i].tpl_signature if i < mparameters.length - 1 then tpl.add ", " end @@ -370,6 +366,7 @@ redef class MClassDef redef fun tpl_definition do var tpl = new TplClassDefinition tpl.namespace = tpl_namespace + var mdoc = mdoc_or_fallback if mdoc != null then tpl.comment = mdoc.tpl_comment end @@ -379,8 +376,8 @@ redef class MClassDef redef fun tpl_css_classes do var set = new HashSet[String] if is_intro then set.add "intro" - set.add_all mclass.intro.modifiers - set.add_all modifiers + for m in mclass.intro.modifiers do set.add m.to_cmangle + for m in modifiers do set.add m.to_cmangle return set.to_a end @@ -388,41 +385,7 @@ redef class MClassDef var tpl = new Template for modifier in modifiers do if modifier == "public" then continue - tpl.add "{modifier} " - end - return tpl - end - - redef fun tpl_list_item do - var lnk = new Template - lnk.add new TplLabel.with_classes(tpl_css_classes) - lnk.add tpl_link - if mdoc != null then - lnk.add ": " - lnk.add mdoc.tpl_short_comment - else if mclass.intro.mdoc != null then - lnk.add ": " - lnk.add mclass.intro.mdoc.tpl_short_comment - end - return new TplListItem.with_content(lnk) - end - - redef fun tpl_anchor: TplLink do - var tpl = new TplLink("#{nitdoc_id}", nitdoc_name) - if mdoc != null then - tpl.title = mdoc.short_comment - else if mclass.intro.mdoc != null then - tpl.title = mclass.intro.mdoc.short_comment - end - return tpl - end - - redef fun tpl_link: TplLink do - var tpl = new TplLink(nitdoc_url, nitdoc_name) - if mdoc != null then - tpl.title = mdoc.short_comment - else if mclass.intro.mdoc != null then - tpl.title = mclass.intro.mdoc.short_comment + tpl.add "{modifier.html_escape} " end return tpl end @@ -430,10 +393,9 @@ end redef class MProperty redef var nitdoc_id = "{intro_mclassdef.mclass.nitdoc_id}__{name.to_cmangle}" is lazy - redef fun nitdoc_name do return name.html_escape redef fun nitdoc_url do return "property_{nitdoc_id}.html" - redef fun mdoc do return intro.mdoc + redef fun mdoc_or_fallback do return intro.mdoc redef fun tpl_namespace do var tpl = new Template @@ -460,25 +422,7 @@ redef class MPropDef redef var nitdoc_id = "{mclassdef.nitdoc_id}__{name.to_cmangle}" is lazy redef fun nitdoc_url do return "{mproperty.nitdoc_url}#{nitdoc_id}" - redef fun tpl_anchor: TplLink do - var tpl = new TplLink("#{nitdoc_id}", nitdoc_name) - if mdoc != null then - tpl.title = mdoc.short_comment - else if mproperty.intro.mdoc != null then - tpl.title = mproperty.intro.mdoc.short_comment - end - return tpl - end - - redef fun tpl_link: TplLink do - var tpl = new TplLink(nitdoc_url, nitdoc_name) - if mdoc != null then - tpl.title = mdoc.short_comment - else if mproperty.intro.mdoc != null then - tpl.title = mproperty.intro.mdoc.short_comment - end - return tpl - end + redef fun mdoc_or_fallback do return mdoc or else mproperty.mdoc_or_fallback redef fun tpl_namespace do var tpl = new Template @@ -496,6 +440,7 @@ redef class MPropDef title.add mclassdef.tpl_link tpl.title = title tpl.subtitle = tpl_declaration + var mdoc = mdoc_or_fallback if mdoc != null then tpl.content = mdoc.tpl_comment end @@ -505,6 +450,7 @@ redef class MPropDef redef fun tpl_definition do var tpl = new TplDefinition tpl.namespace = mclassdef.tpl_namespace + var mdoc = mdoc_or_fallback if mdoc != null then tpl.comment = mdoc.tpl_comment end @@ -522,8 +468,8 @@ redef class MPropDef redef fun tpl_css_classes do var set = new HashSet[String] if is_intro then set.add "intro" - set.add_all mproperty.intro.modifiers - set.add_all modifiers + for m in mproperty.intro.modifiers do set.add m.to_cmangle + for m in modifiers do set.add m.to_cmangle return set.to_a end @@ -531,7 +477,7 @@ redef class MPropDef var tpl = new Template for modifier in modifiers do if modifier == "public" then continue - tpl.add "{modifier} " + tpl.add "{modifier.html_escape} " end return tpl end @@ -544,12 +490,10 @@ redef class MPropDef var anchor = tpl_link anchor.href = "{mclassdef.mclass.nitdoc_url}#{mproperty.nitdoc_id}" lnk.add anchor + var mdoc = mdoc_or_fallback if mdoc != null then lnk.add ": " lnk.add mdoc.tpl_short_comment - else if mproperty.intro.mdoc != null then - lnk.add ": " - lnk.add mproperty.intro.mdoc.tpl_short_comment end return new TplListItem.with_content(lnk) end @@ -562,6 +506,7 @@ redef class MPropDef var anchor = mclassdef.tpl_link anchor.href = "{mclassdef.mclass.nitdoc_url}#{mproperty.nitdoc_id}" lnk.add anchor + var mdoc = mdoc_or_fallback if mdoc != null then lnk.add ": " lnk.add mdoc.tpl_short_comment @@ -588,7 +533,7 @@ redef class MMethod var tpl = new Template var params = new Array[String] for param in intro.msignature.mparameters do - params.add param.name + params.add param.name.html_escape end if not params.is_empty then tpl.add "(" @@ -651,7 +596,7 @@ end redef class MParameterType redef fun tpl_link do - return new TplLink.with_title("{mclass.nitdoc_url}#FT_{name}", name, "formal type") + return new TplLink.with_title("{mclass.nitdoc_url}#FT_{name.to_cmangle}", name, "formal type") end redef fun tpl_signature do return tpl_link end @@ -761,6 +706,7 @@ redef class MInnerClassDef redef fun tpl_definition do var tpl = new TplClassDefinition tpl.namespace = mclassdef.tpl_namespace + var mdoc = mdoc_or_fallback if mdoc != null then tpl.comment = mdoc.tpl_comment end diff --git a/src/doc/doc_pages.nit b/src/doc/doc_pages.nit index b400c6d..38e6cdf 100644 --- a/src/doc/doc_pages.nit +++ b/src/doc/doc_pages.nit @@ -166,7 +166,7 @@ class Nitdoc private fun modules do for mmodule in model.mmodules do - if mmodule.is_fictive then continue + if mmodule.is_fictive or mmodule.is_test_suite then continue var page = new NitdocModule(ctx, model, mainmodule, mmodule) page.render.write_to_file("{ctx.output_dir.to_s}/{page.page_url}") end @@ -211,7 +211,7 @@ class QuickSearch init do for mmodule in model.mmodules do - if mmodule.is_fictive then continue + if mmodule.is_fictive or mmodule.is_test_suite then continue add_result_for(mmodule.name, mmodule.full_name, mmodule.nitdoc_url) end for mclass in model.mclasses do @@ -386,7 +386,7 @@ abstract class NitdocPage var source = ctx.opt_source.value if source == null then var url = location.file.filename.simplify_path - return "View Source" + return "View Source" end # THIS IS JUST UGLY ! (but there is no replace yet) var x = source.split_with("%f") @@ -396,7 +396,7 @@ abstract class NitdocPage x = source.split_with("%L") source = x.join(location.line_end.to_s) source = source.simplify_path - return "View Source" + return "View Source" end # MProject description template @@ -404,8 +404,9 @@ abstract class NitdocPage var article = mproject.tpl_article article.subtitle = mproject.tpl_declaration article.content = mproject.tpl_definition - if mproject.mdoc != null then - article.content = mproject.mdoc.tpl_short_comment + var mdoc = mproject.mdoc_or_fallback + if mdoc != null then + article.content = mdoc.tpl_short_comment end return article end @@ -517,7 +518,8 @@ abstract class NitdocPage else var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mprop.nitdoc_id}" - var lnk = new TplLink.with_title(def_url, mprop.name, "Go to introduction") + var lnk = new TplLink.with_title(def_url, mprop.nitdoc_name, + "Go to introduction") title.add "redef " title.add lnk end @@ -525,8 +527,8 @@ abstract class NitdocPage article.title_classes.add "signature" article.summary_title = "{mprop.nitdoc_name}" article.subtitle = main_mpropdef.tpl_namespace - if main_mpropdef.mdoc != null then - article.content = main_mpropdef.mdoc.tpl_comment + if main_mpropdef.mdoc_or_fallback != null then + article.content = main_mpropdef.mdoc_or_fallback.tpl_comment end var subarticle = new TplArticle("{main_mpropdef.nitdoc_id}.redefs") # Add redef in same `MClass` @@ -665,7 +667,7 @@ class NitdocSearch private fun modules_list: Array[MModule] do var sorted = new Array[MModule] for mmodule in model.mmodule_importation_hierarchy do - if mmodule.is_fictive then continue + if mmodule.is_fictive or mmodule.is_test_suite then continue sorted.add mmodule end name_sorter.sort(sorted) @@ -931,7 +933,7 @@ class NitdocModule # Imports var lst = new Array[MModule] for dep in imports do - if dep.is_fictive then continue + if dep.is_fictive or dep.is_test_suite then continue if dep == mmodule then continue lst.add(dep) end @@ -943,7 +945,7 @@ class NitdocModule # Clients lst = new Array[MModule] for dep in clients do - if dep.is_fictive then continue + if dep.is_fictive or dep.is_test_suite then continue if dep == mmodule then continue lst.add(dep) end @@ -1022,10 +1024,10 @@ class NitdocModule fun tpl_dot(mmodules: Collection[MModule]): nullable TplArticle do var poset = new POSet[MModule] for mmodule in mmodules do - if mmodule.is_fictive then continue + if mmodule.is_fictive or mmodule.is_test_suite then continue poset.add_node mmodule for omodule in mmodules do - if mmodule.is_fictive then continue + if omodule.is_fictive or omodule.is_test_suite then continue poset.add_node mmodule if mmodule.in_importation < omodule then poset.add_edge(mmodule, omodule) @@ -1125,8 +1127,9 @@ class NitdocClass classes.add "inherit" var cls_url = mprop.intro.mclassdef.mclass.nitdoc_url var def_url = "{cls_url}#{mprop.nitdoc_id}" - var lnk = new TplLink(def_url, mprop.name) - if mprop.intro.mdoc != null then lnk.title = mprop.intro.mdoc.short_comment + var lnk = new TplLink(def_url, mprop.nitdoc_name) + var mdoc = mprop.intro.mdoc_or_fallback + if mdoc != null then lnk.title = mdoc.short_comment var item = new Template item.add new TplLabel.with_classes(classes) item.add lnk @@ -1148,8 +1151,9 @@ class NitdocClass var section = new TplSection.with_title("top", tpl_title) section.subtitle = mclass.intro.tpl_declaration var article = new TplArticle("comment") - if mclass.mdoc != null then - article.content = mclass.mdoc.tpl_comment + var mdoc = mclass.mdoc_or_fallback + if mdoc != null then + article.content = mdoc.tpl_comment end section.add_child article return section diff --git a/src/doc/doc_templates.nit b/src/doc/doc_templates.nit index 8384ba9..cbb625e 100644 --- a/src/doc/doc_templates.nit +++ b/src/doc/doc_templates.nit @@ -23,7 +23,7 @@ import json::static class TplPage super Template - # Page title in HTML header + # The unescaped page title to put in the HTML header. var title: String is writable, noinit # Page url @@ -70,7 +70,7 @@ class TplPage addn " " addn " " addn " " - addn " {title}" + addn " {title.html_escape}" addn "" add "" diff --git a/src/ffi/cpp.nit b/src/ffi/cpp.nit index 8ca389a..a6a8ca3 100644 --- a/src/ffi/cpp.nit +++ b/src/ffi/cpp.nit @@ -124,7 +124,7 @@ class CPPLanguage # write .cpp and .hpp file cpp_file.header_custom.add("extern \"C\" \{\n") - cpp_file.header_custom.add("#include \"{mmodule.name}._ffi.h\"\n") + cpp_file.header_custom.add("#include \"{mmodule.c_name}._ffi.h\"\n") cpp_file.header_custom.add("\}\n") var file = cpp_file.write_to_files(mmodule, compdir) @@ -158,10 +158,10 @@ class CPPCompilationUnit fun write_to_files(mmodule: MModule, compdir: String): ExternCppFile do - var base_name = "{mmodule.name}._ffi" + var base_name = "{mmodule.c_name}._ffi" var h_file = "{base_name}.hpp" - var guard = "{mmodule.cname.to_s.to_upper}_NIT_HPP" + var guard = "{mmodule.c_name.to_s.to_upper}_NIT_HPP" write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard) diff --git a/src/ffi/extern_classes.nit b/src/ffi/extern_classes.nit index 05d8fd5..bc4f0c4 100644 --- a/src/ffi/extern_classes.nit +++ b/src/ffi/extern_classes.nit @@ -20,7 +20,7 @@ module extern_classes import ffi_base redef class ToolContext - var extern_classes_typing_phase_ast: Phase = new ExternClassesTypingPhaseAst(self, [ffi_language_assignation_phase]) + var extern_classes_typing_phase_ast: Phase = new ExternClassesTypingPhaseAst(self, [ffi_language_assignation_phase, modelize_class_phase]) var extern_classes_typing_phase_model: Phase = new ExternClassesTypingPhaseModel(self, [extern_classes_typing_phase_ast, modelize_class_phase, modelize_property_phase]) diff --git a/src/ffi/ffi.nit b/src/ffi/ffi.nit index 019d201..b854750 100644 --- a/src/ffi/ffi.nit +++ b/src/ffi/ffi.nit @@ -57,7 +57,7 @@ redef class MModule # include dependancies FFI for mod in header_dependencies do - if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.name}._ffi.h\"\n") + if mod.uses_ffi then ffi_ccu.header_custom.add("#include \"{mod.c_name}._ffi.h\"\n") end ffi_ccu.write_as_impl(self, compdir) @@ -95,7 +95,7 @@ redef class AModule language.compile_module_block(block, ffi_ccu, mmodule) end - ffi_ccu.header_c_base.add( "#include \"{mmodule.name}._nitni.h\"\n" ) + ffi_ccu.header_c_base.add( "#include \"{mmodule.c_name}._nitni.h\"\n" ) ffi_ccu.body_decl.add("#ifdef ANDROID\n") ffi_ccu.body_decl.add(" #include \n") diff --git a/src/ffi/ffi_base.nit b/src/ffi/ffi_base.nit index e470276..babab08 100644 --- a/src/ffi/ffi_base.nit +++ b/src/ffi/ffi_base.nit @@ -152,10 +152,10 @@ end redef class CCompilationUnit fun write_as_impl(mmodule: MModule, compdir: String) do - var base_name = "{mmodule.name}._ffi" + var base_name = "{mmodule.c_name}._ffi" var h_file = "{base_name}.h" - var guard = "{mmodule.cname.to_s.to_upper}_NIT_H" + var guard = "{mmodule.c_name.to_upper}_NIT_H" write_header_to_file(mmodule, "{compdir}/{h_file}", new Array[String], guard) var c_file = "{base_name}.c" diff --git a/src/ffi/header_dependency.nit b/src/ffi/header_dependency.nit index 485ba1f..84bbb29 100644 --- a/src/ffi/header_dependency.nit +++ b/src/ffi/header_dependency.nit @@ -33,36 +33,32 @@ redef class AModule end redef class MModule - private var header_dependencies_cache: nullable Array[MModule] = null - fun header_dependencies: Array[MModule] - do - var cache = header_dependencies_cache - assert cache != null - return cache - end + # Modules with public foreign code blocks (C header) + var header_dependencies: nullable HashSet[MModule] = null private fun compute_header_dependencies(v: HeaderDependancyPhase) do - if header_dependencies_cache != null then return + if header_dependencies != null then return - var header_dependencies = new Array[MModule] + var header_dependencies = new HashSet[MModule] # gather from importation for m in in_importation.direct_greaters do - m.compute_header_dependencies(v) + m.compute_header_dependencies v - # does the super module has inherited dependancies? + # does the super module has inherited dependencies? var hd = m.header_dependencies + assert hd != null if not hd.is_empty then - header_dependencies.add_all(hd) + header_dependencies.add_all hd end - # does the super module itself has extern dependancies? - var amodule = v.toolcontext.modelbuilder.mmodule2nmodule[m] - if amodule.has_public_c_header then header_dependencies.add(m) + # does the super module itself has extern dependencies? + var amodule = v.toolcontext.modelbuilder.mmodule2node(m) + if amodule != null and amodule.has_public_c_header then header_dependencies.add(m) end - header_dependencies_cache = header_dependencies + self.header_dependencies = header_dependencies end end diff --git a/src/ffi/java.nit b/src/ffi/java.nit index 9191141..1fa56e9 100644 --- a/src/ffi/java.nit +++ b/src/ffi/java.nit @@ -518,6 +518,35 @@ redef class MClassType else break end + # Change `float[]` to `[float` + if jni_type.has('[') then + var depth = jni_type.chars.count('[') + var java_type = jni_type.replace("[]", "") + var short + + if java_type == "boolean" then + short = "Z" + else if java_type == "byte" then + short = "B" + else if java_type == "char" then + short = "C" + else if java_type == "short" then + short = "S" + else if java_type == "int" then + short = "I" + else if java_type == "long" then + short = "J" + else if java_type == "float" then + short = "F" + else if java_type == "double" then + short = "D" + else + short = "L{java_type};" + end + + return "["*depth + short + end + return "L{jni_type};" end if mclass.name == "Bool" then return "Z" @@ -530,6 +559,7 @@ redef class MClassType redef fun jni_signature_alt do var ftype = mclass.ftype + if ftype isa ForeignJavaType then return "Object" if mclass.name == "Bool" then return "Boolean" if mclass.name == "Char" then return "Char" diff --git a/src/ffi/objc.nit b/src/ffi/objc.nit index 9ab6f7f..f031289 100644 --- a/src/ffi/objc.nit +++ b/src/ffi/objc.nit @@ -32,6 +32,27 @@ end redef class MModule private var objc_file: nullable ObjCCompilationUnit = null + + private var has_public_objc_header = false + + # Imported modules with public Objective-C code blocks + var objc_imported_headers: HashSet[MModule] is lazy do + var dep = new HashSet[MModule] + + # gather from importation + for m in in_importation.direct_greaters do + # does the super module has inherited dependencies? + var import_dep = m.objc_imported_headers + if not import_dep.is_empty then + dep.add_all import_dep + end + + # does the super module itself has a public header? + if m.has_public_objc_header then dep.add(m) + end + + return dep + end end # The Objective-C langugage visitor @@ -47,6 +68,8 @@ class ObjCLanguage if block.is_objc_header then mmodule.objc_file.header_custom.add block.location.as_line_pragma mmodule.objc_file.header_custom.add block.code + + mmodule.has_public_objc_header = true else if block.is_objc_body then mmodule.objc_file.body_custom.add block.location.as_line_pragma mmodule.objc_file.body_custom.add block.code @@ -77,9 +100,15 @@ class ObjCLanguage var objc_file = mmodule.objc_file assert objc_file != null + # Import public Objective-C header of imported modules + var dep = mmodule.objc_imported_headers + for mod in dep do + objc_file.header_custom.add "#include \"{mod.c_name}._ffi_m.h\"\n" + end + # write .m and _m.h file mmodule.objc_file.header_c_types.add """ - #include "{{{mmodule.cname}}}._ffi.h" + #include "{{{mmodule.c_name}}}._ffi.h" """ var file = objc_file.write_to_files(mmodule, compdir) @@ -114,10 +143,10 @@ private class ObjCCompilationUnit # Write this compilation unit to Objective-C source files fun write_to_files(mmodule: MModule, compdir: String): ExternObjCFile do - var base_name = "{mmodule.cname}._ffi" + var base_name = "{mmodule.c_name}._ffi" var h_file = "{base_name}_m.h" - var guard = "{mmodule.cname.to_s.to_upper}_NIT_OBJC_H" + var guard = "{mmodule.c_name.to_upper}_NIT_OBJC_H" write_header_to_file(mmodule, compdir/h_file, new Array[String], guard) var c_file = "{base_name}.m" @@ -211,6 +240,13 @@ end private class FromObjCCallContext super ObjCCallContext + redef fun cast_to(mtype, name) + do + if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then + return "(__bridge void*)({name})" + else return name + end + redef fun cast_from(mtype, name) do if mtype isa MClassType and mtype.mclass.ftype isa ForeignObjCType then diff --git a/src/frontend/cached.nit b/src/frontend/cached.nit index 4af7e46..f331b1d 100644 --- a/src/frontend/cached.nit +++ b/src/frontend/cached.nit @@ -14,8 +14,7 @@ # Implementation of the method-related annotation `cached` # -# Note this module can be used as a reference on how to implements -# complex annotation that modify both the model and the AST of a Nit program +# The cached annotation is deprecated, use the `lazy` annotation instead. module cached import modelize @@ -25,6 +24,7 @@ private import annotation intrude import modelize::modelize_property redef class ToolContext + # Process the `cached` annotation on methods var cached_phase: Phase = new CachedPhase(self, [modelize_property_phase]) end diff --git a/src/frontend/check_annotation.nit b/src/frontend/check_annotation.nit index 7cd186e..fa909bc 100644 --- a/src/frontend/check_annotation.nit +++ b/src/frontend/check_annotation.nit @@ -22,6 +22,7 @@ import phase private import annotation redef class ToolContext + # Check for unknown annotation in each module var check_annotation_phase: Phase = new CheckAnnotationPhase(self, null) end @@ -88,6 +89,7 @@ old_style_init abstract intern extern +no_warning pkgconfig c_compiler_option diff --git a/src/frontend/frontend.nit b/src/frontend/frontend.nit index 53c1a77..5b04e01 100644 --- a/src/frontend/frontend.nit +++ b/src/frontend/frontend.nit @@ -15,6 +15,7 @@ # Collect and orchestration of main frontend phases module frontend +import no_warning import simple_misc_analysis import literal import modelize @@ -28,8 +29,12 @@ import glsl_validation redef class ToolContext # FIXME: there is conflict in linex in nitc, so use this trick to force invocation private var dummy: Bool = do_dummy - fun do_dummy: Bool + + # SEE `dummy` + private fun do_dummy: Bool do + # Force no warning before analysing classes + phases.add_edge(modelize_class_phase, no_warning_phase) # Force easy warnings after modelbuilder phases.add_edge(simple_misc_analysis_phase, modelize_property_phase) # Force easy warnings before intraproc-errors diff --git a/src/frontend/no_warning.nit b/src/frontend/no_warning.nit new file mode 100644 index 0000000..931440e --- /dev/null +++ b/src/frontend/no_warning.nit @@ -0,0 +1,82 @@ +# 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. + +# Fill toolcontext information about blacklisting of warnings. +module no_warning + +import modelbuilder +private import literal + +redef class ToolContext + # The phase should be executed before any warning on the module is processed. + var no_warning_phase: Phase = new NoWarningPhase(self, [literal_phase]) +end + +private class NoWarningPhase + super Phase + + redef fun process_nmodule(nmodule) + do + # Get the mmodule + var mmodule = nmodule.mmodule + assert mmodule != null + + var source = nmodule.location.file + + # If no decl block then quit + var nmoduledecl = nmodule.n_moduledecl + if nmoduledecl == null then + # Disable `missing-doc` if there is no `module` clause + # Rationale: the presence of a `module` clause is a good heuristic to + # discriminate quick and dirty prototypes from nice and clean modules + if source != null then toolcontext.warning_blacklist[source].add("missing-doc") + return + end + + var modelbuilder = toolcontext.modelbuilder + + # Disable `missing-doc` for `test_suite` + if source != null and not nmoduledecl.get_annotations("test_suite").is_empty then + toolcontext.warning_blacklist[source].add("missing-doc") + end + + # Get all the `no_warning` annotations + var name = "no_warning" + var annots = nmoduledecl.get_annotations(name) + + if annots.is_empty then return + + if source == null then + modelbuilder.warning(annots.first, "file-less-module", "Warning: annotation `{name}` does not currently work on file-less modules.") + return + end + + for annot in annots do + var args = annot.n_args + if args.is_empty then + modelbuilder.error(annot, "Annotation error: `{name}` needs a list of warnings. Use `\"all\"` to disable all warnings.") + continue + end + for arg in args do + var tag = arg.as_string + if tag == null then + modelbuilder.error(arg, "Annotation error: `{name}` expects String as arguments.") + continue + end + + toolcontext.warning_blacklist[source].add(tag) + end + end + end +end diff --git a/src/frontend/serialization_phase.nit b/src/frontend/serialization_phase.nit index a035c8e..78c8c89 100644 --- a/src/frontend/serialization_phase.nit +++ b/src/frontend/serialization_phase.nit @@ -24,7 +24,10 @@ import modelize private import annotation redef class ToolContext + # Generate serialization and deserialization methods on `auto_serializable` annotated classes. var serialization_phase_pre_model: Phase = new SerializationPhasePreModel(self, null) + + # The second phase of the serialization var serialization_phase_post_model: Phase = new SerializationPhasePostModel(self, [modelize_class_phase, serialization_phase_pre_model]) diff --git a/src/frontend/simple_misc_analysis.nit b/src/frontend/simple_misc_analysis.nit index c897ed9..cc12d01 100644 --- a/src/frontend/simple_misc_analysis.nit +++ b/src/frontend/simple_misc_analysis.nit @@ -24,6 +24,7 @@ module simple_misc_analysis import phase redef class ToolContext + # Execute `AModule::do_simple_misc_analysis` on each module. var simple_misc_analysis_phase: Phase = new SimpleMiscAnalysisPhase(self, null) end diff --git a/src/highlight.nit b/src/highlight.nit index d70281e..ecc4c7d 100644 --- a/src/highlight.nit +++ b/src/highlight.nit @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Highliting of Nit AST +# Highlighting of Nit AST module highlight import frontend @@ -48,6 +48,8 @@ class HighlightVisitor html.add_class("nitcode") end + # The entry-point of the highlighting. + # Will fill `html` with the generated HTML content. fun enter_visit(n: ANode) do n.parentize_tokens @@ -329,6 +331,7 @@ redef class MModule return res end + # The module HTML page fun href: String do return name + ".html" @@ -386,6 +389,7 @@ redef class MClassDef return res end + # The class HTML page (an anchor in the module page) fun href: String do return mmodule.href + "#" + to_s @@ -432,6 +436,7 @@ redef class MPropDef return res end + # The property HTML page (an anchor in the module page) fun href: String do return self.mclassdef.mmodule.href + "#" + self.to_s diff --git a/src/interpreter/debugger_socket.nit b/src/interpreter/debugger_socket.nit index b3bac6b..50f98c7 100644 --- a/src/interpreter/debugger_socket.nit +++ b/src/interpreter/debugger_socket.nit @@ -90,20 +90,23 @@ redef class ModelBuilder fun set_stdstreams do if self.toolcontext.opt_socket_mode.value then - var sock = new Socket.server(toolcontext.opt_debug_port.value, 1) + var sock = new TCPServer(toolcontext.opt_debug_port.value) + sock.listen 1 var ns = sock.accept + assert ns != null sock.close sys.set_io(ns,ns,ns) else if self.toolcontext.opt_websocket_mode.value then - var websock = new WebSocket(toolcontext.opt_debug_port.value, 1) - websock.accept - sys.set_io(websock,websock,websock) + var websock = new WebSocketListener(toolcontext.opt_debug_port.value, 1) + var cli = websock.accept + websock.close + sys.set_io(cli,cli,cli) end end fun close_stdstreams do - if sys.stdin isa WebSocket or sys.stdin isa Socket then + if sys.stdin isa TCPStream then sys.stdin.close sys.stdout.close sys.stderr.close @@ -116,6 +119,6 @@ redef class Sys do self.stdin = istream self.stdout = ostream - self.stderr = ostream + self.stderr = errstream end end diff --git a/src/interpreter/naive_interpreter.nit b/src/interpreter/naive_interpreter.nit index 4bbe635..5cc3892 100644 --- a/src/interpreter/naive_interpreter.nit +++ b/src/interpreter/naive_interpreter.nit @@ -458,7 +458,7 @@ class NaiveInterpreter fun send_commons(mproperty: MMethod, args: Array[Instance], mtype: MType): nullable Instance do if mtype isa MNullType then - if mproperty.name == "==" then + if mproperty.name == "==" or mproperty.name == "is_same_instance" then return self.bool_instance(args[0] == args[1]) else if mproperty.name == "!=" then return self.bool_instance(args[0] != args[1]) diff --git a/src/interpreter/primitive_types.nit b/src/interpreter/primitive_types.nit index 127815b..0de93d3 100644 --- a/src/interpreter/primitive_types.nit +++ b/src/interpreter/primitive_types.nit @@ -15,22 +15,23 @@ module primitive_types intrude import standard::file +intrude import standard::string # Wrapper for `NativeFile` class PrimitiveNativeFile - var file: FStream + var file: IOS init native_stdin do - file = new IFStream.from_fd(0) + file = sys.stdin end init native_stdout do - file = new OFStream.from_fd(1) + file = sys.stdout end init native_stderr do - file = new OFStream.from_fd(2) + file = sys.stderr end init io_open_read(path: String) do @@ -41,19 +42,42 @@ class PrimitiveNativeFile file = new OFStream.open(path.to_s) end - fun address_is_null: Bool do return file._file.address_is_null + fun address_is_null: Bool do + if file isa FStream then return file.as(FStream)._file.address_is_null + return false + end - fun io_read(buf: NativeString, len: Int): Int do return file._file.io_read(buf, len) + fun io_read(buf: NativeString, len: Int): Int do + if file isa FStream then return file.as(FStream)._file.io_read(buf, len) + var str = file.as(IStream).read(len) + str.to_cstring.copy_to(buf, str.length, 0, 0) + return str.length + end - fun io_write(buf: NativeString, len: Int): Int do return file._file.io_write(buf, len) + fun io_write(buf: NativeString, len: Int): Int do + if file isa FStream then return file.as(FStream)._file.io_write(buf, len) + file.as(OStream).write(buf.to_s_with_length(len)) + return len + end - fun io_close: Int do return file._file.io_close + fun io_close: Int do + if file isa FStream then return file.as(FStream)._file.io_close + file.close + return 0 + end - fun fileno: Int do return file._file.fileno + fun fileno: Int do + if file isa FStream then return file.as(FStream)._file.fileno + return 0 + end - fun flush: Int do return file._file.flush + fun flush: Int do + if file isa FStream then return file.as(FStream)._file.flush + return 0 + end fun set_buffering_type(size, mode: Int): Int do - return file._file.set_buffering_type(size, mode) + if file isa FStream then return file.as(FStream)._file.set_buffering_type(size, mode) + return 0 end end diff --git a/src/literal.nit b/src/literal.nit index e39409f..bc361ab 100644 --- a/src/literal.nit +++ b/src/literal.nit @@ -20,6 +20,7 @@ module literal import phase redef class ToolContext + # Parses literal values in the whole AST and produces errors if needed var literal_phase: Phase = new LiteralPhase(self, null) end @@ -55,6 +56,38 @@ redef class ANode private fun accept_literal(v: LiteralVisitor) do end end +redef class AExpr + # Get `self` as a `String`. + # Return null if not a string. + fun as_string: nullable String + do + if not self isa AStringFormExpr then return null + return self.value.as(not null) + end + + # Get `self` as an `Int`. + # Return null if not an integer. + fun as_int: nullable Int + do + if not self isa AIntExpr then return null + return self.value.as(not null) + end + + # Get `self` as a single identifier. + # Return null if not a single identifier. + fun as_id: nullable String + do + if self isa AMethidExpr then + return self.collect_text + end + if not self isa ACallExpr then return null + if not self.n_expr isa AImplicitSelfExpr then return null + if not self.n_args.n_exprs.is_empty then return null + return self.n_id.text + end +end + + redef class AIntExpr # The value of the literal int once computed. var value: nullable Int diff --git a/src/loader.nit b/src/loader.nit index 14ec4fe..a046815 100644 --- a/src/loader.nit +++ b/src/loader.nit @@ -88,6 +88,61 @@ redef class ModelBuilder return mmodules.to_a end + # Load recursively all modules of the group `mgroup`. + # See `parse` for details. + fun parse_group(mgroup: MGroup): Array[MModule] + do + var res = new Array[MModule] + visit_group(mgroup) + for mg in mgroup.in_nesting.smallers do + for mp in mg.module_paths do + var nmodule = self.load_module(mp.filepath) + if nmodule == null then continue # Skip error + # Load imported module + build_module_importation(nmodule) + + res.add(nmodule.mmodule.as(not null)) + end + end + return res + end + + # Load a bunch of modules and groups. + # Each name can be a module or a group. + # If it is a group then recursively all its modules are parsed. + # See `parse` for details. + fun parse_full(names: Sequence[String]): Array[MModule] + do + var time0 = get_time + # Parse and recursively load + self.toolcontext.info("*** PARSE ***", 1) + var mmodules = new ArraySet[MModule] + for a in names do + var mgroup = self.get_mgroup(a) + if mgroup != null then + mmodules.add_all parse_group(mgroup) + continue + end + var nmodule = self.load_module(a) + if nmodule == null then continue # Skip error + # Load imported module + build_module_importation(nmodule) + + mmodules.add(nmodule.mmodule.as(not null)) + end + var time1 = get_time + self.toolcontext.info("*** END PARSE: {time1-time0} ***", 2) + + self.toolcontext.check_errors + + if toolcontext.opt_only_parse.value then + self.toolcontext.info("*** ONLY PARSE...", 1) + exit(0) + end + + return mmodules.to_a + end + # The list of directories to search for top level modules # The list is initially set with: # @@ -446,15 +501,26 @@ redef class ModelBuilder do # Check the module name var decl = nmodule.n_moduledecl - if decl == null then - #warning(nmodule, "Warning: Missing 'module' keyword") #FIXME: NOT YET FOR COMPATIBILITY - else + if decl != null then var decl_name = decl.n_name.n_id.text if decl_name != mod_name then error(decl.n_name, "Error: module name missmatch; declared {decl_name} file named {mod_name}") end end + # Check for conflicting module names in the project + if mgroup != null then + var others = model.get_mmodules_by_name(mod_name) + if others != null then for other in others do + if other.mgroup!= null and other.mgroup.mproject == mgroup.mproject then + var node: ANode + if decl == null then node = nmodule else node = decl.n_name + error(node, "Error: A module named `{other.full_name}` already exists at {other.location}") + break + end + end + end + # Create the module var mmodule = new MModule(model, mgroup, mod_name, nmodule.location) nmodule.mmodule = mmodule @@ -462,6 +528,7 @@ redef class ModelBuilder self.mmodule2nmodule[mmodule] = nmodule if decl != null then + # Extract documentation var ndoc = decl.n_doc if ndoc != null then var mdoc = ndoc.to_mdoc @@ -470,6 +537,8 @@ redef class ModelBuilder else advice(decl, "missing-doc", "Documentation warning: Undocumented module `{mmodule}`") end + # Is the module a test suite? + mmodule.is_test_suite = not decl.get_annotations("test_suite").is_empty end return mmodule @@ -551,8 +620,18 @@ redef class ModelBuilder var nmodules = new Array[AModule] # Register the nmodule associated to each mmodule - # FIXME: why not refine the `MModule` class with a nullable attribute? - var mmodule2nmodule = new HashMap[MModule, AModule] + # + # Public clients need to use `mmodule2node` to access stuff. + private var mmodule2nmodule = new HashMap[MModule, AModule] + + # Retrieve the associated AST node of a mmodule. + # This method is used to associate model entity with syntactic entities. + # + # If the module is not associated with a node, returns null. + fun mmodule2node(mmodule: MModule): nullable AModule + do + return mmodule2nmodule.get_or_null(mmodule) + end end # File-system location of a module (file) that is identified but not always loaded. diff --git a/src/location.nit b/src/location.nit index e82775a..d7154ca 100644 --- a/src/location.nit +++ b/src/location.nit @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This module is used to model Nit source-file and locations in source-file +# Nit source-file and locations in source-file module location # A raw text Nit source file @@ -51,8 +51,13 @@ class Location super Comparable redef type OTHER: Location + # The associated source-file var file: nullable SourceFile + + # The starting line number (starting from 1) var line_start: Int + + # The stopping line number (starting from 1) var line_end: Int # Start of this location on `line_start` @@ -64,6 +69,7 @@ class Location # Require: `column_start >= 0` var column_start: Int + # End of this location on `line_end` var column_end: Int # The index in the start character in the source @@ -87,8 +93,6 @@ class Location private var text_cache: nullable String = null - init with_file(f: SourceFile) do init(f,0,0,0,0) - redef fun ==(other: nullable Object): Bool do if other == null then return false if not other isa Location then return false diff --git a/src/model/mmodule.nit b/src/model/mmodule.nit index 3e3c497..d88a0ba 100644 --- a/src/model/mmodule.nit +++ b/src/model/mmodule.nit @@ -76,6 +76,14 @@ class MModule # The group of module in the project if any var mgroup: nullable MGroup + # The project of the module if any + # Safe alias for `mgroup.mproject` + fun mproject: nullable MProject + do + var g = mgroup + if g == null then return null else return g.mproject + end + # The short name of the module redef var name: String @@ -104,6 +112,22 @@ class MModule end end + # The namespace used for entities according to their visibility `v`. + # + # Public entities use only the project as a namespace. + # Private entities use the `full_name` (i.e. "project::module") + # + # This method is used by entities to implement their `full_name`. + fun namespace_for(v: MVisibility): String do + if v <= private_visibility then return full_name + var mgroup = self.mgroup + if mgroup == null then + return full_name + else + return mgroup.mproject.full_name + end + end + # Return the name of the global C identifier associated to `self`. # This name is used to prefix files and other C identifiers associated with `self`. redef var c_name: String is lazy do @@ -117,6 +141,19 @@ class MModule return res end + # C identifier version of `namespace_for`. + # See `c_name` + # + # This method is used by entities to implement their `c_name`. + fun c_namespace_for(v: MVisibility): String do + if v <= private_visibility then return c_name + var mgroup = self.mgroup + if mgroup == null then + return c_name + else + return mgroup.mproject.c_name + end + end # Create a new empty module and register it to a model init @@ -129,12 +166,6 @@ class MModule assert mgroup.default_mmodule == null mgroup.default_mmodule = self end - # placebo for old module nesting hierarchy - var direct_owner = mgroup.default_mmodule - if direct_owner == self then - # The potential owner is the default_mmodule of the parent group - if mgroup.parent != null then direct_owner = mgroup.parent.default_mmodule - end end self.in_importation = model.mmodule_importation_hierarchy.add_node(self) end @@ -209,5 +240,8 @@ class MModule # exposed to the final user. var is_fictive: Bool = false is writable + # Is `self` a unit test module used by `nitunit`? + var is_test_suite: Bool = false is writable + redef fun parent_concern do return mgroup end diff --git a/src/model/model.nit b/src/model/model.nit index 8eabb75..ff12710 100644 --- a/src/model/model.nit +++ b/src/model/model.nit @@ -361,9 +361,13 @@ class MClass # # It is the name of the class prefixed by the full_name of the `intro_mmodule` # Example: `"owner::module::MyClass"` - redef var full_name is lazy do return "{self.intro_mmodule.full_name}::{name}" + redef var full_name is lazy do + return "{self.intro_mmodule.namespace_for(visibility)}::{name}" + end - redef var c_name is lazy do return "{intro_mmodule.c_name}__{name.to_cmangle}" + redef var c_name is lazy do + return "{intro_mmodule.c_namespace_for(visibility)}__{name.to_cmangle}" + end # The number of generic formal parameters # 0 if the class is not generic @@ -373,6 +377,7 @@ class MClass # is empty if the class is not generic var mparameters = new Array[MParameterType] + # Initialize `mparameters` from their names. protected fun setup_parameter_names(parameter_names: nullable Array[String]) is autoinit do @@ -538,17 +543,29 @@ class MClassDef # Example: "my_module#intro_module::MyClass" redef var full_name is lazy do if is_intro then + # public gives 'p#A' + # private gives 'p::m#A' + return "{mmodule.namespace_for(mclass.visibility)}#{mclass.name}" + else if mclass.intro_mmodule.mproject != mmodule.mproject then + # public gives 'q::n#p::A' + # private gives 'q::n#p::m::A' + return "{mmodule.full_name}#{mclass.full_name}" + else if mclass.visibility > private_visibility then + # public gives 'p::n#A' return "{mmodule.full_name}#{mclass.name}" else - return "{mmodule.full_name}#{mclass.full_name}" + # private gives 'p::n#::m::A' (redundant p is omitted) + return "{mmodule.full_name}#::{mclass.intro_mmodule.name}::{mclass.name}" end end redef var c_name is lazy do if is_intro then - return mclass.c_name + return "{mmodule.c_namespace_for(mclass.visibility)}___{mclass.c_name}" + else if mclass.intro_mmodule.mproject == mmodule.mproject and mclass.visibility > private_visibility then + return "{mmodule.c_name}___{mclass.name.to_cmangle}" else - return "{mmodule.c_name}__{mclass.c_name.to_cmangle}" + return "{mmodule.c_name}___{mclass.c_name}" end end @@ -1693,6 +1710,8 @@ class MParameter end end + # Returns a new parameter with the `mtype` resolved. + # See `MType::resolve_for` for details. fun resolve_for(mtype: MType, anchor: nullable MClassType, mmodule: MModule, cleanup_virtual: Bool): MParameter do if not self.mtype.need_anchor then return self @@ -1735,11 +1754,12 @@ abstract class MProperty # It is the short-`name` prefixed by the short-name of the class and the full-name of the module. # Example: "my_project::my_module::MyClass::my_method" redef var full_name is lazy do - return "{intro_mclassdef.mmodule.full_name}::{intro_mclassdef.mclass.name}::{name}" + return "{intro_mclassdef.mmodule.namespace_for(visibility)}::{intro_mclassdef.mclass.name}::{name}" end redef var c_name is lazy do - return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.c_name}__{name.to_cmangle}" + # FIXME use `namespace_for` + return "{intro_mclassdef.mmodule.c_name}__{intro_mclassdef.mclass.name.to_cmangle}__{name.to_cmangle}" end # The visibility of the property @@ -2015,42 +2035,66 @@ abstract class MPropDef # Therefore the combination of identifiers is awful, # the worst case being # - # ~~~nitish - # "{mclassdef.mmodule.full_name}#{mclassdef.mclass.intro_mmodule.full_name}::{mclassdef.name}#{mproperty.intro_mclassdef.mmodule.full_name}::{mproperty.intro_mclassdef.name}::{name}" - # ~~~ + # * a property "p::m::A::x" + # * redefined in a refinement of a class "q::n::B" + # * in a module "r::o" + # * so "r::o#q::n::B#p::m::A::x" # # Fortunately, the full-name is simplified when entities are repeated. - # The simplest form is "my_module#MyClass#my_property". + # For the previous case, the simplest form is "p#A#x". redef var full_name is lazy do var res = new FlatBuffer - res.append mclassdef.mmodule.full_name - res.append "#" - if not mclassdef.is_intro then - res.append mclassdef.mclass.intro_mmodule.full_name - res.append "::" - end - res.append mclassdef.name + + # The first part is the mclassdef. Worst case is "r::o#q::n::B" + res.append mclassdef.full_name + res.append "#" - if mproperty.intro_mclassdef.mmodule != mclassdef.mmodule then - res.append mproperty.intro_mclassdef.mmodule.full_name - res.append "::" - end - if mclassdef.mclass != mproperty.intro_mclassdef.mclass then - res.append mproperty.intro_mclassdef.mclass.name - res.append "::" + + if mclassdef.mclass == mproperty.intro_mclassdef.mclass then + # intro are unambiguous in a class + res.append name + else + # Just try to simplify each part + if mclassdef.mmodule.mproject != mproperty.intro_mclassdef.mmodule.mproject then + # precise "p::m" only if "p" != "r" + res.append mproperty.intro_mclassdef.mmodule.full_name + res.append "::" + else if mproperty.visibility <= private_visibility then + # Same project ("p"=="q"), but private visibility, + # does the module part ("::m") need to be displayed + if mclassdef.mmodule.namespace_for(mclassdef.mclass.visibility) != mproperty.intro_mclassdef.mmodule.mproject then + res.append "::" + res.append mproperty.intro_mclassdef.mmodule.name + res.append "::" + end + end + if mclassdef.mclass != mproperty.intro_mclassdef.mclass then + # precise "B" only if not the same class than "A" + res.append mproperty.intro_mclassdef.name + res.append "::" + end + # Always use the property name "x" + res.append mproperty.name end - res.append name return res.to_s end redef var c_name is lazy do var res = new FlatBuffer res.append mclassdef.c_name - res.append "__" - if is_intro then + res.append "___" + if mclassdef.mclass == mproperty.intro_mclassdef.mclass then res.append name.to_cmangle else - res.append mproperty.c_name.to_cmangle + if mclassdef.mmodule != mproperty.intro_mclassdef.mmodule then + res.append mproperty.intro_mclassdef.mmodule.c_name + res.append "__" + end + if mclassdef.mclass != mproperty.intro_mclassdef.mclass then + res.append mproperty.intro_mclassdef.name.to_cmangle + res.append "__" + end + res.append mproperty.name.to_cmangle end return res.to_s end diff --git a/src/model/mproject.nit b/src/model/mproject.nit index 9002009..283d54c 100644 --- a/src/model/mproject.nit +++ b/src/model/mproject.nit @@ -27,6 +27,10 @@ class MProject # The name of the project redef var name: String + redef fun full_name do return name + + redef var c_name = name.to_cmangle is lazy + # The model of the project redef var model: Model diff --git a/src/modelbuilder.nit b/src/modelbuilder.nit index b38bdac..30f5885 100644 --- a/src/modelbuilder.nit +++ b/src/modelbuilder.nit @@ -86,7 +86,7 @@ redef class ModelBuilder model.mmodule_importation_hierarchy.sort(mmodules) var nmodules = new Array[AModule] for mm in mmodules do - nmodules.add(mmodule2nmodule[mm]) + nmodules.add(mmodule2node(mm).as(not null)) end toolcontext.run_phases(nmodules) diff --git a/src/modelbuilder_base.nit b/src/modelbuilder_base.nit index d09b717..1f8c184 100644 --- a/src/modelbuilder_base.nit +++ b/src/modelbuilder_base.nit @@ -234,6 +234,8 @@ end redef class ADoc private var mdoc_cache: nullable MDoc + + # Convert `self` to a `MDoc` fun to_mdoc: MDoc do var res = mdoc_cache diff --git a/src/modelize/modelize_class.nit b/src/modelize/modelize_class.nit index ae9da02..8a49296 100644 --- a/src/modelize/modelize_class.nit +++ b/src/modelize/modelize_class.nit @@ -20,6 +20,7 @@ module modelize_class import modelbuilder redef class ToolContext + # Run `AModule::build_classes` on each module var modelize_class_phase: Phase = new ModelizeClassPhase(self, null) end @@ -96,6 +97,18 @@ redef class ModelBuilder error(nclassdef, "Redef error: No imported class {name} to refine.") return end + + # Check for conflicting class full-names in the project + if mmodule.mgroup != null and mvisibility >= protected_visibility then + var mclasses = model.get_mclasses_by_name(name) + if mclasses != null then for other in mclasses do + if other.intro_mmodule.mgroup != null and other.intro_mmodule.mgroup.mproject == mmodule.mgroup.mproject then + error(nclassdef, "Error: A class named `{other.full_name}` is already defined in module `{other.intro_mmodule}` at {other.intro.location}.") + break + end + end + end + mclass = new MClass(mmodule, name, names, mkind, mvisibility) #print "new class {mclass}" else if nclassdef isa AStdClassdef and nmodule.mclass2nclassdef.has_key(mclass) then @@ -282,9 +295,8 @@ redef class ModelBuilder nmodule.build_classes_is_done = true var mmodule = nmodule.mmodule.as(not null) for imp in mmodule.in_importation.direct_greaters do - - if not mmodule2nmodule.has_key(imp) then continue - build_classes(mmodule2nmodule[imp]) + var nimp = mmodule2node(imp) + if nimp != null then build_classes(nimp) end if errcount != toolcontext.error_count then return diff --git a/src/modelize/modelize_property.nit b/src/modelize/modelize_property.nit index fef5ca9..d8fd96d 100644 --- a/src/modelize/modelize_property.nit +++ b/src/modelize/modelize_property.nit @@ -21,6 +21,7 @@ intrude import modelize_class private import annotation redef class ToolContext + # Run `AClassdef::build_property` on the classdefs of each module var modelize_property_phase: Phase = new ModelizePropertyPhase(self, [modelize_class_phase]) end @@ -44,11 +45,16 @@ redef class ModelBuilder # Retrieve the associated AST node of a mpropertydef. # This method is used to associate model entity with syntactic entities. # - # If the property definition is not associated with a node, returns node. + # If the property definition is not associated with a node, returns `null`. fun mpropdef2node(mpropdef: MPropDef): nullable ANode do - var res: nullable ANode = mpropdef2npropdef.get_or_null(mpropdef) - if res != null then return res + var res + res = mpropdef2npropdef.get_or_null(mpropdef) + if res != null then + # Run the phases on it + toolcontext.run_phases_on_npropdef(res) + return res + end if mpropdef isa MMethodDef and mpropdef.mproperty.is_root_init then res = mclassdef2nclassdef.get_or_null(mpropdef.mclassdef) if res != null then return res @@ -65,6 +71,8 @@ redef class ModelBuilder if n == null then return res for npropdef in n.n_propdefs do if npropdef isa AAttrPropdef then + # Run the phases on it + toolcontext.run_phases_on_npropdef(npropdef) res.add(npropdef) end end @@ -324,7 +332,8 @@ redef class MPropDef end redef class AClassdef - var build_properties_is_done = false + # Marker used in `ModelBuilder::build_properties` + private var build_properties_is_done = false # The free init (implicitely constructed by the class if required) var mfree_init: nullable MMethodDef = null @@ -476,6 +485,18 @@ redef class APropdef modelbuilder.error(self, "Redef error: {mclassdef.mclass}::{mprop.name} is an inherited property. To redefine it, add the redef keyword.") return false end + + # Check for full-name conflicts in the project. + # A public property should have a unique qualified name `project::class::prop`. + if mprop.intro_mclassdef.mmodule.mgroup != null and mprop.visibility >= protected_visibility then + var others = modelbuilder.model.get_mproperties_by_name(mprop.name) + if others != null then for other in others do + if other != mprop and other.intro_mclassdef.mmodule.mgroup != null and other.intro_mclassdef.mmodule.mgroup.mproject == mprop.intro_mclassdef.mmodule.mgroup.mproject and other.intro_mclassdef.mclass.name == mprop.intro_mclassdef.mclass.name and other.visibility >= protected_visibility then + modelbuilder.advice(self, "full-name-conflict", "Warning: A property named `{other.full_name}` is already defined in module `{other.intro_mclassdef.mmodule}` for the class `{other.intro_mclassdef.mclass.name}`.") + break + end + end + end else if not need_redef then modelbuilder.error(self, "Error: No property {mclassdef.mclass}::{mprop.name} is inherited. Remove the redef keyword to define a new property.") diff --git a/src/nit.nit b/src/nit.nit index 5e8697d..5a74d48 100644 --- a/src/nit.nit +++ b/src/nit.nit @@ -20,6 +20,7 @@ module nit import interpreter import frontend import parser_util +import vm # Create a tool context to handle options and paths var toolcontext = new ToolContext @@ -30,7 +31,8 @@ var opt = new OptionString("compatibility (does noting)", "-o") toolcontext.option_context.add_option(opt) var opt_eval = new OptionBool("Specifies the program from command-line", "-e") var opt_loop = new OptionBool("Repeatedly run the program for each line in file-name arguments", "-n") -toolcontext.option_context.add_option(opt_eval, opt_loop) +var opt_vm = new OptionBool("Run the virtual machine instead of the naive interpreter (experimental)", "--vm") +toolcontext.option_context.add_option(opt_eval, opt_loop, opt_vm) # We do not add other options, so process them now! toolcontext.process_options(args) @@ -78,6 +80,8 @@ if toolcontext.opt_debugger_autorun.value then modelbuilder.run_debugger_autorun(self_mm, self_args) else if toolcontext.opt_debugger_mode.value then modelbuilder.run_debugger(self_mm, self_args) +else if opt_vm.value then + modelbuilder.run_virtual_machine(self_mm, self_args) else modelbuilder.run_naive_interpreter(self_mm, self_args) end diff --git a/src/nitdbg_client.nit b/src/nitdbg_client.nit index 8983b61..349f53c 100644 --- a/src/nitdbg_client.nit +++ b/src/nitdbg_client.nit @@ -65,19 +65,19 @@ if toolcontext.opt_debug_port.value < 0 or toolcontext.opt_debug_port.value > 65 return end -var debug: Socket +var debug: TCPStream # An IPV4 address does always complies to this form : x.x.x.x # Where x is an integer whose value is >=0 and <= 255 if toolcontext.opt_host_address.value != null then if toolcontext.opt_host_address.value.is_valid_ipv4_address then - debug = new Socket.client(toolcontext.opt_host_address.value.as(not null), toolcontext.opt_debug_port.value) + debug = new TCPStream.connect(toolcontext.opt_host_address.value.as(not null), toolcontext.opt_debug_port.value) else toolcontext.option_context.usage return end else - debug = new Socket.client("127.0.0.1", toolcontext.opt_debug_port.value) + debug = new TCPStream.connect("127.0.0.1", toolcontext.opt_debug_port.value) end print "[HOST ADDRESS] : {debug.address}" diff --git a/src/nitdoc.nit b/src/nitdoc.nit index 02f3bb1..c1a4851 100644 --- a/src/nitdoc.nit +++ b/src/nitdoc.nit @@ -41,7 +41,7 @@ var arguments = toolcontext.option_context.rest # build model var model = new Model var mbuilder = new ModelBuilder(model, toolcontext) -var mmodules = mbuilder.parse(arguments) +var mmodules = mbuilder.parse_full(arguments) if mmodules.is_empty then return mbuilder.run_phases diff --git a/src/nitlight.nit b/src/nitlight.nit index cc35b10..0c8998b 100644 --- a/src/nitlight.nit +++ b/src/nitlight.nit @@ -41,7 +41,7 @@ var modelbuilder = new ModelBuilder(model, toolcontext) var args = toolcontext.option_context.rest -var mmodules = modelbuilder.parse(args) +var mmodules = modelbuilder.parse_full(args) modelbuilder.run_phases if opt_full.value then mmodules = model.mmodules @@ -67,7 +67,8 @@ for mm in mmodules do if opt_last_line.value != 0 then v.last_line = opt_last_line.value if opt_ast.value then v.with_ast = true var page = null - var m = modelbuilder.mmodule2nmodule[mm] + var m = modelbuilder.mmodule2node(mm) + assert m != null if not opt_fragment.value then page = new HTMLTag("html") page.add_raw_html """ diff --git a/src/nitmetrics.nit b/src/nitmetrics.nit index ee8b827..3925360 100644 --- a/src/nitmetrics.nit +++ b/src/nitmetrics.nit @@ -36,7 +36,7 @@ var model = new Model var modelbuilder = new ModelBuilder(model, toolcontext) # Here we load an process all modules passed on the command line -var mmodules = modelbuilder.parse(arguments) +var mmodules = modelbuilder.parse_full(arguments) modelbuilder.run_phases print "*** METRICS ***" diff --git a/src/nitni/nitni_base.nit b/src/nitni/nitni_base.nit index 4f31aaf..6685029 100644 --- a/src/nitni/nitni_base.nit +++ b/src/nitni/nitni_base.nit @@ -22,7 +22,6 @@ module nitni_base import parser import modelbuilder # builder only for externcalls -private import compiler::abstract_compiler redef class MMethod # Short name of this method in C (without the class name) @@ -52,12 +51,6 @@ redef class MMethod end end -redef class MModule - # Mangled name of this module in C - fun cname: String do return c_name # FIXME this is a hack to keep the internal FFI - # API independent of the compilers while still using the `MModule::c_name` service. -end - redef class MMethodDef # Name of the function to callback this method from C, # also used in other functions names used for this method. diff --git a/src/nitpick.nit b/src/nitpick.nit index c7664a9..57bbce3 100644 --- a/src/nitpick.nit +++ b/src/nitpick.nit @@ -46,7 +46,7 @@ var model = new Model var modelbuilder = new ModelBuilder(model, toolcontext) # Here we load an process all modules passed on the command line -var mmodules = modelbuilder.parse(arguments) +var mmodules = modelbuilder.parse_full(arguments) toolcontext.mmodules_to_check.add_all mmodules modelbuilder.run_phases diff --git a/src/nitpretty.nit b/src/nitpretty.nit index 7ff0b16..9dd723e 100644 --- a/src/nitpretty.nit +++ b/src/nitpretty.nit @@ -32,6 +32,20 @@ redef class ToolContext var opt_meld = new OptionBool("Show diff between source and output using meld", "--meld") + # Break too long string literals. + var opt_break_str = new OptionBool("Break too long string literals", "--break-strings") + + # Force `do` on the same line as the method signature. + var opt_inline_do = new OptionBool("Force do keyword on the same line as the method signature", + "--inline-do") + + # Force formatting on empty lines. + # + # By default empty lines are kept as they were typed in the file. + # When enabling this option, `nitpretty` will decide where to break lines + # and will put empty lines to separate properties and code blocks. + var opt_skip_empty = new OptionBool("Force formatting of empty lines", "--skip-empty") + # Check formatting instead of pretty printing. # # This option create a tempory pretty printed file then check if @@ -52,9 +66,11 @@ end # process options var toolcontext = new ToolContext -toolcontext.option_context. - add_option(toolcontext.opt_dir, toolcontext.opt_output, toolcontext.opt_diff, - toolcontext.opt_meld, toolcontext.opt_check) +var opts = toolcontext.option_context +opts.add_option(toolcontext.opt_dir, toolcontext.opt_output) +opts.add_option(toolcontext.opt_diff, toolcontext.opt_meld, toolcontext.opt_check) +opts.add_option(toolcontext.opt_break_str, toolcontext.opt_inline_do) +opts.add_option(toolcontext.opt_skip_empty) toolcontext.tooldescription = "Usage: nitpretty [OPTION]... \n" + "Pretty print Nit code from Nit source files." @@ -81,13 +97,22 @@ var dir = toolcontext.opt_dir.value or else ".nitpretty" if not dir.file_exists then dir.mkdir var v = new PrettyPrinterVisitor +if toolcontext.opt_break_str.value then + v.break_strings = true +end +if toolcontext.opt_inline_do.value then + v.inline_do = true +end +if toolcontext.opt_skip_empty.value then + v.skip_empty = true +end + for mmodule in mmodules do - if not mbuilder.mmodule2nmodule.has_key(mmodule) then + var nmodule = mbuilder.mmodule2node(mmodule) + if nmodule == null then print " Error: no source file for module {mmodule}" return end - - var nmodule = mbuilder.mmodule2nmodule[mmodule] var file = "{dir}/{mmodule.name}.nit" var tpl = v.pretty_nmodule(nmodule) tpl.write_to_file file diff --git a/src/nitserial.nit b/src/nitserial.nit index 2fd0f82..4953156 100644 --- a/src/nitserial.nit +++ b/src/nitserial.nit @@ -127,7 +127,7 @@ end var model = new Model var modelbuilder = new ModelBuilder(model, toolcontext) -var mmodules = modelbuilder.parse(arguments) +var mmodules = modelbuilder.parse_full(arguments) modelbuilder.run_phases # Create a distinct support module per targetted modules diff --git a/src/nituml.nit b/src/nituml.nit index 04cb4b3..9ea9048 100644 --- a/src/nituml.nit +++ b/src/nituml.nit @@ -51,7 +51,7 @@ var arguments = toolcontext.option_context.rest # build model var model = new Model var mbuilder = new ModelBuilder(model, toolcontext) -var mmodules = mbuilder.parse(arguments) +var mmodules = mbuilder.parse_full(arguments) if mmodules.is_empty then return mbuilder.run_phases diff --git a/src/nitunit.nit b/src/nitunit.nit index c057beb..7969f29 100644 --- a/src/nitunit.nit +++ b/src/nitunit.nit @@ -53,7 +53,7 @@ end var model = new Model var modelbuilder = new ModelBuilder(model, toolcontext) -var mmodules = modelbuilder.parse(args) +var mmodules = modelbuilder.parse_full(args) modelbuilder.run_phases if toolcontext.opt_gen_unit.value then diff --git a/src/nitvm.nit b/src/nitvm.nit index fe12c15..088cd92 100644 --- a/src/nitvm.nit +++ b/src/nitvm.nit @@ -60,4 +60,4 @@ end var self_mm = mainmodule var self_args = arguments -modelbuilder.run_naive_interpreter(self_mm, self_args) +modelbuilder.run_virtual_machine(self_mm, self_args) diff --git a/src/parser/lexer.nit b/src/parser/lexer.nit index e7d48a0..2335cd3 100644 --- a/src/parser/lexer.nit +++ b/src/parser/lexer.nit @@ -1,6 +1,6 @@ # Lexer and its tokens. # This file was generated by SableCC (http://www.sablecc.org/). -module lexer +module lexer is no_warning("missing-doc") intrude import parser_nodes intrude import lexer_work diff --git a/src/parser/parser.nit b/src/parser/parser.nit index d9b49c2..00a3106 100644 --- a/src/parser/parser.nit +++ b/src/parser/parser.nit @@ -1,6 +1,6 @@ # Parser. # This file was generated by SableCC (http://www.sablecc.org/). -module parser +module parser is no_warning("missing-doc", "unread-variable") intrude import parser_prod intrude import parser_work diff --git a/src/parser/parser_abs.nit b/src/parser/parser_abs.nit index 1aaa263..bc9b980 100644 --- a/src/parser/parser_abs.nit +++ b/src/parser/parser_abs.nit @@ -1,6 +1,6 @@ # Raw AST node hierarchy. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_abs +module parser_abs is no_warning("missing-doc") import location diff --git a/src/parser/parser_nodes.nit b/src/parser/parser_nodes.nit index 493ed0e..289ad53 100644 --- a/src/parser/parser_nodes.nit +++ b/src/parser/parser_nodes.nit @@ -117,8 +117,48 @@ abstract class ANode # Visit all nodes in order. # Thus, call `v.enter_visit(e)` for each child `e` fun visit_all(v: Visitor) is abstract + + # Do a deep search and return an array of tokens that match a given text + fun collect_tokens_by_text(text: String): Array[Token] + do + var v = new CollectTokensByTextVisitor(text) + v.enter_visit(self) + return v.result + end + + # Do a deep search and return an array of node that are annotated + # The attached node can be retrieved by two invocations of parent + fun collect_annotations_by_name(name: String): Array[AAnnotation] + do + var v = new CollectAnnotationsByNameVisitor(name) + v.enter_visit(self) + return v.result + end +end + +private class CollectTokensByTextVisitor + super Visitor + var text: String + var result = new Array[Token] + redef fun visit(node) + do + node.visit_all(self) + if node isa Token and node.text == text then result.add(node) + end end +private class CollectAnnotationsByNameVisitor + super Visitor + var name: String + var result = new Array[AAnnotation] + redef fun visit(node) + do + node.visit_all(self) + if node isa AAnnotation and node.n_atid.n_id.text == name then result.add(node) + end +end + + # A sequence of nodes # It is a specific class (instead of using a Array) to track the parent/child relation when nodes are added or removed class ANodes[E: ANode] @@ -269,6 +309,20 @@ abstract class Prod # All the annotations attached directly to the node var n_annotations: nullable AAnnotations = null is writable + # Return all its annotations of a given name in the order of their declaration + # Retun an empty array if no such an annotation. + fun get_annotations(name: String): Array[AAnnotation] + do + var res = new Array[AAnnotation] + var nas = n_annotations + if nas == null then return res + for na in nas.n_items do + if na.name != name then continue + res.add(na) + end + return res + end + redef fun replace_with(n: ANode) do super @@ -737,7 +791,7 @@ class TClassid end end -# A standard identifier (variable, method...). They start with a lowercase. +# A standard identifier (variable, method...). They start with a lowercase. class TId super Token redef fun to_s @@ -853,40 +907,65 @@ end class AModule super Prod + # The declaration part of the module var n_moduledecl: nullable AModuledecl = null is writable + + # List of importation clauses var n_imports = new ANodes[AImport](self) + + # List of extern blocks var n_extern_code_blocks = new ANodes[AExternCodeBlock](self) + + # List of class definition (including top-level methods and the main) var n_classdefs = new ANodes[AClassdef](self) end -# The declaration of the module with the documentation, name, and annotations -class AModuledecl +# Abstract class for definition of entities +abstract class ADefinition super Prod + # The documentation var n_doc: nullable ADoc = null is writable + + # The `redef` keyword var n_kwredef: nullable TKwredef = null is writable - var n_visibility: AVisibility is writable, noinit + + # The declared visibility + var n_visibility: nullable AVisibility = null is writable +end + +# The declaration of the module with the documentation, name, and annotations +class AModuledecl + super ADefinition + + # The `module` keyword var n_kwmodule: TKwmodule is writable, noinit + + # The declared module name var n_name: AModuleName is writable, noinit end # A import clause of a module abstract class AImport super Prod + + # The declared visibility + var n_visibility: AVisibility is writable, noinit + + # The `import` keyword + var n_kwimport: TKwimport is writable, noinit end # A standard import clause. eg `import x` class AStdImport super AImport - var n_visibility: AVisibility is writable, noinit - var n_kwimport: TKwimport is writable, noinit + # The imported module name var n_name: AModuleName is writable, noinit end # The special import clause of the kernel module. eg `import end` class ANoImport super AImport - var n_visibility: AVisibility is writable, noinit - var n_kwimport: TKwimport is writable, noinit + # The `end` keyword, that indicate the root module var n_kwend: TKwend is writable, noinit end @@ -903,21 +982,25 @@ end # An implicit or explicit public visibility modifier class APublicVisibility super AVisibility + # The `public` keyword, if any var n_kwpublic: nullable TKwpublic is writable end # An explicit private visibility modifier class APrivateVisibility super AVisibility + # The `private` keyword var n_kwprivate: TKwprivate is writable, noinit end # An explicit protected visibility modifier class AProtectedVisibility super AVisibility + # The `protected` keyword var n_kwprotected: TKwprotected is writable, noinit end # An explicit intrude visibility modifier class AIntrudeVisibility super AVisibility + # The `intrude` keyword var n_kwintrude: TKwintrude is writable, noinit end @@ -926,21 +1009,33 @@ end # There is tow special case of class definition abstract class AClassdef super Prod + # All the declared properties (including the main method) var n_propdefs = new ANodes[APropdef](self) end # A standard class definition with a name, superclasses and properties class AStdClassdef super AClassdef - var n_doc: nullable ADoc = null is writable - var n_kwredef: nullable TKwredef = null is writable - var n_visibility: AVisibility is writable, noinit + super ADefinition + + # The class kind (interface, abstract class, etc.) var n_classkind: AClasskind is writable, noinit + + # The name of the class var n_id: nullable TClassid = null is writable + + # The list of formal parameter types var n_formaldefs = new ANodes[AFormaldef](self) + + # The extern block code var n_extern_code_block: nullable AExternCodeBlock = null is writable + + # The list of super-classes var n_superclasses = new ANodes[ASuperclass](self) + + # The `end` keyword var n_kwend: TKwend is writable, noinit + redef fun hot_location do return n_id.location end @@ -962,39 +1057,56 @@ end # A default, or concrete class modifier (just `class`) class AConcreteClasskind super AClasskind + + # The `class` keyword. var n_kwclass: TKwclass is writable, noinit end # An abstract class modifier (`abstract class`) class AAbstractClasskind super AClasskind + + # The `abstract` keyword. var n_kwabstract: TKwabstract is writable, noinit + + # The `class` keyword. var n_kwclass: TKwclass is writable, noinit end # An interface class modifier (`interface`) class AInterfaceClasskind super AClasskind + + # The `interface` keyword. var n_kwinterface: TKwinterface is writable, noinit end # An enum/universal class modifier (`enum class`) class AEnumClasskind super AClasskind + + # The `enum` keyword. var n_kwenum: TKwenum is writable, noinit end # An extern class modifier (`extern class`) class AExternClasskind super AClasskind + + # The `extern` keyword. var n_kwextern: TKwextern is writable, noinit + + # The `class` keyword. var n_kwclass: nullable TKwclass = null is writable end # The definition of a formal generic parameter type. eg `X: Y` class AFormaldef super Prod + + # The name of the parameter type var n_id: TClassid is writable, noinit + # The bound of the parameter type var n_type: nullable AType = null is writable end @@ -1002,32 +1114,37 @@ end # A super-class. eg `super X` class ASuperclass super Prod + + # The super keyword var n_kwsuper: TKwsuper is writable, noinit + + # The super-class (indicated as a type) var n_type: AType is writable, noinit end # The definition of a property abstract class APropdef - super Prod - var n_doc: nullable ADoc = null is writable - var n_kwredef: nullable TKwredef = null is writable - var n_visibility: nullable AVisibility = null is writable + super ADefinition end # A definition of an attribute # For historical reason, old-syle and new-style attributes use the same `ANode` sub-class class AAttrPropdef super APropdef + + # The identifier for a old-style attribute (null if new-style) var n_kwvar: TKwvar is writable, noinit # The identifier for a new-style attribute (null if old-style) var n_id2: TId is writable, noinit + # The declared type of the attribute var n_type: nullable AType = null is writable - # The initial value, if any + # The initial value, if any (set with `=`) var n_expr: nullable AExpr = null is writable + # The initial value, if any (set with `do return`) var n_block: nullable AExpr = null is writable redef fun hot_location @@ -1039,14 +1156,31 @@ end # A definition of all kind of method (including constructors) class AMethPropdef super APropdef + + # The `fun` keyword, if any var n_kwmeth: nullable TKwmeth = null is writable + + # The `init` keyword, if any var n_kwinit: nullable TKwinit = null is writable + + # The `new` keyword, if any var n_kwnew: nullable TKwnew = null is writable + + # The name of the method, if any var n_methid: nullable AMethid = null is writable + + # The signature of the method, if any var n_signature: nullable ASignature = null is writable + + # The body (in Nit) of the method, if any var n_block: nullable AExpr = null is writable + + # The list of declared callbacks (for extern methods) var n_extern_calls: nullable AExternCalls = null is writable + + # The body (in extern code) of the method, if any var n_extern_code_block: nullable AExternCodeBlock = null is writable + redef fun hot_location do if n_methid != null then @@ -1069,7 +1203,11 @@ end # Declaration of callbacks for extern methods class AExternCalls super Prod + + # The `import` keyword var n_kwimport: TKwimport is writable, noinit + + # The list of declared callbacks var n_extern_calls: ANodes[AExternCall] = new ANodes[AExternCall](self) end @@ -1086,26 +1224,38 @@ end # A single callback declaration on a method on the current receiver class ALocalPropExternCall super APropExternCall + + # The name of the called-back method var n_methid: AMethid is writable, noinit end # A single callback declaration on a method on an explicit receiver type. class AFullPropExternCall super APropExternCall + + # The type of the receiver of the called-back method var n_type: AType is writable, noinit + + # The dot `.` var n_dot: nullable TDot = null is writable + + # The name of the called-back method var n_methid: AMethid is writable, noinit end # A single callback declaration on a method on a constructor class AInitPropExternCall super APropExternCall + + # The allocated type var n_type: AType is writable, noinit end # A single callback declaration on a `super` call class ASuperExternCall super AExternCall + + # The `super` keyword var n_kwsuper: TKwsuper is writable, noinit end @@ -1117,34 +1267,62 @@ end # A single callback declaration on a cast to a given type class ACastAsExternCall super ACastExternCall + + # The origin type of the cast var n_from_type: AType is writable, noinit + + # The dot (`.`) var n_dot: nullable TDot = null is writable + + # The `as` keyword var n_kwas: TKwas is writable, noinit + + # The destination of the cast var n_to_type: AType is writable, noinit end # A single callback declaration on a cast to a nullable type class AAsNullableExternCall super ACastExternCall + + # The origin type to cast as nullable var n_type: AType is writable, noinit + + # The `as` keyword var n_kwas: TKwas is writable, noinit + + # The `nullable` keyword var n_kwnullable: TKwnullable is writable, noinit end # A single callback declaration on a cast to a non-nullable type class AAsNotNullableExternCall super ACastExternCall + + # The destination type on a cast to not nullable var n_type: AType is writable, noinit + + # The `as` keyword. var n_kwas: TKwas is writable, noinit + + # The `not` keyword var n_kwnot: TKwnot is writable, noinit + + # The `nullable` keyword var n_kwnullable: TKwnullable is writable, noinit end # A definition of a virtual type class ATypePropdef super APropdef + + # The `type` keyword var n_kwtype: TKwtype is writable, noinit + + # The name of the virtual type var n_id: TClassid is writable, noinit + + # The bound of the virtual type var n_type: AType is writable, noinit end @@ -1157,141 +1335,202 @@ end # A method name with a simple identifier class AIdMethid super AMethid + + # The simple identifier var n_id: TId is writable, noinit end # A method name `+` class APlusMethid super AMethid + + # The `+` symbol var n_plus: TPlus is writable, noinit end # A method name `-` class AMinusMethid super AMethid + + # The `-` symbol var n_minus: TMinus is writable, noinit end # A method name `*` class AStarMethid super AMethid + + # The `*` symbol var n_star: TStar is writable, noinit end # A method name `**` class AStarstarMethid super AMethid + + # The `**` symbol var n_starstar: TStarstar is writable, noinit end # A method name `/` class ASlashMethid super AMethid + + # The `/` symbol var n_slash: TSlash is writable, noinit end # A method name `%` class APercentMethid super AMethid + + # The `%` symbol var n_percent: TPercent is writable, noinit end # A method name `==` class AEqMethid super AMethid + + # The `==` symbol var n_eq: TEq is writable, noinit end # A method name `!=` class ANeMethid super AMethid + + # The `!=` symbol var n_ne: TNe is writable, noinit end # A method name `<=` class ALeMethid super AMethid + + # The `<=` symbol var n_le: TLe is writable, noinit end # A method name `>=` class AGeMethid super AMethid + + # The `>=` symbol var n_ge: TGe is writable, noinit end # A method name `<` class ALtMethid super AMethid + + # The `<` symbol var n_lt: TLt is writable, noinit end # A method name `>` class AGtMethid super AMethid + + # The `>` symbol var n_gt: TGt is writable, noinit end # A method name `<<` class ALlMethid super AMethid + + # The `<<` symbol var n_ll: TLl is writable, noinit end # A method name `>>` class AGgMethid super AMethid + + # The `>>` symbol var n_gg: TGg is writable, noinit end # A method name `[]` class ABraMethid super AMethid + + # The `[` symbol var n_obra: TObra is writable, noinit + + # The `]` symbol var n_cbra: TCbra is writable, noinit end # A method name `<=>` class AStarshipMethid super AMethid + + # The `<=>` symbol var n_starship: TStarship is writable, noinit end # A setter method name with a simple identifier (with a `=`) class AAssignMethid super AMethid + + # The base identifier var n_id: TId is writable, noinit + + # The `=` symbol var n_assign: TAssign is writable, noinit end # A method name `[]=` class ABraassignMethid super AMethid + + # The `[` symbol var n_obra: TObra is writable, noinit + + # The `]` symbol var n_cbra: TCbra is writable, noinit + + # The `=` symbol var n_assign: TAssign is writable, noinit end # A signature in a method definition. eg `(x,y:X,z:Z):T` class ASignature super Prod + + # The `(` symbol var n_opar: nullable TOpar = null is writable + + # The list of parameters var n_params = new ANodes[AParam](self) + + # The `)` symbol var n_cpar: nullable TCpar = null is writable + + # The return type var n_type: nullable AType = null is writable end # A parameter definition in a signature. eg `x:X` class AParam super Prod + + # The name of the parameter var n_id: TId is writable, noinit + + # The type of the parameter, if any var n_type: nullable AType = null is writable + + # The `...` symbol to indicate varargs var n_dotdotdot: nullable TDotdotdot = null is writable end # A static type. eg `nullable X[Y]` class AType super Prod + # The `nullable` keyword var n_kwnullable: nullable TKwnullable = null is writable # The name of the class or of the formal type @@ -1304,7 +1543,11 @@ end # A label at the end of a block or in a break/continue statement. eg `label x` class ALabel super Prod + + # The `label` keyword var n_kwlabel: TKwlabel is writable, noinit + + # The name of the label, if any var n_id: nullable TId is writable end @@ -1318,16 +1561,29 @@ end # The last `AExpr` gives the value of the whole block class ABlockExpr super AExpr + + # The list of statements in the bloc. + # The last element is often considered as an expression that give the value of the whole block. var n_expr = new ANodes[AExpr](self) + + # The `end` keyword var n_kwend: nullable TKwend = null is writable end # A declaration of a local variable. eg `var x: X = y` class AVardeclExpr super AExpr + + # The `var` keyword var n_kwvar: TKwvar is writable, noinit + + # The name of the local variable var n_id: TId is writable, noinit + + # The declaration type of the local variable var n_type: nullable AType = null is writable + + # The `=` symbol (for the initial value) var n_assign: nullable TAssign = null is writable # The initial value, if any @@ -1337,13 +1593,19 @@ end # A `return` statement. eg `return x` class AReturnExpr super AExpr + + # The `return` keyword var n_kwreturn: nullable TKwreturn = null is writable + + # The return value, if any var n_expr: nullable AExpr = null is writable end # Something that has a label. abstract class ALabelable super Prod + + # The associated label declatation var n_label: nullable ALabel = null is writable end @@ -1351,24 +1613,32 @@ end abstract class AEscapeExpr super AExpr super ALabelable + + # The return value, if nay (unused currently) var n_expr: nullable AExpr = null is writable end # A `break` statement. class ABreakExpr super AEscapeExpr + + # The `break` keyword var n_kwbreak: TKwbreak is writable, noinit end # An `abort` statement class AAbortExpr super AExpr + + # The `abort` keyword var n_kwabort: TKwabort is writable, noinit end # A `continue` statement class AContinueExpr super AEscapeExpr + + # The `continue` keyword. var n_kwcontinue: nullable TKwcontinue = null is writable end @@ -1376,27 +1646,51 @@ end class ADoExpr super AExpr super ALabelable + + # The `do` keyword var n_kwdo: TKwdo is writable, noinit + + # The list of statements of the `do`. var n_block: nullable AExpr = null is writable end # A `if` statement class AIfExpr super AExpr + + # The `if` keyword var n_kwif: TKwif is writable, noinit + + # The expression used as the condition of the `if` var n_expr: AExpr is writable, noinit + + # The body of the `then` part var n_then: nullable AExpr = null is writable + + # The body of the `else` part var n_else: nullable AExpr = null is writable end -# A `if` expression +# A `if` expression (ternary conditional). eg. `if true then 1 else 0` class AIfexprExpr super AExpr + + # The `if` keyword var n_kwif: TKwif is writable, noinit + + # The expression used as the condition of the `if` var n_expr: AExpr is writable, noinit + + # The `then` keyword var n_kwthen: TKwthen is writable, noinit + + # The expression in the `then` part var n_then: AExpr is writable, noinit + + # The `else` keyword var n_kwelse: TKwelse is writable, noinit + + # The expression in the `else` part var n_else: AExpr is writable, noinit end @@ -1404,9 +1698,17 @@ end class AWhileExpr super AExpr super ALabelable + + # The `while` keyword var n_kwwhile: TKwwhile is writable, noinit + + # The expression used as the condition of the `while` var n_expr: AExpr is writable, noinit + + # The `do` keyword var n_kwdo: TKwdo is writable, noinit + + # The body of the loop var n_block: nullable AExpr = null is writable end @@ -1414,7 +1716,11 @@ end class ALoopExpr super AExpr super ALabelable + + # The `loop` keyword var n_kwloop: TKwloop is writable, noinit + + # The body of the loop var n_block: nullable AExpr = null is writable end @@ -1422,40 +1728,70 @@ end class AForExpr super AExpr super ALabelable + + # The `for` keyword var n_kwfor: TKwfor is writable, noinit + + # The list of name of the automatic variables var n_ids = new ANodes[TId](self) + + # The expression used as the collection to iterate on var n_expr: AExpr is writable, noinit + + # The `do` keyword var n_kwdo: TKwdo is writable, noinit + + # The body of the loop var n_block: nullable AExpr = null is writable end # An `assert` statement class AAssertExpr super AExpr + + # The `assert` keyword var n_kwassert: TKwassert is writable, noinit + + # The name of the assert, if any var n_id: nullable TId = null is writable + + # The expression used as the condition of the `assert` var n_expr: AExpr is writable, noinit + + # The body to execute when the assert fails var n_else: nullable AExpr = null is writable end # Whatever is a simple assignment. eg `= something` abstract class AAssignFormExpr super AExpr + + # The `=` symbol var n_assign: TAssign is writable, noinit + + # The right-value to assign. var n_value: AExpr is writable, noinit end # Whatever is a combined assignment. eg `+= something` abstract class AReassignFormExpr super AExpr + + # The combined operator (eg. `+=`) var n_assign_op: AAssignOp is writable, noinit + + # The right-value to apply on the combined operator. var n_value: AExpr is writable, noinit end # A `once` expression. eg `once x` class AOnceExpr super AExpr + + # The `once` keyword var n_kwonce: TKwonce is writable, noinit + + # The expression to evaluate only one time var n_expr: AExpr is writable, noinit end @@ -1480,38 +1816,45 @@ abstract class ABoolExpr super AExpr end -# A `or` expression -class AOrExpr +# Something that is binary boolean expression +abstract class ABinBoolExpr super ABoolExpr + + # The first boolean operand var n_expr: AExpr is writable, noinit + + # The second boolean operand var n_expr2: AExpr is writable, noinit end +# A `or` expression +class AOrExpr + super ABinBoolExpr +end + # A `and` expression class AAndExpr - super ABoolExpr - var n_expr: AExpr is writable, noinit - var n_expr2: AExpr is writable, noinit + super ABinBoolExpr end # A `or else` expression class AOrElseExpr - super ABoolExpr - var n_expr: AExpr is writable, noinit - var n_expr2: AExpr is writable, noinit + super ABinBoolExpr end # A `implies` expression class AImpliesExpr - super ABoolExpr - var n_expr: AExpr is writable, noinit - var n_expr2: AExpr is writable, noinit + super ABinBoolExpr end # A `not` expression class ANotExpr super ABoolExpr + + # The `not` keyword var n_kwnot: TKwnot is writable, noinit + + # The boolean operand of the `not` var n_expr: AExpr is writable, noinit end @@ -1558,7 +1901,11 @@ end # A type-ckeck expression. eg `x isa T` class AIsaExpr super ABoolExpr + + # The expression to check var n_expr: AExpr is writable, noinit + + # The destination type to check to var n_type: AType is writable, noinit end @@ -1600,17 +1947,25 @@ end # A unary minus expression. eg `-x` class AUminusExpr super ASendExpr + + # The `-` symbol var n_minus: TMinus is writable, noinit end # An explicit instantiation. eg `new T` class ANewExpr super AExpr + + # The `new` keyword var n_kwnew: TKwnew is writable, noinit + + # The `type` keyword var n_type: AType is writable, noinit # The name of the named-constructor, if any var n_id: nullable TId = null is writable + + # The arguments of the `new` var n_args: AExprs is writable, noinit end @@ -1686,8 +2041,14 @@ end # A call to `super`. OR a call of a super-constructor class ASuperExpr super AExpr + + # The qualifier part before the super (currenlty unused) var n_qualified: nullable AQualified = null is writable + + # The `super` keyword var n_kwsuper: TKwsuper is writable, noinit + + # The arguments of the super var n_args: AExprs is writable, noinit end @@ -1695,13 +2056,19 @@ end # Note: because `init` is a keyword and not a `TId`, the explicit call to init cannot be a `ACallFormExpr`. class AInitExpr super ASendExpr + + # The `init` keyword var n_kwinit: TKwinit is writable, noinit + + # The arguments of the init var n_args: AExprs is writable, noinit end # Whatever looks-like a call of the brackets `[]` operator. abstract class ABraFormExpr super ASendExpr + + # The arguments inside the brackets var n_args: AExprs is writable, noinit end @@ -1719,6 +2086,8 @@ end # Whatever is an access to a local variable abstract class AVarFormExpr super AExpr + + # The name of the attribute var n_id: TId is writable, noinit end @@ -1751,36 +2120,58 @@ end # A literal range, open or closed abstract class ARangeExpr super AExpr + + # The left (lower) element of the range var n_expr: AExpr is writable, noinit + + # The right (uppr) element of the range var n_expr2: AExpr is writable, noinit end # A closed literal range. eg `[x..y]` class ACrangeExpr super ARangeExpr + + # The opening bracket `[` var n_obra: TObra is writable, noinit + + # The closing bracket `]` var n_cbra: TCbra is writable, noinit end # An open literal range. eg `[x..y[` class AOrangeExpr super ARangeExpr + + # The opening bracket `[` var n_obra: TObra is writable, noinit + + # The closing bracket `[` (because open range) var n_cbra: TObra is writable, noinit end # A literal array. eg. `[x,y,z]` class AArrayExpr super AExpr + + # The opening bracket `[` var n_obra: TObra is writable, noinit + + # The elements of the array var n_exprs = new ANodes[AExpr](self) + + # The type of the element of the array (if any) var n_type: nullable AType = null is writable + + # The closing bracket `]` var n_cbra: TCbra is writable, noinit end -# A read of `self` +# A read of `self` class ASelfExpr super AExpr + + # The `self` keyword var n_kwself: nullable TKwself is writable end @@ -1792,45 +2183,69 @@ end # A `true` boolean literal constant class ATrueExpr super ABoolExpr + + # The `true` keyword var n_kwtrue: TKwtrue is writable, noinit end + # A `false` boolean literal constant class AFalseExpr super ABoolExpr + + # The `false` keyword var n_kwfalse: TKwfalse is writable, noinit end + # A `null` literal constant class ANullExpr super AExpr + + # The `null` keyword var n_kwnull: TKwnull is writable, noinit end + # An integer literal class AIntExpr super AExpr end + # An integer literal in decimal format class ADecIntExpr super AIntExpr + + # The decimal token var n_number: TNumber is writable, noinit end + # An integer literal in hexadecimal format class AHexIntExpr super AIntExpr + + # The hexadecimal token var n_hex_number: THexNumber is writable, noinit end + # A float literal class AFloatExpr super AExpr + + # The float token var n_float: TFloat is writable, noinit end + # A character literal class ACharExpr super AExpr + + # The character token var n_char: TChar is writable, noinit end + # A string literal abstract class AStringFormExpr super AExpr + + # The string token var n_string: Token is writable, noinit end @@ -1858,54 +2273,85 @@ end # Each part is modeled a sequence of expression. eg. `["a{, x, }b{, y, }c"]` class ASuperstringExpr super AExpr + + # The list of the expressions of the superstring var n_exprs = new ANodes[AExpr](self) end # A simple parenthesis. eg `(x)` class AParExpr super AExpr + + # The opening parenthesis var n_opar: TOpar is writable, noinit + + # The inner expression var n_expr: AExpr is writable, noinit + + # The closing parenthesis var n_cpar: TCpar is writable, noinit end -# A type cast. eg `x.as(T)` -class AAsCastExpr +# A cast, against a type or `not null` +class AAsCastForm super AExpr + + # The expression to cast var n_expr: AExpr is writable, noinit + + # The `as` keyword var n_kwas: TKwas is writable, noinit + + # The opening parenthesis var n_opar: nullable TOpar = null is writable - var n_type: AType is writable, noinit + + # The closing parenthesis var n_cpar: nullable TCpar = null is writable end +# A type cast. eg `x.as(T)` +class AAsCastExpr + super AAsCastForm + + # The target type to cast to + var n_type: AType is writable, noinit +end + # A as-not-null cast. eg `x.as(not null)` class AAsNotnullExpr - super AExpr - var n_expr: AExpr is writable, noinit - var n_kwas: TKwas is writable, noinit - var n_opar: nullable TOpar = null is writable + super AAsCastForm + + # The `not` keyword var n_kwnot: TKwnot is writable, noinit + + # The `null` keyword var n_kwnull: TKwnull is writable, noinit - var n_cpar: nullable TCpar = null is writable end # A is-set check of old-style attributes. eg `isset x._a` class AIssetAttrExpr super AAttrFormExpr + + # The `isset` keyword var n_kwisset: TKwisset is writable, noinit end # An ellipsis notation used to pass an expression as it, in a vararg parameter class AVarargExpr super AExpr + + # The passed expression var n_expr: AExpr is writable, noinit + + # The `...` symbol var n_dotdotdot: TDotdotdot is writable, noinit end # A list of expression separated with commas (arguments for instance) class AManyExpr super AExpr + + # The list of expressions var n_exprs = new ANodes[AExpr](self) end @@ -1913,6 +2359,8 @@ end # Can only be found in special construction like arguments of annotations. class ATypeExpr super AExpr + + # The encapsulated type var n_type: AType is writable, noinit end @@ -1920,13 +2368,18 @@ end # Can only be found in special construction like arguments of annotations. class AMethidExpr super AExpr - # The receiver, is any + + # The receiver var n_expr: AExpr is writable, noinit + + # The encapsulated method identifier var n_id: AMethid is writable, noinit end # A special expression that encapsulate an annotation # Can only be found in special construction like arguments of annotations. +# +# The encapsulated annotations are in `n_annotations` class AAtExpr super AExpr end @@ -1934,15 +2387,25 @@ end # A special expression to debug types class ADebugTypeExpr super AExpr + + # The `debug` keyword var n_kwdebug: TKwdebug is writable, noinit + + # The `type` keyword var n_kwtype: TKwtype is writable, noinit + + # The expression to check var n_expr: AExpr is writable, noinit + + # The type to check var n_type: AType is writable, noinit end # A list of expression separated with commas (arguments for instance) abstract class AExprs super Prod + + # The list of expressions var n_exprs = new ANodes[AExpr](self) end @@ -1954,14 +2417,22 @@ end # A list of expressions enclosed in parentheses class AParExprs super AExprs + + # The opening parenthesis var n_opar: TOpar is writable, noinit + + # The closing parenthesis var n_cpar: TCpar is writable, noinit end # A list of expressions enclosed in brackets class ABraExprs super AExprs + + # The opening bracket var n_obra: TObra is writable, noinit + + # The closing bracket var n_cbra: TCbra is writable, noinit end @@ -1973,42 +2444,66 @@ end # The `+=` assignment operation class APlusAssignOp super AAssignOp + + # The `+=` operator var n_pluseq: TPluseq is writable, noinit end # The `-=` assignment operator class AMinusAssignOp super AAssignOp + + # The `-=` operator var n_minuseq: TMinuseq is writable, noinit end # A possibly fully-qualified module identifier class AModuleName super Prod + + # The starting quad (`::`) var n_quad: nullable TQuad = null is writable + + # The list of quad-separated project/group identifiers var n_path = new ANodes[TId](self) + + # The final module identifier var n_id: TId is writable, noinit end # A language declaration for an extern block class AInLanguage super Prod + + # The `in` keyword var n_kwin: TKwin is writable, noinit + + # The language name var n_string: TString is writable, noinit end # An full extern block class AExternCodeBlock super Prod + + # The language declration var n_in_language: nullable AInLanguage = null is writable + + # The block of extern code var n_extern_code_segment: TExternCodeSegment is writable, noinit end # A possible full method qualifier. class AQualified super Prod + + # The starting quad (`::`) var n_quad: nullable TQuad = null is writable + + # The list of quad-separated project/group/module identifiers var n_id = new ANodes[TId](self) + + # A class identifier var n_classid: nullable TClassid = null is writable end @@ -2016,33 +2511,63 @@ end # It contains the block of comments just above the declaration class ADoc super Prod + + # A list of lines of comment var n_comment = new ANodes[TComment](self) end # A group of annotation on a node +# +# This same class is used for the 3 kind of annotations: +# +# * *is* annotations. eg `module foo is bar`. +# * *at* annotations. eg `foo@bar` or `foo@(bar,baz)`. +# * *class* annotations, defined in classes. class AAnnotations super Prod + + # The `@` symbol, for *at* annotations var n_at: nullable TAt = null is writable + + # The opening parenthesis in *at* annotations var n_opar: nullable TOpar = null is writable + + # The list of annotations var n_items = new ANodes[AAnnotation](self) + + # The closing parenthesis in *at* annotations var n_cpar: nullable TCpar = null is writable end # A single annotation class AAnnotation - super Prod - var n_doc: nullable ADoc = null is writable - var n_kwredef: nullable TKwredef = null is writable - var n_visibility: nullable AVisibility is writable + super ADefinition + + # The name of the annotation var n_atid: AAtid is writable, noinit + + # The opening parenthesis of the arguments var n_opar: nullable TOpar = null is writable + + # The list of arguments var n_args = new ANodes[AExpr](self) + + # The closing parenthesis var n_cpar: nullable TCpar = null is writable + + # The name of the annotation + fun name: String + do + return n_atid.n_id.text + end end # An annotation name abstract class AAtid super Prod + + # The identifier of the annotation. + # Can be a TId of a keyword var n_id: Token is writable, noinit end @@ -2069,6 +2594,10 @@ end # The root of the AST class Start super Prod + + # The main module var n_base: nullable AModule is writable + + # The end of file (or error) token var n_eof: EOF is writable end diff --git a/src/parser/parser_prod.nit b/src/parser/parser_prod.nit index 39fafc6..d49b809 100644 --- a/src/parser/parser_prod.nit +++ b/src/parser/parser_prod.nit @@ -1,6 +1,6 @@ # Production AST nodes full definition. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_prod +module parser_prod is no_warning("missing-doc") import lexer intrude import parser_nodes diff --git a/src/parser/xss/main.xss b/src/parser/xss/main.xss index 00507f9..fae2ea4 100644 --- a/src/parser/xss/main.xss +++ b/src/parser/xss/main.xss @@ -23,7 +23,7 @@ $ include 'prods.xss' $ output 'parser_abs.nit' # Raw AST node hierarchy. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_abs +module parser_abs is no_warning("missing-doc") import location @@ -34,7 +34,7 @@ $ end output $ output 'lexer.nit' # Lexer and its tokens. # This file was generated by SableCC (http://www.sablecc.org/). -module lexer +module lexer is no_warning("missing-doc") $ if $usermodule intrude import $usermodule @@ -51,7 +51,7 @@ $ end output $ output 'parser_prod.nit' # Production AST nodes full definition. # This file was generated by SableCC (http://www.sablecc.org/). -module parser_prod +module parser_prod is no_warning("missing-doc") import lexer $ if $usermodule @@ -67,7 +67,7 @@ $ end output $ output 'parser.nit' # Parser. # This file was generated by SableCC (http://www.sablecc.org/). -module parser +module parser is no_warning("missing-doc", "unread-variable") intrude import parser_prod intrude import parser_work diff --git a/src/parser_util.nit b/src/parser_util.nit index 82aec57..69a170c 100644 --- a/src/parser_util.nit +++ b/src/parser_util.nit @@ -222,10 +222,15 @@ redef class ToolContext end end +# A modified lexer that feed tokens before and after the real tokens. class InjectedLexer super Lexer + # The tokens to use before the real tokens (in order). var injected_before = new List[Token] + + # The tokens to use after the real tokens (in order). + # The real EOF token is produced after these tokens. var injected_after = new List[Token] private var is_finished = false @@ -246,44 +251,3 @@ class InjectedLexer return tok end end - -redef class ANode - # Do a deep search and return an array of tokens that match a given text - fun collect_tokens_by_text(text: String): Array[Token] - do - var v = new CollectTokensByTextVisitor(text) - v.enter_visit(self) - return v.result - end - - # Do a deep search and return an array of node that are annotated - # The attached node can be retrieved by two invocation of parent - fun collect_annotations_by_name(name: String): Array[AAnnotation] - do - var v = new CollectAnnotationsByNameVisitor(name) - v.enter_visit(self) - return v.result - end -end - -private class CollectTokensByTextVisitor - super Visitor - var text: String - var result = new Array[Token] - redef fun visit(node) - do - node.visit_all(self) - if node isa Token and node.text == text then result.add(node) - end -end - -private class CollectAnnotationsByNameVisitor - super Visitor - var name: String - var result = new Array[AAnnotation] - redef fun visit(node) - do - node.visit_all(self) - if node isa AAnnotation and node.n_atid.n_id.text == name then result.add(node) - end -end diff --git a/src/phase.nit b/src/phase.nit index 2011cd8..1a10cf2 100644 --- a/src/phase.nit +++ b/src/phase.nit @@ -21,7 +21,7 @@ import poset redef class ToolContext # The various registered phases to performs - # The order in the poset is the dependance of phases + # The order in the poset is the dependence of phases # # While you can directly modify the poset (nodes and edges), # it is often simpler to use the constructor in `Phase` @@ -30,11 +30,14 @@ redef class ToolContext # --disable-phase var opt_disable_phase = new OptionArray("DEBUG: Disable a specific phase; use `list` to get the list.", "--disable-phase") + # --disable-phase + var opt_sloppy = new OptionBool("DEBUG: force lazy semantic analysis of the source-code", "--sloppy") + redef init do super - option_context.add_option(opt_disable_phase) + option_context.add_option(opt_disable_phase, opt_sloppy) end redef fun process_options(args) @@ -62,15 +65,24 @@ redef class ToolContext end if not found then fatal_error(null, "Error: no phase named `{v}`. Use `list` to list all phases.") end + + if opt_sloppy.value then semantize_is_lazy = true end - fun phases_list: Sequence[Phase] - do + # The list of registered phases in the application order. + var phases_list: Sequence[Phase] is lazy do var phases = self.phases.to_a self.phases.sort(phases) return phases end + # Is `phase_process_npropdef` not called automatically by `run_phases`? + # + # When set to true, it is the responsibility of the tools + # + # Is false by default. + var semantize_is_lazy = false is writable + # Set of already analyzed modules. private var phased_modules = new HashSet[AModule] @@ -110,7 +122,7 @@ redef class ToolContext for nclassdef in nmodule.n_classdefs do assert phase.toolcontext == self phase.process_nclassdef(nclassdef) - for npropdef in nclassdef.n_propdefs do + if not semantize_is_lazy then for npropdef in nclassdef.n_propdefs do assert phase.toolcontext == self phase_process_npropdef(phase, npropdef) end @@ -136,10 +148,37 @@ redef class ToolContext errors_info end - fun phase_process_npropdef(phase: Phase, npropdef: APropdef) + # Process the given `phase` on the `npropdef` + # Called by `run_phases` + protected fun phase_process_npropdef(phase: Phase, npropdef: APropdef) do phase.process_npropdef(npropdef) end + + # Run the phase on the given npropdef. + # Does nothing if `semantize_is_lazy` is false. + fun run_phases_on_npropdef(npropdef: APropdef) + do + if not semantize_is_lazy then return + if npropdef.is_phased then return + npropdef.is_phased = true + + #self.info("Semantic analysis of property {npropdef.location.file.filename}", 0) + + var phases = phases_list + for phase in phases do + if phase.disabled then continue + assert phase.toolcontext == self + phase_process_npropdef(phase, npropdef) + self.check_errors + end + end +end + +redef class APropdef + # Is the propdef already analyzed by `run_phases_on_npropdef`. + # Unused unless `semantize_is_lazy` is true. + private var is_phased = false end # Collect all annotation diff --git a/src/compiler/android_platform.nit b/src/platform/android.nit similarity index 82% rename from src/compiler/android_platform.nit rename to src/platform/android.nit index 1cf6ba8..6ba5502 100644 --- a/src/compiler/android_platform.nit +++ b/src/platform/android.nit @@ -15,10 +15,10 @@ # limitations under the License. # Compile program for the Android platform -module android_platform +module android import platform -import abstract_compiler +import compiler::abstract_compiler import ffi intrude import ffi::extra_java_files import android_annotations @@ -36,6 +36,8 @@ class AndroidPlatform redef fun supports_libunwind do return false + redef fun supports_linker_script do return false + redef fun toolchain(toolcontext) do return new AndroidToolchain(toolcontext) end @@ -174,7 +176,8 @@ $(call import-module,android/native_app_glue) + android:debuggable="{{{not release}}}" + {{{icon_declaration}}}> @@ -234,21 +237,22 @@ $(call import-module,android/native_app_glue) end end - ### copy resources (for android) - # This will be accessed from `android_project_root` - var res_dir + ### Copy resources and libs where expected by the SDK + var project_root if compiler.mainmodule.location.file != null then # it is a real file, use "{file}/../res" - res_dir = "{compiler.mainmodule.location.file.filename.dirname}/../res" + project_root = "{compiler.mainmodule.location.file.filename.dirname}/.." else # probably used -m, use "." - res_dir = "res" + project_root = "." end + + # Android resources folder + var res_dir = project_root / "res" if res_dir.file_exists then # copy the res folder to .nit_compile res_dir = res_dir.realpath - var target_res_dir = "{android_project_root}" - toolcontext.exec_and_check(["cp", "-R", res_dir, target_res_dir], "Android project error") + toolcontext.exec_and_check(["cp", "-R", res_dir, android_project_root], "Android project error") end if not res_dir.file_exists or not "{res_dir}/values/strings.xml".file_exists then @@ -258,6 +262,12 @@ $(call import-module,android/native_app_glue) {{{app_name}}} """.write_to_file "{dir}/res/values/strings.xml" end + + # Android libs folder + var libs_dir = project_root / "libs" + if libs_dir.file_exists then + toolcontext.exec_and_check(["cp", "-r", libs_dir, android_project_root], "Android project error") + end end redef fun write_makefile(compiler, compile_dir, cfiles) @@ -283,12 +293,40 @@ $(call import-module,android/native_app_glue) # Move the apk to the target var outname = outfile(compiler.mainmodule) - var src_apk_suffix if release then - src_apk_suffix = "release-unsigned" - else src_apk_suffix = "debug" + var apk_path = "{android_project_root}/bin/{compiler.mainmodule.name}-release-unsigned.apk" + + # Sign APK + var keystore_path= "KEYSTORE".environ + var key_alias= "KEY_ALIAS".environ + var tsa_server= "TSA_SERVER".environ + + if key_alias.is_empty then + toolcontext.fatal_error(null, + "Fatal Error: the environment variable `KEY_ALIAS` must be set to use the `--release` option on Android projects.") + end - toolcontext.exec_and_check(["mv", "{android_project_root}/bin/{compiler.mainmodule.name}-{src_apk_suffix}.apk", outname], "Android project error") + args = ["jarsigner", "-sigalg", "MD5withRSA", "-digestalg", "SHA1", apk_path, key_alias] + + ## Use a custom keystore + if not keystore_path.is_empty then args.add_all(["-keystore", keystore_path]) + + ## Use a TSA server + if not tsa_server.is_empty then args.add_all(["-tsa", tsa_server]) + + toolcontext.exec_and_check(args, "Android project error") + + # Clean output file + if outname.to_path.exists then outname.to_path.delete + + # Align APK + args = ["zipalign", "4", apk_path, outname] + toolcontext.exec_and_check(args, "Android project error") + else + # Move to the expected output path + args = ["mv", "{android_project_root}/bin/{compiler.mainmodule.name}-debug.apk", outname] + toolcontext.exec_and_check(args, "Android project error") + end end end diff --git a/src/compiler/android_annotations.nit b/src/platform/android_annotations.nit similarity index 100% rename from src/compiler/android_annotations.nit rename to src/platform/android_annotations.nit diff --git a/src/compiler/emscripten_platform.nit b/src/platform/emscripten.nit similarity index 94% rename from src/compiler/emscripten_platform.nit rename to src/platform/emscripten.nit index 0a262fc..dc2662f 100644 --- a/src/compiler/emscripten_platform.nit +++ b/src/platform/emscripten.nit @@ -15,10 +15,10 @@ # limitations under the License. # Compile to JavaScript using the Emscripten SDK -module emscripten_platform +module emscripten import platform -import abstract_compiler +import compiler::abstract_compiler redef class ToolContext redef fun platform_from_name(name) @@ -33,6 +33,7 @@ class EmscriptenPlatform redef fun supports_libunwind do return false redef fun supports_libgc do return false + redef fun supports_linker_script do return false redef fun toolchain(toolcontext) do return new EnscriptenToolchain(toolcontext) end diff --git a/src/platform.nit b/src/platform/platform.nit similarity index 97% rename from src/platform.nit rename to src/platform/platform.nit index 8f2edbb..5b7f7a5 100644 --- a/src/platform.nit +++ b/src/platform/platform.nit @@ -103,7 +103,7 @@ end # Sub-classes of `Platform` represent the target platform of a compilation # # Services will be added to this class in other modules. -abstract class Platform +class Platform # Does the platform provide and support the library `unwind`? fun supports_libunwind: Bool do return true @@ -112,4 +112,7 @@ abstract class Platform # Does this platform declare its own main function? If so, we won't generate one in Nit. fun no_main: Bool do return false + + # Does the platform accepts linker scripts? + fun supports_linker_script: Bool do return true end diff --git a/src/compiler/pnacl_platform.nit b/src/platform/pnacl.nit similarity index 99% rename from src/compiler/pnacl_platform.nit rename to src/platform/pnacl.nit index 3f96318..9fceaad 100644 --- a/src/compiler/pnacl_platform.nit +++ b/src/platform/pnacl.nit @@ -15,10 +15,10 @@ # limitations under the License. # Compile program for the PNaCl platform -module pnacl_platform +module pnacl import platform -import abstract_compiler +import compiler::abstract_compiler redef class ToolContext redef fun platform_from_name(name) diff --git a/src/pretty.nit b/src/pretty.nit index 3b9f7ef..2d08571 100644 --- a/src/pretty.nit +++ b/src/pretty.nit @@ -79,7 +79,7 @@ class PrettyPrinterVisitor current_token = nmodule.location.file.first_token visit nmodule catch_up nmodule.location.file.last_token - tpl.add "\n" + if skip_empty then tpl.add "\n" return tpl.as(not null) end @@ -172,7 +172,7 @@ class PrettyPrinterVisitor else abort end - assert current_token.location <= token.location + if current_token.location > token.location then return while current_token != token do visit current_token end @@ -183,7 +183,7 @@ class PrettyPrinterVisitor visit current_token end - while current_token isa TEol do skip + while current_token isa TEol do visit(current_token) end # The template under construction. @@ -223,6 +223,14 @@ class PrettyPrinterVisitor if current_length == 0 and last_line_is_blank then return previous_length = current_length current_length = 0 + if skip_empty then wait_addn += 1 + end + + # Perform `addn` even if not `skip_empty`. + fun forcen do + if current_length == 0 and last_line_is_blank then return + previous_length = current_length + current_length = 0 wait_addn += 1 end @@ -243,6 +251,15 @@ class PrettyPrinterVisitor consume "." end end + + # Do we break string literals that are too long? + var break_strings = false is public writable + + # Do we force `do` to be on the same line as the method signature? + var inline_do = false is public writable + + # Do we force the deletion of empty lines? + var skip_empty = false is public writable end # Base framework redefs @@ -255,9 +272,10 @@ redef class ANodes[E] if e != first then if not e_can_inline then v.add "," - v.addn - v.addt + v.forcen + v.indent += 1 v.addt + v.indent -= 1 else v.add ", " end @@ -308,6 +326,17 @@ redef class Token redef fun was_inline do return true end +redef class TEol + redef fun accept_pretty_printer(v) do + if v.skip_empty then + super + else + v.add text + v.current_token = next_token + end + end +end + redef class Prod redef fun accept_pretty_printer(v) do v.visit first_token @@ -344,7 +373,7 @@ redef class Prod end redef fun was_inline do - return first_token.location.line_start == last_token.location.line_end + return start_token.location.line_start == last_token.location.line_end end end @@ -355,13 +384,13 @@ redef class TComment if is_adoc then v.addt super - v.addn + v.forcen return end if is_licence then super - v.addn + v.forcen if is_last_in_group then v.addn return end @@ -370,7 +399,7 @@ redef class TComment v.addn v.addt super - v.addn + v.forcen v.addn return end @@ -379,13 +408,14 @@ redef class TComment if next_token isa TComment and is_first_in_group then v.addn v.addt super - v.addn + v.forcen var prev_token = self.prev_token if prev_token isa TComment and prev_token.is_inline and is_last_in_group then v.addn return end super + if not v.skip_empty then v.forcen end # Is `self` part of an `ADoc`? @@ -434,7 +464,6 @@ redef class AAnnotations redef fun accept_pretty_printer(v) do v.adds v.consume "is" - if v.can_inline(self) then v.adds for n_item in n_items do @@ -443,21 +472,27 @@ redef class AAnnotations v.add ", " end end - v.finish_line - else if n_items.length > 1 then - v.addn + if not was_inline then + v.finish_line + if v.current_token isa TKwend then v.skip + end + else + v.forcen v.indent += 1 - for n_item in n_items do v.addt v.visit n_item v.finish_line - v.addn + if n_item != n_items.last then + if was_inline then + v.forcen + else + v.addn + end + end end - v.indent -= 1 end - if not was_inline and v.current_token isa TKwend then v.skip end redef fun is_inlinable do @@ -469,6 +504,10 @@ end redef class AAnnotation redef fun accept_pretty_printer(v) do + if n_visibility != null and not n_visibility isa APublicVisibility then + v.visit n_visibility + v.adds + end v.visit n_atid if not n_args.is_empty then if n_opar == null then @@ -494,7 +533,7 @@ redef class AModule v.visit n_moduledecl if not n_imports.is_empty then - v.addn + if v.skip_empty then v.addn for n_import in n_imports do v.catch_up n_import @@ -516,7 +555,7 @@ redef class AModule end if not n_classdefs.is_empty then - v.addn + if v.skip_empty then v.addn for n_classdef in n_classdefs do v.catch_up n_classdef @@ -561,7 +600,7 @@ redef class AModuledecl end v.finish_line - v.addn + if v.skip_empty then v.addn end end @@ -582,7 +621,7 @@ redef class ANoImport v.adds v.visit n_kwend v.finish_line - v.addn + if v.skip_empty then v.addn end end @@ -597,7 +636,7 @@ redef class AStdImport v.adds v.visit n_name v.finish_line - v.addn + if v.skip_empty then v.addn end end @@ -609,9 +648,9 @@ redef class AClassdef v.catch_up n_propdef if n_propdef.n_doc != null or not v.can_inline(n_propdef) then - if n_propdef != n_propdefs.first then v.addn + if v.skip_empty and n_propdef != n_propdefs.first then v.addn v.visit n_propdef - if n_propdef != n_propdefs.last then v.addn + if v.skip_empty and n_propdef != n_propdefs.last then v.addn else v.visit n_propdef end @@ -660,7 +699,7 @@ redef class AStdClassdef end else v.finish_line - v.addn + if v.skip_empty then v.addn v.indent += 1 for n_superclass in n_superclasses do @@ -672,7 +711,7 @@ redef class AStdClassdef end if not n_superclasses.is_empty and not n_propdefs.is_empty then - v.addn + if v.skip_empty then v.addn end super @@ -682,7 +721,7 @@ redef class AStdClassdef v.visit n_kwend v.finish_line - v.addn + if v.skip_empty then v.addn assert v.indent == 0 end @@ -772,6 +811,90 @@ redef class APropdef end end + # Factorize annotations visit for all APropdef. + # + # Return true if annotations were inlined. + fun visit_annotations(v: PrettyPrinterVisitor, n_annotations: nullable AAnnotations): Bool do + var res = v.can_inline(n_annotations) + if n_annotations != null then v.visit n_annotations + return res + end + + # Factorize block visit for APropdefs. + # + # Were annotations printed inline? If so, we need to print the block differently. + fun visit_block(v: PrettyPrinterVisitor, n_block: nullable AExpr, annot_inline: Bool) do + # var can_inline = v.can_inline(n_block) + if n_block == null then return + if n_annotations != null and not annot_inline then + v.forcen + v.addt + end + if v.inline_do then + while not v.current_token isa TKwdo do v.skip + end + var token = v.current_token + var do_inline = true + loop + if token isa TEol then + v.skip + if not v.can_inline(n_block) then + v.forcen + v.addt + do_inline = false + end + end + token = v.current_token + if token isa TKwdo then break + end + if annot_inline and do_inline then v.adds + v.consume "do" + + if v.can_inline(n_block) and do_inline then + v.adds + + if n_block isa ABlockExpr then + if n_block.n_expr.is_empty then + v.visit n_block.n_kwend + else + v.visit n_block.n_expr.first + v.current_token = n_block.n_kwend + v.skip + end + else + v.visit n_block + if v.current_token isa TKwend then v.skip + end + else + v.finish_line + if was_inline then + v.forcen + else + v.addn + end + v.indent += 1 + + if n_block isa ABlockExpr then + n_block.force_block = true + v.visit n_block + v.catch_up n_block.n_kwend + else + v.addt + v.visit n_block + v.forcen + end + + v.indent -= 1 + v.addt + if n_block isa ABlockExpr then + v.visit n_block.n_kwend + else + v.add "end" + end + end + v.finish_line + end + redef fun start_token do if n_doc == null then return super return n_doc.last_token.next_token @@ -798,7 +921,8 @@ redef class AAttrPropdef v.visit n_expr end - if n_annotations != null then v.visit n_annotations + var annot_inline = visit_annotations(v, n_annotations) + visit_block(v, n_block, annot_inline) v.finish_line v.addn end @@ -822,6 +946,7 @@ redef class ATypePropdef v.consume ":" v.adds v.visit n_type + visit_annotations(v, n_annotations) v.finish_line v.addn end @@ -834,7 +959,6 @@ redef class AMethPropdef # TODO: Handle extern annotations var before = v.indent - var can_inline = v.can_inline(self) super if n_kwinit != null then v.visit n_kwinit if n_kwmeth != null then v.visit n_kwmeth @@ -847,72 +971,15 @@ redef class AMethPropdef v.visit n_signature - if n_annotations != null then - v.visit n_annotations - else - v.adds - end + var annot_inline = visit_annotations(v, n_annotations) if n_extern_calls != null or n_extern_code_block != null then - if n_annotations != null then v.adds + v.adds if n_extern_calls != null then v.visit n_extern_calls if n_extern_code_block != null then v.visit n_extern_code_block end - var n_block = self.n_block - - if n_block != null then - while not v.current_token isa TKwdo do v.skip - if n_annotations != null then - if v.can_inline(n_annotations) then - v.adds - else - v.addt - end - end - v.consume "do" - - if can_inline then - v.adds - - if n_block isa ABlockExpr then - if n_block.n_expr.is_empty then - v.visit n_block.n_kwend - else - v.visit n_block.n_expr.first - v.current_token = n_block.n_kwend - v.skip - end - else - v.visit n_block - if v.current_token isa TKwend then v.skip - end - else - v.finish_line - v.addn - v.indent += 1 - - if n_block isa ABlockExpr then - n_block.force_block = true - v.visit n_block - v.catch_up n_block.n_kwend - else - v.addt - v.visit n_block - v.addn - end - - v.indent -= 1 - v.addt - if n_block isa ABlockExpr then - v.visit n_block.n_kwend - else - v.add "end" - end - end - end - - v.finish_line + visit_block(v, n_block, annot_inline) v.addn assert v.indent == before end @@ -934,7 +1001,7 @@ end redef class AMainMethPropdef redef fun accept_pretty_printer(v) do v.visit n_block - v.addn + if v.skip_empty then v.addn end end @@ -980,8 +1047,9 @@ redef class AExternCalls v.visit_list n_extern_calls else v.addn + v.indent += 1 v.addt - v.addt + v.indent -= 1 v.visit_list n_extern_calls end @@ -1087,7 +1155,7 @@ redef class TExternCodeSegment for line in lines do v.add line.r_trim - v.addn + v.forcen end v.addt @@ -1177,11 +1245,18 @@ redef class AIfExpr else if n_then == null then v.add "end" end - v.skip_to last_token.last_real_token_in_line else v.finish_line - v.addn + if was_inline then + v.forcen + else if not v.skip_empty and n_then != null and + n_then.was_inline and + n_then.location.line_end == location.line_start then + v.forcen # Xymus fucking syntax + else + v.addn + end v.indent += 1 if n_then != null then @@ -1191,7 +1266,11 @@ redef class AIfExpr else v.addt v.visit n_then - v.addn + if n_then.was_inline then + v.forcen + else + v.addn + end end end @@ -1210,7 +1289,11 @@ redef class AIfExpr v.visit n_else else v.finish_line - v.addn + if was_inline then + v.forcen + else + v.addn + end v.indent += 1 if n_else isa ABlockExpr then @@ -1219,7 +1302,11 @@ redef class AIfExpr else v.addt v.visit n_else - v.addn + if n_else.was_inline then + v.forcen + else + v.addn + end end if last_token isa TKwend then @@ -1479,7 +1566,6 @@ redef class ACallExpr if not n_expr isa AImplicitSelfExpr and not can_inline then v.addn v.addt - v.addt end v.visit n_id @@ -1640,8 +1726,9 @@ redef class ANewExpr if not can_inline then v.addn + v.indent += 1 v.addt - v.addt + v.indent -= 1 end v.visit n_id @@ -1749,16 +1836,15 @@ redef class AAssertExpr v.visit n_else else v.addn + v.indent += 1 if n_else isa ABlockExpr then - v.indent += 1 n_else.force_block = true v.visit n_else v.indent -= 1 v.addt v.visit n_else.n_kwend else - v.indent += 1 v.addt v.visit n_else v.addn @@ -1897,8 +1983,9 @@ private class ABinOpHelper v.visit bin_expr2 else v.addn + v.indent += 1 v.addt - v.addt + v.indent -= 1 v.visit bin_expr2 end end @@ -2073,9 +2160,13 @@ end redef class AStringFormExpr redef fun accept_pretty_printer(v) do - var can_inline = v.can_inline(self) - - if can_inline then + if not v.break_strings then + # n_string.force_inline = true + v.visit n_string + return + end + if v.can_inline(self) then + n_string.force_inline = true v.visit n_string else var text = n_string.text @@ -2086,7 +2177,11 @@ redef class AStringFormExpr if v.current_length >= v.max_size and i <= text.length - 3 then v.add "\" +" - v.addn + if was_inline then + v.forcen + else + v.addn + end v.indent += 1 v.addt v.indent -= 1 @@ -2103,7 +2198,12 @@ end redef class ASuperstringExpr redef fun accept_pretty_printer(v) do - for n_expr in n_exprs do v.visit n_expr + for n_expr in n_exprs do + if not v.break_strings then + n_expr.force_inline = true + end + v.visit n_expr + end end redef fun must_be_inline do diff --git a/src/rapid_type_analysis.nit b/src/rapid_type_analysis.nit index 7881db0..2d9fee4 100644 --- a/src/rapid_type_analysis.nit +++ b/src/rapid_type_analysis.nit @@ -87,8 +87,7 @@ class RapidTypeAnalysis var anchor = callsite.anchor if anchor != null then mtype = mtype.anchor_to(callsite.mmodule, anchor) mtype = mtype.as_notnullable - assert mtype isa MClassType - mtype = mtype.mclass.intro.bound_mtype + if mtype isa MClassType then mtype = mtype.mclass.intro.bound_mtype var mproperty = callsite.mproperty var res = live_targets_cache[mtype, mproperty] if res != null then return res diff --git a/src/semantize/auto_super_init.nit b/src/semantize/auto_super_init.nit index ac7763e..b16eaf5 100644 --- a/src/semantize/auto_super_init.nit +++ b/src/semantize/auto_super_init.nit @@ -14,7 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Computing of super-constructors that must be implicitely called at the begin of constructors. +# Computing of super-constructors that must be implicitly called at the begin of constructors. # The current rules are a bit crazy but whatever. module auto_super_init @@ -22,6 +22,7 @@ import typing private import annotation redef class ToolContext + # Phase that inject `super` in constructors that need it. var auto_super_init_phase: Phase = new AutoSuperInitPhase(self, [typing_phase]) end diff --git a/src/semantize/flow.nit b/src/semantize/flow.nit index 02ce863..1f96058 100644 --- a/src/semantize/flow.nit +++ b/src/semantize/flow.nit @@ -20,6 +20,7 @@ module flow import scope redef class ToolContext + # Run `APropdef::do_flow` on each propdef var flow_phase: Phase = new FlowPhase(self, [scope_phase]) end diff --git a/src/semantize/local_var_init.nit b/src/semantize/local_var_init.nit index 4b3fd52..82fa0a4 100644 --- a/src/semantize/local_var_init.nit +++ b/src/semantize/local_var_init.nit @@ -21,6 +21,7 @@ module local_var_init import flow redef class ToolContext + # Run `APropdef::do_local_var_init` on each propdef var local_var_init_phase: Phase = new LocalVarInitPhase(self, [flow_phase]) end diff --git a/src/semantize/scope.nit b/src/semantize/scope.nit index ad6bcd8..6b4e973 100644 --- a/src/semantize/scope.nit +++ b/src/semantize/scope.nit @@ -20,6 +20,7 @@ module scope import phase redef class ToolContext + # Run `APropdef::do_scope` on each propdef. var scope_phase: Phase = new ScopePhase(self, null) end diff --git a/src/semantize/typing.nit b/src/semantize/typing.nit index 1dfaec6..343edbd 100644 --- a/src/semantize/typing.nit +++ b/src/semantize/typing.nit @@ -266,8 +266,13 @@ private class TypeVisitor #debug("recv: {recvtype} (aka {unsafe_type})") if recvtype isa MNullType then - self.error(node, "Error: Method '{name}' call on 'null'.") - return null + # `null` only accepts some methods of object. + if name == "==" or name == "!=" or name == "is_same_instance" then + unsafe_type = mmodule.object_type.as_nullable + else + self.error(node, "Error: Method '{name}' call on 'null'.") + return null + end end var mproperty = self.try_get_mproperty_by_name2(node, unsafe_type, name) @@ -604,6 +609,10 @@ redef class AAttrPropdef var nblock = self.n_block if nblock != null then v.visit_stmt(nblock) + if not nblock.after_flow_context.is_unreachable then + # We reach the end of the init without having a return, it is bad + v.error(self, "Control error: Reached end of block (a 'return' with a value was expected).") + end end end end @@ -756,11 +765,6 @@ redef class AReassignFormExpr self.read_type = readtype - if readtype isa MNullType then - v.error(self, "Error: Method '{reassign_name}' call on 'null'.") - return null - end - var callsite = v.get_method(self, readtype, reassign_name, false) if callsite == null then return null # Skip error self.reassign_callsite = callsite @@ -975,7 +979,7 @@ redef class AForExpr is_col = true end - if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type, objcla.mclass_type.as_nullable])) then + if mapit_cla != null and v.is_subtype(ittype, mapit_cla.get_mtype([objcla.mclass_type.as_nullable, objcla.mclass_type.as_nullable])) then # Map Iterator var coltype = ittype.supertype_to(v.mmodule, v.anchor, mapit_cla) var variables = self.variables @@ -1243,7 +1247,7 @@ redef class AArrayExpr if mtype == null then mtype = v.merge_types(self, mtypes) end - if mtype == null then + if mtype == null or mtype isa MNullType then v.error(self, "Type Error: ambiguous array type {mtypes.join(" ")}") return end @@ -1407,10 +1411,6 @@ redef class ASendExpr var name = self.property_name if recvtype == null then return # Forward error - if recvtype isa MNullType then - v.error(self, "Error: Method '{name}' call on 'null'.") - return - end var callsite = v.get_method(self, recvtype, name, self.n_expr isa ASelfExpr) if callsite == null then return @@ -1554,10 +1554,6 @@ redef class ASendReassignFormExpr var name = self.property_name if recvtype == null then return # Forward error - if recvtype isa MNullType then - v.error(self, "Error: Method '{name}' call on 'null'.") - return - end var for_self = self.n_expr isa ASelfExpr var callsite = v.get_method(self, recvtype, name, for_self) diff --git a/src/test_docdown.nit b/src/test_docdown.nit index 71a8669..e092371 100644 --- a/src/test_docdown.nit +++ b/src/test_docdown.nit @@ -20,7 +20,7 @@ import highlight import docdown redef class ModelBuilder - fun test_markdown(page: HTMLTag, mmodule: MModule) + fun do_test_markdown(page: HTMLTag, mmodule: MModule) do page.add_raw_html "

module {mmodule}

" var mdoc = mmodule.mdoc @@ -115,13 +115,13 @@ if opt_full.value then page.add mdoc.full_markdown end for m in g.mmodules do - modelbuilder.test_markdown(page, m) + modelbuilder.do_test_markdown(page, m) end end end else for m in mmodules do - modelbuilder.test_markdown(page, m) + modelbuilder.do_test_markdown(page, m) end end diff --git a/src/testing/testing_doc.nit b/src/testing/testing_doc.nit index 2bdbb02..e975d85 100644 --- a/src/testing/testing_doc.nit +++ b/src/testing/testing_doc.nit @@ -299,35 +299,28 @@ class DocUnit var block: String end -class SearchAssertVisitor - super Visitor - var foundit = false - redef fun visit(node) - do - if foundit then - return - else if node isa AAssertExpr then - foundit = true - return - else - node.visit_all(self) - end - end -end - redef class ModelBuilder + # Total number analyzed `MEntity` var total_entities = 0 + + # The number of `MEntity` that have some documentation var doc_entities = 0 + + # The total number of executed docunits var unit_entities = 0 + + # The number failed docunits var failed_entities = 0 + # Extracts and executes all the docunits in the `mmodule` + # Returns a JUnit-compatible `` XML element that contains the results of the executions. fun test_markdown(mmodule: MModule): HTMLTag do var ts = new HTMLTag("testsuite") toolcontext.info("nitunit: doc-unit {mmodule}", 2) - if not mmodule2nmodule.has_key(mmodule) then return ts - var nmodule = mmodule2nmodule[mmodule] + var nmodule = mmodule2node(mmodule) + if nmodule == null then return ts # usualy, only the original module must be imported in the unit test. var o = mmodule diff --git a/src/toolcontext.nit b/src/toolcontext.nit index 4547fb7..021c2c7 100644 --- a/src/toolcontext.nit +++ b/src/toolcontext.nit @@ -24,6 +24,7 @@ import opts import location import version import template +import more_collections # A warning or an error class Message @@ -111,6 +112,25 @@ class ToolContext # Set this value to `true` if you need to keep the program going in case of error. var keep_going = false is writable + # List of tags per source-file whose warnings are not displayed. + # + # Initially empty, it is up to the toll to fill it. + # The tag "all" means all warnings and advices. + var warning_blacklist = new MultiHashMap[SourceFile, String] + + # Is the source-file of `l` associated with `tag` in `warning_blacklist`? + # + # currently returns `false` if `l` is null or does not have a source-file. + fun is_warning_blacklisted(l: nullable Location, tag: String): Bool + do + if l == null then return false + var f = l.file + if f == null then return false + var tags = warning_blacklist.get_or_null(f) + if tags == null then return false + return tags.has("all") or tags.has(tag) + end + # Output all current stacked messages and display total error informations # # Return true if no errors occurred. @@ -181,6 +201,7 @@ class ToolContext do if opt_warning.value.has("no-{tag}") then return if not opt_warning.value.has(tag) and opt_warn.value == 0 then return + if is_warning_blacklisted(l, tag) then return messages.add(new Message(l, tag, text)) warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors @@ -203,6 +224,7 @@ class ToolContext do if opt_warning.value.has("no-{tag}") then return if not opt_warning.value.has(tag) and opt_warn.value <= 1 then return + if is_warning_blacklisted(l, tag) then return messages.add(new Message(l, tag, text)) warning_count = warning_count + 1 if opt_stop_on_first_error.value then check_errors @@ -220,28 +242,30 @@ class ToolContext # # Stops execution and prints errors if the program isn't available or didn't end correctly fun exec_and_check(args: Array[String], error: String) - do - var prog = args.first - args.remove_at 0 - - # Is the wanted program available? - var proc_which = new IProcess.from_a("which", [prog]) - proc_which.wait - var res = proc_which.status - if res != 0 then - print "{error}: executable \"{prog}\" not found" - exit 1 - end - - # Execute the wanted program - var proc = new Process.from_a(prog, args) - proc.wait - res = proc.status - if res != 0 then - print "{error}: execution of \"{prog} {args.join(" ")}\" failed" - exit 1 - end - end + do + info("+ {args.join(" ")}", 2) + + var prog = args.first + args.remove_at 0 + + # Is the wanted program available? + var proc_which = new IProcess.from_a("which", [prog]) + proc_which.wait + var res = proc_which.status + if res != 0 then + print "{error}: executable \"{prog}\" not found" + exit 1 + end + + # Execute the wanted program + var proc = new Process.from_a(prog, args) + proc.wait + res = proc.status + if res != 0 then + print "{error}: execution of \"{prog} {args.join(" ")}\" failed" + exit 1 + end + end # Global OptionContext var option_context = new OptionContext diff --git a/src/vm.nit b/src/vm.nit index ba4d7ff..17d2d34 100644 --- a/src/vm.nit +++ b/src/vm.nit @@ -21,7 +21,7 @@ import interpreter::naive_interpreter import perfect_hashing redef class ModelBuilder - redef fun run_naive_interpreter(mainmodule: MModule, arguments: Array[String]) + fun run_virtual_machine(mainmodule: MModule, arguments: Array[String]) do var time0 = get_time self.toolcontext.info("*** NITVM STARTING ***", 1) diff --git a/tests/base_array.nit b/tests/base_array.nit index f37dd3a..3071768 100644 --- a/tests/base_array.nit +++ b/tests/base_array.nit @@ -25,3 +25,4 @@ for i in a do end #alt1# var b = [10, true] +#alt2# var c = [null, null] diff --git a/tests/base_attr_init_val_block.nit b/tests/base_attr_init_val_block.nit index 6d2badc..fdafa3d 100644 --- a/tests/base_attr_init_val_block.nit +++ b/tests/base_attr_init_val_block.nit @@ -24,7 +24,7 @@ class A 2.output var res = a if res == 10 then res = 20 - return res + return res #alt1# return #alt2# end end diff --git a/tests/base_null.nit b/tests/base_null.nit new file mode 100644 index 0000000..78f362a --- /dev/null +++ b/tests/base_null.nit @@ -0,0 +1,42 @@ +# 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 + +class A + redef fun output do 10.output +end + +var a: Object = new A +var na: nullable Object = new A +var nn: nullable Object = null + +(null == a).output +(null == na).output +(null == nn).output +(null == null).output + +'\n'.output + +null.is_same_instance(a).output +null.is_same_instance(na).output +null.is_same_instance(nn).output +null.is_same_instance(null).output + +'\n'.output + +(null != a).output +(null != na).output +(null != nn).output +(null != null).output diff --git a/tests/bench_add_all.nit b/tests/bench_add_all.nit new file mode 100644 index 0000000..733ac9b --- /dev/null +++ b/tests/bench_add_all.nit @@ -0,0 +1,30 @@ +# 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. + +# Performance test for Array::add_all + +import standard::collection + +var a = new Array[Numeric] +var b = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] +for i in 10000.times do + a.add_all b +end + +var c = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0] +for i in 10000.times do + a.add_all c +end + +a.length.output diff --git a/tests/neo_doxygen_dump.args b/tests/neo_doxygen_dump.args index abd29fc..21f45e7 100644 --- a/tests/neo_doxygen_dump.args +++ b/tests/neo_doxygen_dump.args @@ -6,3 +6,4 @@ --src-lang java -- root-namespace ../contrib/neo_doxygen/tests/root-namespace/xml --src-lang java -- inner-class ../contrib/neo_doxygen/tests/inner-class/xml -- python-def ../contrib/neo_doxygen/tests/python-def/xml +--src-lang python -- python-def ../contrib/neo_doxygen/tests/python-def/xml diff --git a/tests/nitg-g.skip b/tests/nitg-g.skip index d25129d..0d0961d 100644 --- a/tests/nitg-g.skip +++ b/tests/nitg-g.skip @@ -1,6 +1,8 @@ nitc nitdoc nitlight +neo_doxygen_descriptions +neo_doxygen_doc_module_class neo_doxygen_dump neo_doxygen_file_compound neo_doxygen_graph_empty_project diff --git a/tests/nitpretty.args b/tests/nitpretty.args index 61b0157..77c26ea 100644 --- a/tests/nitpretty.args +++ b/tests/nitpretty.args @@ -1,3 +1,34 @@ +--skip-empty test_pretty/test_mod1.nit +--skip-empty test_pretty/test_mod2.nit +--skip-empty test_pretty/test_mod3.nit +--skip-empty test_pretty/test_class1.nit +--skip-empty test_pretty/test_class2.nit +--skip-empty test_pretty/test_class3.nit +--skip-empty test_pretty/test_prop1.nit +--skip-empty test_pretty/test_prop2.nit +--skip-empty test_pretty/test_prop3.nit +--skip-empty test_pretty/test_loop1.nit +--skip-empty test_pretty/test_loop2.nit +--skip-empty test_pretty/test_loop3.nit +--skip-empty test_pretty/test_call1.nit +--skip-empty test_pretty/test_call2.nit +--skip-empty test_pretty/test_if1.nit +--skip-empty test_pretty/test_if2.nit +--skip-empty test_pretty/test_if3.nit +--skip-empty test_pretty/test_op1.nit +--skip-empty test_pretty/test_op2.nit +--skip-empty test_pretty/test_op3.nit +--skip-empty test_pretty/test_extern1.nit +--skip-empty test_pretty/test_attr1.nit +--skip-empty test_pretty/test_attr2.nit +--skip-empty test_pretty/test_comments1.nit +--skip-empty test_pretty/test_indent1.nit +--skip-empty test_pretty/test_prims1.nit +--skip-empty test_pretty/test_annot1.nit +--skip-empty --break-strings test_pretty/test_prop1.nit +--skip-empty --break-strings test_pretty/test_indent1.nit +--skip-empty --inline-do test_pretty/test_prop1.nit +--skip-empty --inline-do test_pretty/test_indent1.nit test_pretty/test_mod1.nit test_pretty/test_mod2.nit test_pretty/test_mod3.nit @@ -25,3 +56,7 @@ test_pretty/test_comments1.nit test_pretty/test_indent1.nit test_pretty/test_prims1.nit test_pretty/test_annot1.nit +--break-strings test_pretty/test_prop1.nit +--break-strings test_pretty/test_indent1.nit +--inline-do test_pretty/test_prop1.nit +--inline-do test_pretty/test_indent1.nit diff --git a/tests/nitvm.skip b/tests/nitvm.skip index 329751f..a78f621 100644 --- a/tests/nitvm.skip +++ b/tests/nitvm.skip @@ -3,6 +3,7 @@ shoot_logic bench_ nit_args1 nit_args3 +nit_args4 nitvm_args1 nitvm_args3 nitc_args1 @@ -10,10 +11,15 @@ nitc_args3 nitc_args5 nitc_args6 nitc_args8 -test_markdown_args1 +nitunit_args +test_docdown_args pep8analysis -test_android_platform -android -nitcc_parser_gen -mnit emscripten +nitserial_args +nitunit_args +nitpretty_args +hamming_number +hailstone +test_map +nitls +nituml diff --git a/tests/sav/base_array_alt2.res b/tests/sav/base_array_alt2.res new file mode 100644 index 0000000..e9c5053 --- /dev/null +++ b/tests/sav/base_array_alt2.res @@ -0,0 +1 @@ +alt/base_array_alt2.nit:28,9--20: Type Error: ambiguous array type null null diff --git a/tests/sav/base_attr_init_val_block_alt1.res b/tests/sav/base_attr_init_val_block_alt1.res new file mode 100644 index 0000000..e50539b --- /dev/null +++ b/tests/sav/base_attr_init_val_block_alt1.res @@ -0,0 +1 @@ +alt/base_attr_init_val_block_alt1.nit:27,3--8: Error: Return without value in a function. diff --git a/tests/sav/base_attr_init_val_block_alt2.res b/tests/sav/base_attr_init_val_block_alt2.res new file mode 100644 index 0000000..8545480 --- /dev/null +++ b/tests/sav/base_attr_init_val_block_alt2.res @@ -0,0 +1 @@ +alt/base_attr_init_val_block_alt2.nit:23,6: Control error: Reached end of block (a 'return' with a value was expected). diff --git a/tests/sav/base_null.res b/tests/sav/base_null.res new file mode 100644 index 0000000..80acf2a --- /dev/null +++ b/tests/sav/base_null.res @@ -0,0 +1,16 @@ +base_null.nit:28,2--13: Warning: expression is not null, since it is a `null`. +base_null.nit:42,2--13: Warning: expression is not null, since it is a `null`. +false +false +true +true + +false +false +true +true + +true +true +false +false diff --git a/tests/sav/bench_add_all.res b/tests/sav/bench_add_all.res new file mode 100644 index 0000000..87766d8 --- /dev/null +++ b/tests/sav/bench_add_all.res @@ -0,0 +1 @@ +200000 diff --git a/tests/sav/error_annot_c_compiler_alt5.res b/tests/sav/error_annot_c_compiler_alt5.res index 63c96c0..2251e01 100644 --- a/tests/sav/error_annot_c_compiler_alt5.res +++ b/tests/sav/error_annot_c_compiler_alt5.res @@ -1,2 +1 @@ -Caught signal : Aborted alt/error_annot_c_compiler_alt5.nit:21,57--79: Annotation error: Something went wrong executing the argument of annotation "c_compiler_option", make sure the command is valid. diff --git a/tests/sav/fixme/nitpretty_args22.res b/tests/sav/fixme/nitpretty_args22.res deleted file mode 100644 index 4ad3dc3..0000000 --- a/tests/sav/fixme/nitpretty_args22.res +++ /dev/null @@ -1 +0,0 @@ -UNDEFINED diff --git a/tests/sav/fixme/nitpretty_args48.res b/tests/sav/fixme/nitpretty_args48.res new file mode 100644 index 0000000..c3a432c --- /dev/null +++ b/tests/sav/fixme/nitpretty_args48.res @@ -0,0 +1,61 @@ +# 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. + +var a = 1 +var b = 2 + +# 0 +if a == b then # 1 + # 2 +else # 3 + # 4 +end # 5 + +if a == b then print a # printing a + +if a == b then + print a # printing a +end + +if a == b then print a + # end + +if a == b then a = b + +if a == b then end + +if a == b then end + +if a != b then a = b + + +if a > b then + a = b +else + a = b +end + +if a < b then + a = b +else if a == b then + a = b +end + +if a < b then + a = b +else if a == b then + a = b +else + a = b +end diff --git a/tests/sav/fixme/nitpretty_args58.res b/tests/sav/fixme/nitpretty_args58.res new file mode 100644 index 0000000..2e6ac9a --- /dev/null +++ b/tests/sav/fixme/nitpretty_args58.res @@ -0,0 +1,51 @@ +# 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. + +module test_annot1 is platform("android") + +class A + fun goo is intern + + # test + fun foo is a, b + fun bar is a, b do print "1" + fun baz is + a + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + do print "2" +end + +class B + fun foo is a, b + + + fun bar is a, b + do print "1" + + fun baz is a, b + do + bar + print "2" + end + + fun gaz is + a + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + + do + bar + print "2" + end + +end diff --git a/tests/sav/neo_doxygen_descriptions.res b/tests/sav/neo_doxygen_descriptions.res new file mode 100644 index 0000000..e69de29 diff --git a/tests/sav/neo_doxygen_doc_module_class.res b/tests/sav/neo_doxygen_doc_module_class.res new file mode 100644 index 0000000..e69de29 diff --git a/tests/sav/neo_doxygen_dump.res b/tests/sav/neo_doxygen_dump.res index e043e01..74384a4 100644 --- a/tests/sav/neo_doxygen_dump.res +++ b/tests/sav/neo_doxygen_dump.res @@ -25,7 +25,7 @@ -h, --help Show the help (this page). - --src-lang The programming language to assume when processing chunk in the + --src-lang The programming language to assume when processing chunks in the declarations left as-is by Doxygen. Use `any` (the default) to - disable any language-specific processing. + disable any language-specific processing. diff --git a/tests/sav/neo_doxygen_dump_args4.res b/tests/sav/neo_doxygen_dump_args4.res index 3a3c933..0d6a211 100644 --- a/tests/sav/neo_doxygen_dump_args4.res +++ b/tests/sav/neo_doxygen_dump_args4.res @@ -242,8 +242,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C","mdoc":["An interface."]} Edge @@ -256,8 +256,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C","mdoc":["An interface."]} ---- =to=Entity#36:interfaceorg_1_1example_1_1foo_1_1_c =labels=Array(3): @@ -265,7 +265,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} Edge @@ -278,8 +278,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(3): @@ -287,7 +287,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} Edge @@ -308,8 +308,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass","mdoc":["This class is empty and is only visible in this package."]} Edge @@ -322,8 +322,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass","mdoc":["This class is empty and is only visible in this package."]} ---- =to=Entity#42:classorg_1_1example_1_1foo_1_1_empty_class =labels=Array(3): @@ -344,8 +344,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass","mdoc":["This class is empty and is only visible in this package."]} ---- =to=Entity#0: =labels=Array(3): @@ -1457,7 +1457,7 @@ Edge 8:MPropDef 13:MAttributeDef =properties=JsonObject(5): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.\u000e2\u00080\u0009c"],"is_intro":true} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true} ---- =to=Entity#0: =labels=Array(4): @@ -1481,7 +1481,7 @@ Edge 8:MPropDef 13:MAttributeDef =properties=JsonObject(5): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.\u000e2\u00080\u0009c"],"is_intro":true} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true} ---- =to=Entity#0: =labels=Array(4): @@ -1576,7 +1576,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1608,7 +1608,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} Edge @@ -1622,7 +1622,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1645,7 +1645,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#36:interfaceorg_1_1example_1_1foo_1_1_c =labels=Array(3): @@ -1653,7 +1653,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} Edge @@ -1667,7 +1667,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1699,7 +1699,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} Edge @@ -1713,7 +1713,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#71:interfaceorg_1_1example_1_1foo_1_1_c_1a4e97061eb40b045e820de05b33c43d21 =labels=Array(4): @@ -1722,7 +1722,7 @@ Edge 8:MPropDef 13:MAttributeDef =properties=JsonObject(5): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.\u000e2\u00080\u0009c"],"is_intro":true} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true} Edge @@ -1736,7 +1736,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1768,7 +1768,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} Edge @@ -1782,7 +1782,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#71:interfaceorg_1_1example_1_1foo_1_1_c_1a28ac7ce349ebb3e4a7747a8dd951582b =labels=Array(4): diff --git a/tests/sav/neo_doxygen_dump_args5.res b/tests/sav/neo_doxygen_dump_args5.res index 95633f3..70cda4d 100644 --- a/tests/sav/neo_doxygen_dump_args5.res +++ b/tests/sav/neo_doxygen_dump_args5.res @@ -242,8 +242,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C","mdoc":["An interface."]} Edge @@ -256,8 +256,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C","mdoc":["An interface."]} ---- =to=Entity#36:interfaceorg_1_1example_1_1foo_1_1_c =labels=Array(3): @@ -265,7 +265,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} Edge @@ -278,8 +278,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:1,1--1,1","name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(3): @@ -287,7 +287,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} Edge @@ -308,8 +308,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass","mdoc":["This class is empty and is only visible in this package."]} Edge @@ -322,8 +322,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass","mdoc":["This class is empty and is only visible in this package."]} ---- =to=Entity#42:classorg_1_1example_1_1foo_1_1_empty_class =labels=Array(3): @@ -344,8 +344,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/EmptyClass.java:1,1--1,1","name":"EmptyClass","mdoc":["This class is empty and is only visible in this package."]} ---- =to=Entity#0: =labels=Array(3): @@ -1457,7 +1457,7 @@ Edge 8:MPropDef 13:MAttributeDef =properties=JsonObject(5): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.\u000e2\u00080\u0009c"],"is_intro":true} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true} ---- =to=Entity#0: =labels=Array(4): @@ -1481,7 +1481,7 @@ Edge 8:MPropDef 13:MAttributeDef =properties=JsonObject(5): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.\u000e2\u00080\u0009c"],"is_intro":true} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true} ---- =to=Entity#0: =labels=Array(4): @@ -1576,7 +1576,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1608,7 +1608,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} Edge @@ -1622,7 +1622,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1645,7 +1645,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#36:interfaceorg_1_1example_1_1foo_1_1_c =labels=Array(3): @@ -1653,7 +1653,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface"]} +{"kind":"interface","visibility":"public","name":"C","location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","mdoc":["An interface."]} Edge @@ -1667,7 +1667,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1699,7 +1699,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} Edge @@ -1713,7 +1713,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#71:interfaceorg_1_1example_1_1foo_1_1_c_1a4e97061eb40b045e820de05b33c43d21 =labels=Array(4): @@ -1722,7 +1722,7 @@ Edge 8:MPropDef 13:MAttributeDef =properties=JsonObject(5): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.\u000e2\u00080\u0009c"],"is_intro":true} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:25,1---1,1","visibility":"public","name":"THE_ANSWER","mdoc":["\u000e2\u00080\u0009cAnswer to the Ultimate Question of Life, the Universe, and Everything.","\u000e2\u00080\u0009c"],"is_intro":true} Edge @@ -1736,7 +1736,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#0: =labels=Array(4): @@ -1768,7 +1768,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} Edge @@ -1782,7 +1782,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface"]} +{"location":"%SOURCE_DIRECTORY%\/org\/example\/foo\/C.java:21,1--31,1","is_intro":true,"name":"C","mdoc":["An interface."]} ---- =to=Entity#71:interfaceorg_1_1example_1_1foo_1_1_c_1a28ac7ce349ebb3e4a7747a8dd951582b =labels=Array(4): diff --git a/tests/sav/neo_doxygen_dump_args6.res b/tests/sav/neo_doxygen_dump_args6.res index 5436c24..c59297e 100644 --- a/tests/sav/neo_doxygen_dump_args6.res +++ b/tests/sav/neo_doxygen_dump_args6.res @@ -59,7 +59,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace"]} +{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace."]} ---- =to=Entity#0: =labels=Array(4): @@ -91,7 +91,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace"]} +{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace."]} Edge @@ -105,7 +105,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","is_intro":true,"name":"Foo","mdoc":["A class in the root namespace"]} +{"location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","is_intro":true,"name":"Foo","mdoc":["A class in the root namespace."]} ---- =to=Entity#0: =labels=Array(4): @@ -128,7 +128,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","is_intro":true,"name":"Foo","mdoc":["A class in the root namespace"]} +{"location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","is_intro":true,"name":"Foo","mdoc":["A class in the root namespace."]} ---- =to=Entity#9:class_foo =labels=Array(3): @@ -136,7 +136,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace"]} +{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace."]} Edge @@ -157,8 +157,8 @@ Edge 14:root-namespace 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/Foo.java:1,1--1,1","name":"Foo"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/Foo.java:1,1--1,1","name":"Foo","mdoc":["A class in the root namespace."]} Edge @@ -171,8 +171,8 @@ Edge 14:root-namespace 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/Foo.java:1,1--1,1","name":"Foo"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/Foo.java:1,1--1,1","name":"Foo","mdoc":["A class in the root namespace."]} ---- =to=Entity#9:class_foo =labels=Array(3): @@ -180,7 +180,7 @@ Edge 7:MEntity 6:MClass =properties=JsonObject(5): -{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace"]} +{"kind":"class","visibility":"package","name":"Foo","location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","mdoc":["A class in the root namespace."]} Edge @@ -193,8 +193,8 @@ Edge 14:root-namespace 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"%SOURCE_DIRECTORY%\/Foo.java:1,1--1,1","name":"Foo"} +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/Foo.java:1,1--1,1","name":"Foo","mdoc":["A class in the root namespace."]} ---- =to=Entity#0: =labels=Array(3): @@ -202,7 +202,7 @@ Edge 7:MEntity 9:MClassDef =properties=JsonObject(4): -{"location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","is_intro":true,"name":"Foo","mdoc":["A class in the root namespace"]} +{"location":"%SOURCE_DIRECTORY%\/Foo.java:19,1--19,1","is_intro":true,"name":"Foo","mdoc":["A class in the root namespace."]} ---===DONE===--- diff --git a/tests/sav/neo_doxygen_dump_args9.res b/tests/sav/neo_doxygen_dump_args9.res new file mode 100644 index 0000000..1c9cc04 --- /dev/null +++ b/tests/sav/neo_doxygen_dump_args9.res @@ -0,0 +1,391 @@ +Reading ../contrib/neo_doxygen/tests/python-def/xml... Done. +3 files read. +Linking nodes...  Done. +Saving 10 nodes... +---===DONE===--- +Saving 17 edges... +Edge +=type=4:ROOT +=properties=JsonObject(0): +{} +---- +=from=Node +=labels=Array(3): +10:python-def +7:MEntity +8:MProject +=properties=JsonObject(1): +{"name":"python-def"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(1): +{"name":"python-def"} + + +Edge +=type=7:PROJECT +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(1): +{"name":"python-def"} +---- +=to=Node +=labels=Array(3): +10:python-def +7:MEntity +8:MProject +=properties=JsonObject(1): +{"name":"python-def"} + + +Edge +=type=6:PARENT +=properties=JsonObject(0): +{} +---- +=from=Entity#12:namespacefoo +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(4): +{"kind":"namespace","visibility":"","name":"foo","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(1): +{"name":"python-def"} + + +Edge +=type=5:NESTS +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(1): +{"name":"python-def"} +---- +=to=Entity#12:namespacefoo +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(4): +{"kind":"namespace","visibility":"","name":"foo","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} + + +Edge +=type=8:DECLARES +=properties=JsonObject(0): +{} +---- +=from=Entity#12:namespacefoo +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(4): +{"kind":"namespace","visibility":"","name":"foo","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +7:MModule +=properties=JsonObject(2): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","name":"foo"} + + +Edge +=type=10:INTRODUCES +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +7:MModule +=properties=JsonObject(2): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","name":"foo"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MClass +=properties=JsonObject(4): +{"kind":"class","visibility":"public","name":"(self)","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} + + +Edge +=type=7:DEFINES +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +7:MModule +=properties=JsonObject(2): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","name":"foo"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +9:MClassDef +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","is_intro":true,"name":"(self)"} + + +Edge +=type=7:DEFINES +=properties=JsonObject(0): +{} +---- +=from=Entity#47:namespacefoo_1aab1e88a2212b202c20f3c9bd799a1ad4 +=labels=Array(4): +10:python-def +7:MEntity +8:MPropDef +10:MMethodDef +=properties=JsonObject(8): +{"location":"%SOURCE_DIRECTORY%\/foo.py:16,1--20,1","is_intern":false,"is_extern":false,"is_abstract":false,"visibility":"public","name":"bar","mdoc":["A bar function in the foo namespace.","By default, Doxygen recognizes anything in the docstrings as verbatim\ndetailed description."],"is_intro":true} +---- +=to=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +9:MProperty +7:MMethod +=properties=JsonObject(3): +{"visibility":"public","is_init":false,"name":"bar"} + + +Edge +=type=9:SIGNATURE +=properties=JsonObject(0): +{} +---- +=from=Entity#47:namespacefoo_1aab1e88a2212b202c20f3c9bd799a1ad4 +=labels=Array(4): +10:python-def +7:MEntity +8:MPropDef +10:MMethodDef +=properties=JsonObject(8): +{"location":"%SOURCE_DIRECTORY%\/foo.py:16,1--20,1","is_intern":false,"is_extern":false,"is_abstract":false,"visibility":"public","name":"bar","mdoc":["A bar function in the foo namespace.","By default, Doxygen recognizes anything in the docstrings as verbatim\ndetailed description."],"is_intro":true} +---- +=to=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +5:MType +10:MSignature +=properties=JsonObject(0): +{} + + +Edge +=type=7:PROJECT +=properties=JsonObject(0): +{} +---- +=from=Entity#12:namespacefoo +=labels=Array(3): +10:python-def +7:MEntity +6:MGroup +=properties=JsonObject(4): +{"kind":"namespace","visibility":"","name":"foo","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} +---- +=to=Node +=labels=Array(3): +10:python-def +7:MEntity +8:MProject +=properties=JsonObject(1): +{"name":"python-def"} + + +Edge +=type=9:CLASSTYPE +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MClass +=properties=JsonObject(4): +{"kind":"class","visibility":"public","name":"(self)","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} +---- +=to=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +5:MType +10:MClassType +=properties=JsonObject(1): +{"name":"(self)"} + + +Edge +=type=5:CLASS +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +5:MType +10:MClassType +=properties=JsonObject(1): +{"name":"(self)"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MClass +=properties=JsonObject(4): +{"kind":"class","visibility":"public","name":"(self)","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} + + +Edge +=type=9:BOUNDTYPE +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +9:MClassDef +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","is_intro":true,"name":"(self)"} +---- +=to=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +5:MType +10:MClassType +=properties=JsonObject(1): +{"name":"(self)"} + + +Edge +=type=6:MCLASS +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +9:MClassDef +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","is_intro":true,"name":"(self)"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +6:MClass +=properties=JsonObject(4): +{"kind":"class","visibility":"public","name":"(self)","location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1"} + + +Edge +=type=10:INTRODUCES +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +9:MClassDef +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","is_intro":true,"name":"(self)"} +---- +=to=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +9:MProperty +7:MMethod +=properties=JsonObject(3): +{"visibility":"public","is_init":false,"name":"bar"} + + +Edge +=type=14:INTRO_CLASSDEF +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(4): +10:python-def +7:MEntity +9:MProperty +7:MMethod +=properties=JsonObject(3): +{"visibility":"public","is_init":false,"name":"bar"} +---- +=to=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +9:MClassDef +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","is_intro":true,"name":"(self)"} + + +Edge +=type=8:DECLARES +=properties=JsonObject(0): +{} +---- +=from=Entity#0: +=labels=Array(3): +10:python-def +7:MEntity +9:MClassDef +=properties=JsonObject(3): +{"location":"%SOURCE_DIRECTORY%\/foo.py:1,1--1,1","is_intro":true,"name":"(self)"} +---- +=to=Entity#47:namespacefoo_1aab1e88a2212b202c20f3c9bd799a1ad4 +=labels=Array(4): +10:python-def +7:MEntity +8:MPropDef +10:MMethodDef +=properties=JsonObject(8): +{"location":"%SOURCE_DIRECTORY%\/foo.py:16,1--20,1","is_intern":false,"is_extern":false,"is_abstract":false,"visibility":"public","name":"bar","mdoc":["A bar function in the foo namespace.","By default, Doxygen recognizes anything in the docstrings as verbatim\ndetailed description."],"is_intro":true} + + +---===DONE===--- diff --git a/tests/sav/neo_doxygen_file_compound.res b/tests/sav/neo_doxygen_file_compound.res index 9b16eb8..740393b 100644 --- a/tests/sav/neo_doxygen_file_compound.res +++ b/tests/sav/neo_doxygen_file_compound.res @@ -186,8 +186,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} Edge =type=10:INTRODUCES @@ -199,8 +199,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} ---- =to=Entity#12:classa_b_bar =labels=Array(3): @@ -220,8 +220,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} ---- =to=Entity#0: =labels=Array(3): @@ -762,8 +762,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} Edge =type=10:INTRODUCES @@ -775,8 +775,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} ---- =to=Entity#12:classa_b_bar =labels=Array(3): @@ -796,8 +796,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} ---- =to=Entity#0: =labels=Array(3): @@ -1167,8 +1167,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} Edge =type=10:INTRODUCES @@ -1180,8 +1180,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} ---- =to=Entity#8:classbaz =labels=Array(3): @@ -1201,8 +1201,8 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar"} +=properties=JsonObject(3): +{"location":"a\/b\/Bar.java:1,1--1,1","name":"Bar","mdoc":["The first file."]} ---- =to=Entity#0: =labels=Array(3): diff --git a/tests/sav/neo_doxygen_namespace_members.res b/tests/sav/neo_doxygen_namespace_members.res index c785591..753a400 100644 --- a/tests/sav/neo_doxygen_namespace_members.res +++ b/tests/sav/neo_doxygen_namespace_members.res @@ -51,8 +51,8 @@ Edge 3:foo 7:MEntity 6:MGroup -=properties=JsonObject(1): -{"name":"foo"} +=properties=JsonObject(2): +{"name":"foo","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(3): @@ -80,8 +80,8 @@ Edge 3:foo 7:MEntity 6:MGroup -=properties=JsonObject(1): -{"name":"foo"} +=properties=JsonObject(2): +{"name":"foo","mdoc":["A documented namespace."]} Edge =type=8:DECLARES @@ -93,16 +93,16 @@ Edge 3:foo 7:MEntity 6:MGroup -=properties=JsonObject(1): -{"name":"foo"} +=properties=JsonObject(2): +{"name":"foo","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(3): 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"\/dev\/null:1,1--1,1","name":"foo"} +=properties=JsonObject(3): +{"location":"\/dev\/null:1,1--1,1","name":"foo","mdoc":["A documented namespace."]} Edge =type=10:INTRODUCES @@ -114,16 +114,16 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"\/dev\/null:1,1--1,1","name":"foo"} +=properties=JsonObject(3): +{"location":"\/dev\/null:1,1--1,1","name":"foo","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(3): 3:foo 7:MEntity 6:MClass -=properties=JsonObject(4): -{"kind":"class","visibility":"public","name":"(self)","location":"\/dev\/null:1,1--1,1"} +=properties=JsonObject(5): +{"kind":"class","visibility":"public","name":"(self)","mdoc":["A documented namespace."],"location":"\/dev\/null:1,1--1,1"} Edge =type=7:DEFINES @@ -135,16 +135,16 @@ Edge 3:foo 7:MEntity 7:MModule -=properties=JsonObject(2): -{"location":"\/dev\/null:1,1--1,1","name":"foo"} +=properties=JsonObject(3): +{"location":"\/dev\/null:1,1--1,1","name":"foo","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(3): 3:foo 7:MEntity 9:MClassDef -=properties=JsonObject(3): -{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)"} +=properties=JsonObject(4): +{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)","mdoc":["A documented namespace."]} Edge =type=7:DEFINES @@ -179,8 +179,8 @@ Edge 3:foo 7:MEntity 6:MGroup -=properties=JsonObject(1): -{"name":"foo"} +=properties=JsonObject(2): +{"name":"foo","mdoc":["A documented namespace."]} ---- =to=Node =labels=Array(3): @@ -200,8 +200,8 @@ Edge 3:foo 7:MEntity 6:MClass -=properties=JsonObject(4): -{"kind":"class","visibility":"public","name":"(self)","location":"\/dev\/null:1,1--1,1"} +=properties=JsonObject(5): +{"kind":"class","visibility":"public","name":"(self)","mdoc":["A documented namespace."],"location":"\/dev\/null:1,1--1,1"} ---- =to=Entity#0: =labels=Array(4): @@ -231,8 +231,8 @@ Edge 3:foo 7:MEntity 6:MClass -=properties=JsonObject(4): -{"kind":"class","visibility":"public","name":"(self)","location":"\/dev\/null:1,1--1,1"} +=properties=JsonObject(5): +{"kind":"class","visibility":"public","name":"(self)","mdoc":["A documented namespace."],"location":"\/dev\/null:1,1--1,1"} Edge =type=9:BOUNDTYPE @@ -244,8 +244,8 @@ Edge 3:foo 7:MEntity 9:MClassDef -=properties=JsonObject(3): -{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)"} +=properties=JsonObject(4): +{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(4): @@ -266,16 +266,16 @@ Edge 3:foo 7:MEntity 9:MClassDef -=properties=JsonObject(3): -{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)"} +=properties=JsonObject(4): +{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(3): 3:foo 7:MEntity 6:MClass -=properties=JsonObject(4): -{"kind":"class","visibility":"public","name":"(self)","location":"\/dev\/null:1,1--1,1"} +=properties=JsonObject(5): +{"kind":"class","visibility":"public","name":"(self)","mdoc":["A documented namespace."],"location":"\/dev\/null:1,1--1,1"} Edge =type=10:INTRODUCES @@ -287,8 +287,8 @@ Edge 3:foo 7:MEntity 9:MClassDef -=properties=JsonObject(3): -{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)"} +=properties=JsonObject(4): +{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(4): @@ -318,8 +318,8 @@ Edge 3:foo 7:MEntity 9:MClassDef -=properties=JsonObject(3): -{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)"} +=properties=JsonObject(4): +{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)","mdoc":["A documented namespace."]} Edge =type=8:DECLARES @@ -331,8 +331,8 @@ Edge 3:foo 7:MEntity 9:MClassDef -=properties=JsonObject(3): -{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)"} +=properties=JsonObject(4): +{"location":"\/dev\/null:1,1--1,1","is_intro":true,"name":"(self)","mdoc":["A documented namespace."]} ---- =to=Entity#0: =labels=Array(4): diff --git a/tests/sav/nitpretty_args21.res b/tests/sav/nitpretty_args21.res index 9b58418..7a8fe04 100644 --- a/tests/sav/nitpretty_args21.res +++ b/tests/sav/nitpretty_args21.res @@ -78,8 +78,8 @@ extern class TimeT `{time_t`} # Difference in secondes from start (self if the end time) fun difftime(start: TimeT): Float `{ return difftime(recv, start); `} - private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is extern import - Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`} + private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is + extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`} end fun address_is_null: Bool is extern "address_is_null" diff --git a/tests/sav/nitpretty_args25.res b/tests/sav/nitpretty_args25.res index 6638706..e8ba342 100644 --- a/tests/sav/nitpretty_args25.res +++ b/tests/sav/nitpretty_args25.res @@ -15,7 +15,8 @@ class Foo fun bar: Bool do return true - fun foo(other: Foo): Foo do + fun foo(other: Foo): Foo + do if other.bar then return other else @@ -34,7 +35,8 @@ class Foo return nb end - fun gaz: Int do + fun gaz: Int + do if bar then # 3 return 1 else @@ -67,7 +69,9 @@ class Test[E] end end - fun save_those_nodes(nodes: Collection[Object]) do for node in nodes do count(node) + fun save_those_nodes(nodes: Collection[Object]) do + for node in nodes do count(node) + end end fun foo do @@ -78,28 +82,12 @@ fun foo do end end -print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidun" + - "t sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit a" + - "met lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus" + - " eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pell" + - "entesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae" + - " consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae" + - " lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas" + - " turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed phar" + - "etra lacus." +print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." var lorem = "lorem" var ipsum = "ipsum" # for fun -print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, con" + - "sectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius a" + - "t non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisi" + - "s neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}" + - ". Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} pla" + - "cerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus pl" + - "acerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum " + - "augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pell" + - "entesque vitae arcu justo. Aliquam sed pharetra lacus." # ending +print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending var title = "title" var links = new Array[String] # why not? diff --git a/tests/sav/nitpretty_args27.res b/tests/sav/nitpretty_args27.res index 0406b29..422cc2a 100644 --- a/tests/sav/nitpretty_args27.res +++ b/tests/sav/nitpretty_args27.res @@ -15,15 +15,17 @@ module test_annot1 is platform("android") class A + fun goo is intern + + # test fun foo is a, b + fun bar is a, b do print "1" fun baz is a bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb - do - print "2" - end + do print "2" end class B diff --git a/tests/sav/nitpretty_args28.res b/tests/sav/nitpretty_args28.res new file mode 100644 index 0000000..1944090 --- /dev/null +++ b/tests/sav/nitpretty_args28.res @@ -0,0 +1,51 @@ +# 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. + +# comment 1 +class A + type FOO: Discrete + private var foo: FOO # comment + + # comment 2 + var bar: Int = 10 +end + +class B + super A + + redef type FOO: Int + + # comment 3 + redef fun foo do return bar # comment + + redef fun bar + do + return 10 # comment 4 + end + + fun baz do return # comment 5 + protected fun baz2 do end + + fun other: String do + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaa" + end + + fun foo1(arr: Array[String], len: Int, ind: Int): String + do + return "Hello World!" + end +end + +# end diff --git a/tests/sav/nitpretty_args29.res b/tests/sav/nitpretty_args29.res new file mode 100644 index 0000000..4d75c17 --- /dev/null +++ b/tests/sav/nitpretty_args29.res @@ -0,0 +1,127 @@ +# 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. + +class Foo + fun bar: Bool do return true + + fun foo(other: Foo): Foo + do + if other.bar then + return other + else + return self + end + end + + fun baz: Int do + var nb = 0 + + while nb < 10 do + print nb + nb += 1 + end # 1 + + return nb + end + + fun gaz: Int + do + if bar then # 3 + return 1 + else + return -1 # 4 + end + end +end + +class Test[E] + var heap: ArrayHeap[E] + init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator) + + init from(comparator: Comparator[E], items: Collection[E]) do + heap = new ArrayHeap[E].from(comparator, items.to_a) + end + + fun count(k: E): Int do + if heap.has(k) then + return 1 + else + return 0 + end + end + + fun node_at_idx(i: Int, k: E) do + while heap != null do + if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet + break + end + end + end + + fun save_those_nodes(nodes: Collection[Object]) do + for node in nodes do count(node) + end +end + +fun foo do + if last_slash > 0 then + return substring(last_slash + 1, length) + else + return null + end +end + +print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidun" + + "t sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit a" + + "met lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus" + + " eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pell" + + "entesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae" + + " consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae" + + " lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas" + + " turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed phar" + + "etra lacus." + +var lorem = "lorem" +var ipsum = "ipsum" # for fun + +print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, con" + + "sectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius a" + + "t non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisi" + + "s neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}" + + ". Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} pla" + + "cerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus pl" + + "acerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum " + + "augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pell" + + "entesque vitae arcu justo. Aliquam sed pharetra lacus." # ending + +var title = "title" +var links = new Array[String] # why not? + +var body = """ + + + + + + {{{title}}} + + +
+

{{{title}}}

+
    +
  • {{{links.join("
  • \n\t\t\t
  • ")}}}
  • +
+
+ +""" diff --git a/tests/sav/nitpretty_args30.res b/tests/sav/nitpretty_args30.res new file mode 100644 index 0000000..57f7966 --- /dev/null +++ b/tests/sav/nitpretty_args30.res @@ -0,0 +1,48 @@ +# 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. + +# comment 1 +class A + type FOO: Discrete + private var foo: FOO # comment + + # comment 2 + var bar: Int = 10 +end + +class B + super A + + redef type FOO: Int + + # comment 3 + redef fun foo do return bar # comment + + redef fun bar do + return 10 # comment 4 + end + + fun baz do return # comment 5 + protected fun baz2 do end + + fun other: String do + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + + fun foo1(arr: Array[String], len: Int, ind: Int): String do + return "Hello World!" + end +end + +# end diff --git a/tests/sav/nitpretty_args31.res b/tests/sav/nitpretty_args31.res new file mode 100644 index 0000000..99d53dd --- /dev/null +++ b/tests/sav/nitpretty_args31.res @@ -0,0 +1,109 @@ +# 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. + +class Foo + fun bar: Bool do return true + + fun foo(other: Foo): Foo do + if other.bar then + return other + else + return self + end + end + + fun baz: Int do + var nb = 0 + + while nb < 10 do + print nb + nb += 1 + end # 1 + + return nb + end + + fun gaz: Int do + if bar then # 3 + return 1 + else + return -1 # 4 + end + end +end + +class Test[E] + var heap: ArrayHeap[E] + init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator) + + init from(comparator: Comparator[E], items: Collection[E]) do + heap = new ArrayHeap[E].from(comparator, items.to_a) + end + + fun count(k: E): Int do + if heap.has(k) then + return 1 + else + return 0 + end + end + + fun node_at_idx(i: Int, k: E) do + while heap != null do + if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet + break + end + end + end + + fun save_those_nodes(nodes: Collection[Object]) do + for node in nodes do count(node) + end +end + +fun foo do + if last_slash > 0 then + return substring(last_slash + 1, length) + else + return null + end +end + +print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." + +var lorem = "lorem" +var ipsum = "ipsum" # for fun + +print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending + +var title = "title" +var links = new Array[String] # why not? + +var body = """ + + + + + + {{{title}}} + + +
+

{{{title}}}

+
    +
  • {{{links.join("
  • \n\t\t\t
  • ")}}}
  • +
+
+ +""" diff --git a/tests/sav/nitpretty_args32.res b/tests/sav/nitpretty_args32.res new file mode 100644 index 0000000..f55be8f --- /dev/null +++ b/tests/sav/nitpretty_args32.res @@ -0,0 +1,18 @@ +# 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. + + + +# An empty module + diff --git a/tests/sav/nitpretty_args33.res b/tests/sav/nitpretty_args33.res new file mode 100644 index 0000000..76149a7 --- /dev/null +++ b/tests/sav/nitpretty_args33.res @@ -0,0 +1,24 @@ +# 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. + +# Testing only imports + +# Module comment +module test_mod2 # second comment + +import standard::kernel +#import standard::string + +import template # no need for string +# import standard \ No newline at end of file diff --git a/tests/sav/nitpretty_args34.res b/tests/sav/nitpretty_args34.res new file mode 100644 index 0000000..6ee57a1 --- /dev/null +++ b/tests/sav/nitpretty_args34.res @@ -0,0 +1,25 @@ +# 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 simple module +module test_mod3 + +# before +print "Hello World" # comment +# after + +# end + + + diff --git a/tests/sav/nitpretty_args35.res b/tests/sav/nitpretty_args35.res new file mode 100644 index 0000000..ed30bc2 --- /dev/null +++ b/tests/sav/nitpretty_args35.res @@ -0,0 +1,24 @@ +# 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. + +# comment 1 +interface A end + +abstract class B # comment 2 +end + +class C end # comment 3 + +enum D end # comment 4 + diff --git a/tests/sav/nitpretty_args36.res b/tests/sav/nitpretty_args36.res new file mode 100644 index 0000000..2a61cf1 --- /dev/null +++ b/tests/sav/nitpretty_args36.res @@ -0,0 +1,25 @@ +# 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. + +module test_class2 + + +# comment +class A end + +class B[T] # comment +end + +private class C[U, V: B[A]] end # comment + diff --git a/tests/sav/nitpretty_args37.res b/tests/sav/nitpretty_args37.res new file mode 100644 index 0000000..feedd55 --- /dev/null +++ b/tests/sav/nitpretty_args37.res @@ -0,0 +1,39 @@ +# 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. + +# comment +class A end + +class B[T] # comment + # comment + super A # comment + + + super C[A, B[A]] + # comment +end + +class C[U, V: B[A]] end # comment + +class D super A end # comment + +class E + + + + super A # comment +end + +# end + diff --git a/tests/sav/nitpretty_args38.res b/tests/sav/nitpretty_args38.res new file mode 100644 index 0000000..31e90fc --- /dev/null +++ b/tests/sav/nitpretty_args38.res @@ -0,0 +1,46 @@ +# 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. + +# comment 1 +class A + type FOO: Discrete + private var foo: FOO # comment + # comment 2 + var bar: Int = 10 +end + +class B + super A + + redef type FOO: Int + # comment 3 + redef fun foo do return bar # comment + redef fun bar + do + return 10 # comment 4 + end + fun baz do return # comment 5 + protected fun baz2 do end + fun other: String do + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + + fun foo1(arr: Array[String], len: Int, ind: Int): String + do + return "Hello World!" + end +end + +# end + diff --git a/contrib/neo_doxygen/src/doxml/doc.nit b/tests/sav/nitpretty_args39.res similarity index 68% rename from contrib/neo_doxygen/src/doxml/doc.nit rename to tests/sav/nitpretty_args39.res index da9f09e..e48e1da 100644 --- a/contrib/neo_doxygen/src/doxml/doc.nit +++ b/tests/sav/nitpretty_args39.res @@ -12,21 +12,21 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Documentation reading. -module doxml::doc +class A + fun foo(a, b: Int): Bool do return true # 1 -import listener + fun foo2(a, b: Int): Bool do return true # 2 -# Processes documentation. -class DocListener - super TextListener + fun foo3(a, b: Int): Bool do return true - # The read documentation. - var doc = new JsonArray is writable - - redef fun end_listening do - super - var line = to_s.trim - if not line.is_empty then doc.add(line) + fun foo4(a, b: Int): Bool do + var res = true # 3 + return res # 4 end + + fun foo5 do end # 5 + # fun foo6 do end end + +# end + diff --git a/tests/sav/nitpretty_args40.res b/tests/sav/nitpretty_args40.res new file mode 100644 index 0000000..59e2e25 --- /dev/null +++ b/tests/sav/nitpretty_args40.res @@ -0,0 +1,35 @@ +# 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. + +class A + fun foo(aaaaaaaaaaaaaa, + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb: Int): Bool do return true # comment + + fun foo2(a, b: Int): Bool do return true # comment + + fun foo3(a, b: Int): Bool do # comment + return true # comment + end # comment + + fun foo4(a, b: Int): Bool do # comment + var res = true # comment + return res # comment + end # comment + + fun foo5 do end # comment + + fun foo6(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Int) do print 1 + +end # comment \ No newline at end of file diff --git a/tests/sav/nitpretty_args41.res b/tests/sav/nitpretty_args41.res new file mode 100644 index 0000000..1be308b --- /dev/null +++ b/tests/sav/nitpretty_args41.res @@ -0,0 +1,34 @@ +# 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. + +var a = 1 +var b = 2 + +while a != b do # comment 1 + # comment 2 + var tmp = a + a = b + b = tmp + # comment 3 +end + +# comment 4 +while a != b do a = b # comment 5 + +while a != b do + # comment 6 +end # comment 7 + +# end + diff --git a/tests/sav/nitpretty_args42.res b/tests/sav/nitpretty_args42.res new file mode 100644 index 0000000..6b49ffa --- /dev/null +++ b/tests/sav/nitpretty_args42.res @@ -0,0 +1,47 @@ +# 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. + +var a = 0 +var b = 2 + +do # comment 1 + # comment 2 + var tmp = a + a = b + b = tmp + # comment 3 +end + +# comment 4 +do a = b # comment 5 + +do + # comment 6 +end + +if a > b then loop print a # test + +if a > b then loop print a + + +if a > b then loop print a + + +if a > b then + loop + # comment 7 + print a + end +end + diff --git a/tests/sav/nitpretty_args43.res b/tests/sav/nitpretty_args43.res new file mode 100644 index 0000000..0135aa1 --- /dev/null +++ b/tests/sav/nitpretty_args43.res @@ -0,0 +1,27 @@ +# 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. + +var a = 0 + +for i in [1, 2, 3] do # comment 1 + # comment 2 + a += i +end + +# comment 4 +for i in [1..3] do a += i # comment 5 + +for i in [1..3[ do + # comment 6 +end diff --git a/tests/sav/nitpretty_args44.res b/tests/sav/nitpretty_args44.res new file mode 100644 index 0000000..2cd014e --- /dev/null +++ b/tests/sav/nitpretty_args44.res @@ -0,0 +1,36 @@ +# 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. + +class A + fun foo do end + fun bar(a: Int): Int do return 1 + fun baz(a, b: Int) do end + fun gaz(a: Int, b: Float...) do end +end + +fun top1 do end +fun top2(a: Int) do end + +# comment 1 +var a = new A # comment 2 +a.foo # comment 3 +a.bar 1 # comment 4 +a.baz(1, 2) # comment 5 +top1 # comment 6 +top2 10 # comment 7 + +print 10 # comment 8 + +var b = a.bar(1) + diff --git a/tests/sav/nitpretty_args45.res b/tests/sav/nitpretty_args45.res new file mode 100644 index 0000000..789abdd --- /dev/null +++ b/tests/sav/nitpretty_args45.res @@ -0,0 +1,39 @@ +# 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. + +class A + var attr: Int + fun foo1=(i: Int) do end + fun foo2=(i, j: Int) do end + fun [](a: Int): Int is abstract + fun []=(a, b: Int) do end +end + +class B + fun [](a, b: Int): Int is abstract + fun []=(a, b, c: Int) do end +end + +# comment 1 +var a = new A(10) # comment 2 + +a.foo1 = 10 # comment 3 +a.foo2(1) = 10 # comment 4 +print a[1] # comment 5 +a[1] = 2 # comment 6 +a[2] += 3 # comment 7 + +var b = new B +print b[1, 2] +b[1, 2] = 10 diff --git a/tests/sav/nitpretty_args46.res b/tests/sav/nitpretty_args46.res new file mode 100644 index 0000000..60bf5ef --- /dev/null +++ b/tests/sav/nitpretty_args46.res @@ -0,0 +1,50 @@ +# 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. + +var a = 1 +var b = 2 + +if a == b then a = b + +if a != b then + a = b + a = b +end + +if a > b then + b = a + a = b +else + a = b + a = b +end + +if a < b then + a = b + a = b +else if a == b then + b = a + a = b +end + +if a < b then + a = b + a = b +else if a == b then + b = b + a = b +else + a = b + a = b +end diff --git a/tests/sav/nitpretty_args47.res b/tests/sav/nitpretty_args47.res new file mode 100644 index 0000000..12d8db5 --- /dev/null +++ b/tests/sav/nitpretty_args47.res @@ -0,0 +1,81 @@ +# 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. + +# comment +var a = 1 # comment +# comment +var b = 2 # comment + +# comment +if a == b then a = b # comment + +# comment +if a != b then # comment + # comment + a = b # comment + # comment + a = b # comment + # comment +end # comment + +# comment +if a > b then # comment + # comment + b = a # comment + # comment + a = b # comment + # comment +else # comment + # comment + a = b # comment + # comment + a = b # comment + # comment +end # comment + +# comment +if a < b then # comment + # comment + a = b # comment + # comment + a = b # comment + # comment +else if a == b then # comment + # comment + b = a # comment + # comment + a = b # comment + # comment +end # comment + +# comment +if a < b then # comment + # comment + a = b # comment + # comment + a = b # comment + # comment +else if a == b then # comment + # comment + b = b # comment + # comment + a = b # comment + # comment +else # comment + # comment + a = b # comment + # comment + a = b # comment + # comment +end # comment \ No newline at end of file diff --git a/tests/sav/nitpretty_args48.res b/tests/sav/nitpretty_args48.res new file mode 100644 index 0000000..bfde8c2 --- /dev/null +++ b/tests/sav/nitpretty_args48.res @@ -0,0 +1,59 @@ +# 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. + +var a = 1 +var b = 2 + +# 0 +if a == b then # 1 + # 2 +else # 3 + # 4 +end # 5 + +if a == b then print a # printing a + +if a == b then + print a # printing a +end + +if a == b then print a # end + +if a == b then a = b + +if a == b then end + +if a == b then end + +if a != b then a = b + +if a > b then + a = b +else + a = b +end + +if a < b then + a = b +else if a == b then + a = b +end + +if a < b then + a = b +else if a == b then + a = b +else + a = b +end diff --git a/tests/sav/nitpretty_args49.res b/tests/sav/nitpretty_args49.res new file mode 100644 index 0000000..4fec81e --- /dev/null +++ b/tests/sav/nitpretty_args49.res @@ -0,0 +1,21 @@ +# 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. + +var a: nullable Int = 1 +var b: nullable Int = a.as(Int) +var c: nullable Int = a.as(not null) + +assert c isa Int +assert test1: c isa Int +assert test2: c isa Int else abort diff --git a/tests/sav/nitpretty_args50.res b/tests/sav/nitpretty_args50.res new file mode 100644 index 0000000..c428dcb --- /dev/null +++ b/tests/sav/nitpretty_args50.res @@ -0,0 +1,31 @@ +# 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. + +var a = 1 +var b = 2 + +assert a == 2 +assert not a < 2 # comment 1 +assert a > 2 and b >= 2 +assert b != 2 or a <= 2 +assert b != null # comment 2 + +# comment 3 +print a + b +print a - b # comment 4 +print a * b +print a / b +print a % b + +print -a # comment 5 \ No newline at end of file diff --git a/tests/sav/nitpretty_args51.res b/tests/sav/nitpretty_args51.res new file mode 100644 index 0000000..90425ff --- /dev/null +++ b/tests/sav/nitpretty_args51.res @@ -0,0 +1,27 @@ +# 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. + +var a = 1 +var b = 2 + +assert not a < 2 and (a == b or a > b) # comment 1 +assert not a < 2 and (a == b or ((a > b) or a <= b)) +assert (a > 2 and b >= 2) +assert (b >= 2) + +# comment 3 +var c = a + (b - a) +var d = (a - b) + c # comment 4 +var e = (-a) # comment 5 +var f = -(a - c) diff --git a/tests/sav/fixme/nitpretty_args21.res b/tests/sav/nitpretty_args52.res similarity index 93% rename from tests/sav/fixme/nitpretty_args21.res rename to tests/sav/nitpretty_args52.res index f4a3324..415c33f 100644 --- a/tests/sav/fixme/nitpretty_args21.res +++ b/tests/sav/nitpretty_args52.res @@ -34,9 +34,8 @@ fun errno: Int is extern `{ return errno; `} -fun errnoooooooooooooooooooooooooooooooooooooooooooooooooooooooooo: Int is extern `{ - return errno; -`} +fun errnoooooooooooooooooooooooooooooooooooooooooooooooooooooooooo: Int is + extern `{ return errno; `} private class A var my_attr = 1234 @@ -69,6 +68,7 @@ end extern class TimeT `{time_t`} new `{ return time(NULL); `} new from_i(i: Int) `{ return i; `} + fun update `{ time(&recv); `} fun ctime: String import NativeString.to_s_with_copy `{ @@ -78,10 +78,11 @@ extern class TimeT `{time_t`} # Difference in secondes from start (self if the end time) fun difftime(start: TimeT): Float `{ return difftime(recv, start); `} - private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is import - Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`} + private fun intern_poll(in_fds: Array[Int], out_fds: Array[Int]): nullable Int is + extern import Array[Int].length, Array[Int].[], Int.as(nullable Int) `{`} end fun address_is_null: Bool is extern "address_is_null" fun free `{ free(recv); `} + diff --git a/tests/sav/nitpretty_args53.res b/tests/sav/nitpretty_args53.res new file mode 100644 index 0000000..8152504 --- /dev/null +++ b/tests/sav/nitpretty_args53.res @@ -0,0 +1,35 @@ +# 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. + +class A + var a: Int # comment + private var b: nullable Int # happy + protected var c = 10 # ending + var d: Int = 10 + + + + + + # Test test... + var e: Int is writable + var f: Int is protected writable + # Adoc + var k: Int = 10 is protected writable + + + + # more comments +end # end + diff --git a/tests/sav/nitpretty_args54.res b/tests/sav/nitpretty_args54.res new file mode 100644 index 0000000..08a4d49 --- /dev/null +++ b/tests/sav/nitpretty_args54.res @@ -0,0 +1,24 @@ +# 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. + +class Foo + var a: Int + private var b: nullable Int + protected var c = 10 + var d: Int = 10 +end + +var foo = new Foo(1, 2) +print foo._a +foo._a = 10 diff --git a/tests/sav/nitpretty_args55.res b/tests/sav/nitpretty_args55.res new file mode 100644 index 0000000..2096847 --- /dev/null +++ b/tests/sav/nitpretty_args55.res @@ -0,0 +1,98 @@ +# 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. + + +# toplevel comment + + + + + + +# block +# block +# block + + + + + +# Adoc1 +class A # ending comments + + super Object + # super Int + + + super String + # super Truc + + + + # inclass comments + # comm + # ented + # blocks + + + + # Adoc2 + fun foo do + + # comment + + + + var truc + + # comment + # comment + + + + # comment + + + var chose + + # comment + end + + # comm + # ented + # blocks + + + fun bar do end + + + fun baz do end + # comment before end + +end # ending comments + +# comm +# ented +# blocks + +abstract class B # comment +end + +abstract class C end + +abstract class B # comment 2 + +end + +abstract class C end diff --git a/tests/sav/nitpretty_args56.res b/tests/sav/nitpretty_args56.res new file mode 100644 index 0000000..adabdcf --- /dev/null +++ b/tests/sav/nitpretty_args56.res @@ -0,0 +1,111 @@ +# 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. + +class Foo + fun bar: Bool do return true + + fun foo(other: Foo): Foo + do + if other.bar then + return other + else + return self + end + end + + fun baz: Int do + var nb = 0 + while nb < 10 do + print nb + nb += 1 + end # 1 + return nb + end + + fun gaz: Int + do + if bar then # 3 + return 1 + else + return -1 # 4 + end + end +end + +class Test[E] + var heap: ArrayHeap[E] + + init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator) + + init from(comparator: Comparator[E], items: Collection[E]) do + heap = new ArrayHeap[E].from(comparator, items.to_a) + end + + fun count(k: E): Int do + if heap.has(k) then + return 1 + else + return 0 + end + end + + fun node_at_idx(i: Int, k: E) do + while heap != null do + if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet + break + end + end + end + + fun save_those_nodes(nodes: Collection[Object]) do + for node in nodes do count(node) + end +end + +fun foo do + if last_slash > 0 then + return substring(last_slash + 1, length) + else + return null + end +end + +print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." + +var lorem = "lorem" +var ipsum = "ipsum" # for fun + +print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending + +var title = "title" +var links = new Array[String] # why not? + +var body = """ + + + + + + {{{title}}} + + +
+

{{{title}}}

+
    +
  • {{{links.join("
  • \n\t\t\t
  • ")}}}
  • +
+
+ +""" + diff --git a/tests/sav/nitpretty_args57.res b/tests/sav/nitpretty_args57.res new file mode 100644 index 0000000..e385838 --- /dev/null +++ b/tests/sav/nitpretty_args57.res @@ -0,0 +1,48 @@ +# 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. + +# prims + +var a = true +var b = false + +var c = 10 +var d = -10 +var e = 1.12 + +var f = -1.12 +var n = 'a' +var o = null +var p = 0x12345678 + +# strings + +var g = "test" +var h1 = "Hello {g}" +var h2 = "Hello \"{g}\" Hello" +var h3 = "Hello {g}" +var m = """ +bla + bla + +bla""" + +# arrays + +var j = [1, 2, 3] +var k = [1..2[ +var l = [1..2] + + + diff --git a/tests/sav/nitpretty_args58.res b/tests/sav/nitpretty_args58.res new file mode 100644 index 0000000..edc3f3f --- /dev/null +++ b/tests/sav/nitpretty_args58.res @@ -0,0 +1,48 @@ +# 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. + +module test_annot1 is platform("android") + +class A + fun goo is intern + + # test + fun foo is a, b + fun bar is a, b do print "1" + fun baz is + a + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb do print "2" +end + +class B + fun foo is a, b + + + fun bar is a, b do print "1" + + fun baz is a, b + do + bar + print "2" + end + + fun gaz is + a + bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + do + bar + print "2" + end + +end diff --git a/tests/sav/nitpretty_args59.res b/tests/sav/nitpretty_args59.res new file mode 100644 index 0000000..dadc656 --- /dev/null +++ b/tests/sav/nitpretty_args59.res @@ -0,0 +1,47 @@ +# 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. + +# comment 1 +class A + type FOO: Discrete + private var foo: FOO # comment + # comment 2 + var bar: Int = 10 +end + +class B + super A + + redef type FOO: Int + # comment 3 + redef fun foo do return bar # comment + redef fun bar + do + return 10 # comment 4 + end + fun baz do return # comment 5 + protected fun baz2 do end + fun other: String do + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + "aaaaaaaaaaaaaaaaaaaaaaaaaa" + end + + fun foo1(arr: Array[String], len: Int, ind: Int): String + do + return "Hello World!" + end +end + +# end + diff --git a/tests/sav/nitpretty_args60.res b/tests/sav/nitpretty_args60.res new file mode 100644 index 0000000..0fd4ac4 --- /dev/null +++ b/tests/sav/nitpretty_args60.res @@ -0,0 +1,127 @@ +# 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. + +class Foo + fun bar: Bool do return true + + fun foo(other: Foo): Foo + do + if other.bar then + return other + else + return self + end + end + + fun baz: Int do + var nb = 0 + while nb < 10 do + print nb + nb += 1 + end # 1 + return nb + end + + fun gaz: Int + do + if bar then # 3 + return 1 + else + return -1 # 4 + end + end +end + +class Test[E] + var heap: ArrayHeap[E] + + init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator) + + init from(comparator: Comparator[E], items: Collection[E]) do + heap = new ArrayHeap[E].from(comparator, items.to_a) + end + + fun count(k: E): Int do + if heap.has(k) then + return 1 + else + return 0 + end + end + + fun node_at_idx(i: Int, k: E) do + while heap != null do + if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet + break + end + end + end + + fun save_those_nodes(nodes: Collection[Object]) do + for node in nodes do count(node) + end +end + +fun foo do + if last_slash > 0 then + return substring(last_slash + 1, length) + else + return null + end +end + +print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidun" + + "t sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit a" + + "met lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus" + + " eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pell" + + "entesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae" + + " consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae" + + " lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas" + + " turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed phar" + + "etra lacus." + +var lorem = "lorem" +var ipsum = "ipsum" # for fun + +print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, con" + + "sectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius a" + + "t non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisi" + + "s neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}" + + ". Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} pla" + + "cerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus pl" + + "acerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum " + + "augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pell" + + "entesque vitae arcu justo. Aliquam sed pharetra lacus." # ending + +var title = "title" +var links = new Array[String] # why not? + +var body = """ + + + + + + {{{title}}} + + +
+

{{{title}}}

+
    +
  • {{{links.join("
  • \n\t\t\t
  • ")}}}
  • +
+
+ +""" + diff --git a/tests/sav/nitpretty_args61.res b/tests/sav/nitpretty_args61.res new file mode 100644 index 0000000..6b5b168 --- /dev/null +++ b/tests/sav/nitpretty_args61.res @@ -0,0 +1,44 @@ +# 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. + +# comment 1 +class A + type FOO: Discrete + private var foo: FOO # comment + # comment 2 + var bar: Int = 10 +end + +class B + super A + + redef type FOO: Int + # comment 3 + redef fun foo do return bar # comment + redef fun bar do + return 10 # comment 4 + end + fun baz do return # comment 5 + protected fun baz2 do end + fun other: String do + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + + fun foo1(arr: Array[String], len: Int, ind: Int): String do + return "Hello World!" + end +end + +# end + diff --git a/tests/sav/nitpretty_args62.res b/tests/sav/nitpretty_args62.res new file mode 100644 index 0000000..329aa03 --- /dev/null +++ b/tests/sav/nitpretty_args62.res @@ -0,0 +1,109 @@ +# 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. + +class Foo + fun bar: Bool do return true + + fun foo(other: Foo): Foo do + if other.bar then + return other + else + return self + end + end + + fun baz: Int do + var nb = 0 + while nb < 10 do + print nb + nb += 1 + end # 1 + return nb + end + + fun gaz: Int do + if bar then # 3 + return 1 + else + return -1 # 4 + end + end +end + +class Test[E] + var heap: ArrayHeap[E] + + init to(comparator: Comparator[E]) do heap = new ArrayHeap[E](comparator) + + init from(comparator: Comparator[E], items: Collection[E]) do + heap = new ArrayHeap[E].from(comparator, items.to_a) + end + + fun count(k: E): Int do + if heap.has(k) then + return 1 + else + return 0 + end + end + + fun node_at_idx(i: Int, k: E) do + while heap != null do + if heap.is_empty or i == k then # FIXME prefilter because the compiler is not smart enought yet + break + end + end + end + + fun save_those_nodes(nodes: Collection[Object]) do + for node in nodes do count(node) + end +end + +fun foo do + if last_slash > 0 then + return substring(last_slash + 1, length) + else + return null + end +end + +print "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis lorem. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus ipsum placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." + +var lorem = "lorem" +var ipsum = "ipsum" # for fun + +print "We also need to handle super strings: {lorem} {ipsum} dolor sit amet, consectetur adipiscing elit. Aliquam tincidunt sapien et velit fringilla varius at non eros. Nunc ut ultricies metus, sit amet lacinia felis. Donec in facilisis neque, non laoreet nibh. Etiam nec purus eu orci congue iaculis eu quis {lorem}. Ut et blandit erat. Cras fermentum pellentesque ante, ut dapibus {ipsum} placerat sit amet. Vivamus pharetra, sem vitae consequat venenatis, diam risus placerat est, sed hendrerit purus justo vitae lectus. In id quam mattis, rutrum augue eu, vehicula ipsum. Nulla nec egestas turpis, nec ullamcorper odio. Pellentesque vitae arcu justo. Aliquam sed pharetra lacus." # ending + +var title = "title" +var links = new Array[String] # why not? + +var body = """ + + + + + + {{{title}}} + + +
+

{{{title}}}

+
    +
  • {{{links.join("
  • \n\t\t\t
  • ")}}}
  • +
+
+ +""" + diff --git a/tests/sav/nitpretty_args7.res b/tests/sav/nitpretty_args7.res index e404557..ea248bc 100644 --- a/tests/sav/nitpretty_args7.res +++ b/tests/sav/nitpretty_args7.res @@ -29,7 +29,8 @@ class B # comment 3 redef fun foo do return bar # comment - redef fun bar do + redef fun bar + do return 10 # comment 4 end @@ -37,8 +38,12 @@ class B protected fun baz2 do end fun other: String do - return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + - "aaaaaaaaaaaaaaaaaaaaaaaaaa" + return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + end + + fun foo1(arr: Array[String], len: Int, ind: Int): String + do + return "Hello World!" end end diff --git a/tests/sav/nitpretty_args9.res b/tests/sav/nitpretty_args9.res index bd21dbb..46c03e1 100644 --- a/tests/sav/nitpretty_args9.res +++ b/tests/sav/nitpretty_args9.res @@ -31,7 +31,5 @@ class A fun foo5 do end # comment fun foo6(aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, - aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Int) do - print 1 - end + aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa: Int) do print 1 end # comment diff --git a/tests/sav/nitunit_args4.res b/tests/sav/nitunit_args4.res index faaa0c9..91eb55f 100644 --- a/tests/sav/nitunit_args4.res +++ b/tests/sav/nitunit_args4.res @@ -5,17 +5,17 @@ Entities: 4; Documented ones: 3; With nitunits: 3; Failures: 0 TestSuites: No test cases found Class suites: 0; Test Cases: 0; Failures: 0 -if true then +if true then assert true end -if true then +if true then assert true end -var a = 1 +var a = 1 assert a == 1 assert a == 1 \ No newline at end of file diff --git a/tests/sav/nitunit_args5.res b/tests/sav/nitunit_args5.res index 7019ad0..356eaf4 100644 --- a/tests/sav/nitunit_args5.res +++ b/tests/sav/nitunit_args5.res @@ -5,7 +5,7 @@ Entities: 6; Documented ones: 5; With nitunits: 3; Failures: 0 TestSuites: No test cases found Class suites: 0; Test Cases: 0; Failures: 0 -assert true # tested -assert true # tested -assert true # tested +assert true # tested +assert true # tested +assert true # tested \ No newline at end of file diff --git a/tests/sav/opengles2_hello_triangle.res b/tests/sav/opengles2_hello_triangle.res deleted file mode 100644 index c1dad34..0000000 --- a/tests/sav/opengles2_hello_triangle.res +++ /dev/null @@ -1,2 +0,0 @@ -../lib/mnit_linux/linux_app.nit:29,16--31: Redef Error: a virtual type cannot be refined. -../lib/mnit_linux/linux_app.nit:30,16--29: Redef Error: a virtual type cannot be refined. diff --git a/tests/sav/socket_simple_server.res b/tests/sav/socket_simple_server.res new file mode 100644 index 0000000..1ff31ed --- /dev/null +++ b/tests/sav/socket_simple_server.res @@ -0,0 +1 @@ +Usage : socket_simple_server diff --git a/tests/sav/test_exec.res b/tests/sav/test_exec.res index 2f109dd..3647533 100644 --- a/tests/sav/test_exec.res +++ b/tests/sav/test_exec.res @@ -1,3 +1,14 @@ A hello world! +0 + +B hello world!0 + C hello world! -B hello world!D hello world! \ No newline at end of file +0 + +D hello world!0 + +E +1 + +127 diff --git a/tests/sav/test_new_native_alt1.res b/tests/sav/test_new_native_alt1.res index c4db140..d21eb0d 100644 --- a/tests/sav/test_new_native_alt1.res +++ b/tests/sav/test_new_native_alt1.res @@ -1,4 +1,4 @@ -Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:786) +Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:808) NativeString N Nit diff --git a/tests/test_exec.nit b/tests/test_exec.nit index 4a1a749..da3bf84 100644 --- a/tests/test_exec.nit +++ b/tests/test_exec.nit @@ -18,19 +18,43 @@ import exec var hw = new Process("echo", "A", "hello", "world!") hw.wait +print hw.status + +print "" var ip = new IProcess("echo", "B hello world!") ip.read_line.output ip.wait +print ip.status + +print "" var op = new OProcess.from_a("cat", null) op.write("C hello world!\n") op.close op.wait +print op.status + +print "" var iop = new IOProcess.from_a("cat", null) iop.write("D hello world!\n") iop.read_line.output iop.close iop.wait +print iop.status + +print "" + +var e1 = new Process("sh", "-c", "echo E; exit 1") +e1.wait +print e1.status + +print "" +var ioperr = new IOProcess.from_a("bad command", null) +ioperr.write("D hello world!\n") +ioperr.read_line.output +ioperr.close +ioperr.wait +print ioperr.status diff --git a/tests/test_pretty/test_annot1.nit b/tests/test_pretty/test_annot1.nit index 381cc4f..695020f 100644 --- a/tests/test_pretty/test_annot1.nit +++ b/tests/test_pretty/test_annot1.nit @@ -15,6 +15,9 @@ module test_annot1 is platform("android") class A + fun goo is intern + + # test fun foo is a, b fun bar is a, b do print "1" fun baz is a, bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb do print "2" diff --git a/tests/test_pretty/test_prop1.nit b/tests/test_pretty/test_prop1.nit index 0b46c27..3b692a5 100644 --- a/tests/test_pretty/test_prop1.nit +++ b/tests/test_pretty/test_prop1.nit @@ -35,6 +35,11 @@ class B end # comment 5 protected fun baz2 do end fun other: String do return "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + + fun foo1(arr: Array[String], len: Int, ind: Int): String + do + return "Hello World!" + end end # end diff --git a/tests/testall.sh b/tests/testall.sh index 8176ab2..986a852 100755 --- a/tests/testall.sh +++ b/tests/testall.sh @@ -1,4 +1,20 @@ -for x in nitg-g nitg-s nitg-sg nitg-e niti; do +#!/bin/bash +# 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. + +# Run some tests on each engine +for x in nitg-g nitg-s nitg-sg nitg-e niti nitvm; do echo "--engine $x" ./tests.sh --engine $x "$@" done diff --git a/tests/tests.sh b/tests/tests.sh index 1a09d96..6c76366 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -418,6 +418,8 @@ case $engine in ;; nitvm) isinterpret=true + enginebinname=nit + OPT="--vm $OPT" savdirs="sav/niti/" ;; emscripten)