Merge: Rename Container to Ref
authorJean Privat <jean@pryen.org>
Fri, 17 Jul 2015 21:03:18 +0000 (17:03 -0400)
committerJean Privat <jean@pryen.org>
Fri, 17 Jul 2015 21:03:18 +0000 (17:03 -0400)
The `Container` class is widely misunderstood and underused.  However it can be very useful to implement a return parameter. This PR rename the class according to this usage. It will make some signatures easier to understand: (example inspired by a similar function from `UDPSocket`)

~~~nit
# This method has 3 outputs:
# * The received message written into `buffer`.
# * The number of bytes written as the return value.
# * The information on the sender of the message is stored in `sender_info`.
fun recv_from(buffer: NativeString, sender_info: Ref[nullable SocketAddress]): Int
~~~

### Bonus Ref tip!

`Ref` can also be used as a hackish equivalent to a `static` variable in C. This example use the dreaded `once` keyword to easily get a singleton, but it could be done cleaner with an attribute in `Sys`.

~~~nit
fun foo
   ...

    # Count the number of times foo is executed
    var counter = once new Ref[Int](0)
    counter.item += 1
    print "foo has been executed {counter.item} times"
end
~~~

Pull-Request: #1573
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>
Reviewed-by: Jean Privat <jean@pryen.org>

18 files changed:
examples/rosettacode/entropy.nit [new file with mode: 0644]
examples/rosettacode/entropy_narcissist.nit [new file with mode: 0644]
examples/rosettacode/fibonacci_word.nit [new file with mode: 0644]
lib/counter.nit
lib/standard/bytes.nit
lib/standard/collection/array.nit
src/compiler/separate_compiler.nit
src/modelize/modelize_property.nit
tests/base_init_basic.nit
tests/niti.skip
tests/nitvm.skip
tests/sav/base_init_basic_alt3.res
tests/sav/base_init_basic_alt5.res [new file with mode: 0644]
tests/sav/entropy.res [new file with mode: 0644]
tests/sav/entropy_narcissist.res [new file with mode: 0644]
tests/sav/fibonacci_word.res [new file with mode: 0644]
tests/sav/nitpick_args1.res
tests/sav/test_new_native_alt1.res

diff --git a/examples/rosettacode/entropy.nit b/examples/rosettacode/entropy.nit
new file mode 100644 (file)
index 0000000..f47910c
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Entropy
+# SEE: <http://rosettacode.org/wiki/Entropy>
+module entropy
+
+import counter
+
+# Basic implementation with a hashmap of chars to count them
+fun entropy(string: String): Float
+do
+       var cpt = new HashMap[Char, Int]
+       for char in string.chars do
+               var occ = cpt.get_or_default(char, 0)
+               cpt[char] = occ + 1
+       end
+
+       var len = string.length.to_f
+       var e = 0.0
+       for char, occ in cpt do
+               var freq = occ.to_f / len
+               e = e - freq * freq.log_base(2.0)
+       end
+       return e
+end
+print entropy("1223334444")
+
+# Alternative one-liner implementation using the `Counter::entropy` method of the library `counter`
+print "1223334444".chars.to_counter.entropy
diff --git a/examples/rosettacode/entropy_narcissist.nit b/examples/rosettacode/entropy_narcissist.nit
new file mode 100644 (file)
index 0000000..28b6212
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Entropy/Narcissist
+# SEE: <http://rosettacode.org/wiki/Entropy/Narcissist>
+module entropy_narcissist
+
+import counter
+
+# Should be run in the right directory
+print "entropy_narcissist.nit".to_path.read_all.chars.to_counter.entropy
diff --git a/examples/rosettacode/fibonacci_word.nit b/examples/rosettacode/fibonacci_word.nit
new file mode 100644 (file)
index 0000000..fec4929
--- /dev/null
@@ -0,0 +1,28 @@
+#!/usr/bin/env nit
+#
+# This file is part of NIT ( http://www.nitlanguage.org ).
+# This program is public domain
+
+# Task: Fibonacci_word
+# SEE: <http://rosettacode.org/wiki/Fibonacci_word>
+module fibonacci_word
+
+import counter
+
+var words = new Array[String]
+words[0] = ""
+words[1] = "1"
+words[2] = "0"
+
+for i in [1..37] do
+       var w
+       if i >= words.length then
+               w = words[i-1] + words[i-2]
+               words[i] = w
+       else
+               w = words[i]
+       end
+       var out = w
+       if w.length > 40 then out = "..."
+       print "{i}\t{w.length}\t{w.chars.to_counter.entropy.to_precision(16)}\t{out}"
+end
index befce9c..644c115 100644 (file)
@@ -254,6 +254,36 @@ class Counter[E]
                end
                return (sum / map.length.to_f).sqrt
        end
+
+       # The information entropy (Shannon entropy) of the elements in the counter (in bits).
+       fun entropy: Float
+       do
+               var res = 0.0
+               var sum = self.sum.to_f
+               for k, v in self do
+                       var f = v.to_f / sum
+                       res = res - f * f.log_base(2.0)
+               end
+               return res
+       end
+end
+
+redef class Collection[E]
+       # Create and fill up a counter with the elements of `self.
+       #
+       # ~~~
+       # var cpt = "abaa".chars.to_counter
+       # assert cpt['a'] == 3
+       # assert cpt['b'] == 1
+       # assert cpt.length == 2
+       # assert cpt.sum == 4
+       # ~~~
+       fun to_counter: Counter[E]
+       do
+               var res = new Counter[E]
+               res.inc_all(self)
+               return res
+       end
 end
 
 private class CounterComparator[E]
index 2a3526f..9843ddc 100644 (file)
@@ -46,6 +46,7 @@ class Bytes
                init(ns, 0, 0)
        end
 
+       # Init a `Bytes` with capacity `cap`
        init with_capacity(cap: Int) do
                var ns = new NativeString(cap)
                init(ns, 0, cap)
@@ -96,6 +97,16 @@ class Bytes
                end
        end
 
+       #     var b = new Bytes.empty
+       #     b.append([0x41u8, 0x41u8, 0x18u8])
+       #     b.pop
+       #     assert b.to_s == "AA"
+       redef fun pop do
+               assert length >= 1
+               length -= 1
+               return items[length]
+       end
+
        redef fun clear do length = 0
 
        # Regenerates the buffer, necessary when it was persisted
@@ -159,6 +170,7 @@ private class BytesIterator
 end
 
 redef class NativeString
+       # Creates a new `Bytes` object from `self` with `strlen` as length
        fun to_bytes: Bytes do
                var len = cstring_length
                return new Bytes(self, len, len)
index b6721df..ce89435 100644 (file)
@@ -553,6 +553,9 @@ private class ArrayReverseIterator[E]
        do
                _index = _array.length - 1
        end
+
+       # Do not cache `self`
+       redef fun finish do end
 end
 
 # Others collections ##########################################################
index b1e08c8..c73bf58 100644 (file)
@@ -819,12 +819,15 @@ class SeparateCompiler
                var v = new_visitor
 
                var rta = runtime_type_analysis
-               var is_dead = rta != null and not rta.live_classes.has(mclass) and not mtype.is_c_primitive and mclass.name != "NativeArray" and mclass.name != "Pointer"
+               var is_dead = rta != null and not rta.live_classes.has(mclass)
+               # While the class may be dead, some part of separately compiled code may use symbols associated to the class, so
+               # in order to compile and link correctly the C code, these symbols should be declared and defined.
+               var need_corpse = is_dead and mtype.is_c_primitive or mclass.kind == extern_kind or mclass.kind == enum_kind
 
-               v.add_decl("/* runtime class {c_name} */")
+               v.add_decl("/* runtime class {c_name}: {mclass.full_name} (dead={is_dead}; need_corpse={need_corpse})*/")
 
                # Build class vft
-               if not is_dead then
+               if not is_dead or need_corpse then
                        self.provide_declaration("class_{c_name}", "extern const struct class class_{c_name};")
                        v.add_decl("const struct class class_{c_name} = \{")
                        v.add_decl("{self.box_kind_of(mclass)}, /* box_kind */")
@@ -861,7 +864,8 @@ class SeparateCompiler
                        self.header.add_decl("{mtype.ctype_extern} value;")
                        self.header.add_decl("\};")
 
-                       if not rta.live_types.has(mtype) and mtype.mclass.name != "Pointer" then return
+                       # Pointer is needed by extern types, live or not
+                       if is_dead and mtype.mclass.name != "Pointer" then return
 
                        #Build BOX
                        self.provide_declaration("BOX_{c_name}", "val* BOX_{c_name}({mtype.ctype_extern});")
@@ -877,6 +881,7 @@ class SeparateCompiler
                        v.add("return (val*)res;")
                        v.add("\}")
 
+                       # A Pointer class also need its constructor
                        if mtype.mclass.name != "Pointer" then return
 
                        v = new_visitor
@@ -931,7 +936,7 @@ class SeparateCompiler
                        var pointer_type = mainmodule.pointer_type
 
                        self.provide_declaration("NEW_{c_name}", "{mtype.ctype} NEW_{c_name}(const struct type* type);")
-                       v.add_decl("/* allocate {mtype} */")
+                       v.add_decl("/* allocate extern {mtype} */")
                        v.add_decl("{mtype.ctype} NEW_{c_name}(const struct type* type) \{")
                        if is_dead then
                                v.add_abort("{mclass} is DEAD")
@@ -1203,27 +1208,29 @@ class SeparateCompilerVisitor
                        end
                        return self.new_expr("((struct instance_{mtype.c_name}*){value})->value; /* autounbox from {value.mtype} to {mtype} */", mtype)
                else if not mtype.is_c_primitive then
+                       assert value.mtype == value.mcasttype
                        if value.mtype.is_tagged then
+                               var res
                                if value.mtype.name == "Int" then
-                                       return self.new_expr("(val*)({value}<<2|1)", mtype)
+                                       res = self.new_expr("(val*)({value}<<2|1)", mtype)
                                else if value.mtype.name == "Char" then
-                                       return self.new_expr("(val*)((long)({value})<<2|2)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|2)", mtype)
                                else if value.mtype.name == "Bool" then
-                                       return self.new_expr("(val*)((long)({value})<<2|3)", mtype)
+                                       res = self.new_expr("(val*)((long)({value})<<2|3)", mtype)
                                else
                                        abort
                                end
+                               # Do not loose type info
+                               res.mcasttype = value.mcasttype
+                               return res
                        end
                        var valtype = value.mtype.as(MClassType)
                        if mtype isa MClassType and mtype.mclass.kind == extern_kind and mtype.mclass.name != "NativeString" then
                                valtype = compiler.mainmodule.pointer_type
                        end
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(valtype) then
-                               self.add("/*no autobox from {value.mtype} to {mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       # Do not loose type info
+                       res.mcasttype = value.mcasttype
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* autobox from {value.mtype} to {mtype} */")
                        return res
@@ -1259,11 +1266,7 @@ class SeparateCompilerVisitor
                   mtype.mclass.name != "NativeString" then
                        var valtype = compiler.mainmodule.pointer_type
                        var res = self.new_var(mtype)
-                       if compiler.runtime_type_analysis != null and not compiler.runtime_type_analysis.live_types.has(value.mtype.as(MClassType)) then
-                               self.add("/*no boxing of {value.mtype}: {value.mtype} is not live! */")
-                               self.add("PRINT_ERROR(\"Dead code executed!\\n\"); fatal_exit(1);")
-                               return res
-                       end
+                       compiler.undead_types.add(mtype)
                        self.require_declaration("BOX_{valtype.c_name}")
                        self.add("{res} = BOX_{valtype.c_name}({value}); /* boxing {value.mtype} */")
                        self.require_declaration("type_{mtype.c_name}")
index 1afce19..be862c6 100644 (file)
@@ -305,7 +305,12 @@ redef class ModelBuilder
                                        var i = 0
                                        for p in spd.initializers do
                                                if p != longest.initializers[i] then
-                                                       self.error(nclassdef, "Error: conflict for inherited inits {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")})")
+                                                       var proposal = new ArraySet[MProperty]
+                                                       for spd2 in spropdefs do
+                                                               proposal.add_all spd2.initializers
+                                                       end
+                                                       proposal.add_all initializers
+                                                       self.error(nclassdef, "Error: cannot generate automatic init for class {mclassdef.mclass}. Conflict in the order in inherited initializers {spd}({spd.initializers.join(", ")}) and {longest}({longest.initializers.join(", ")}). Use `autoinit` to order initializers. eg `autoinit {proposal.join(", ")}`")
                                                        # TODO: invalidate the initializer to avoid more errors
                                                        return
                                                end
index 6842aa3..36a1be1 100644 (file)
@@ -32,7 +32,7 @@ end
 class D
        super B
        super C
-       #alt3#var b: Int
+       #alt3,5#var b: Int
        #alt4#var b = 11
        init do 'd'.output #alt2#
 end
@@ -48,6 +48,7 @@ class F
        super D
        super E
        init do 'f'.output #alt1,2#
+       #alt5#autoinit c=, e=
 end
 
 class G
@@ -55,6 +56,7 @@ class G
        super E
        var g: Int
        init do 'g'.output #alt2#
+       #alt5#autoinit c=, e=, g=
 end
 
 class H
index 0da465e..5c4ff8a 100644 (file)
@@ -27,3 +27,4 @@ mpi_simple
 blink
 input
 first_letter_last_letter
+fibonacci_word
index 0da465e..5c4ff8a 100644 (file)
@@ -27,3 +27,4 @@ mpi_simple
 blink
 input
 first_letter_last_letter
+fibonacci_word
index a7be1ec..90f67d3 100644 (file)
@@ -1,2 +1,2 @@
-alt/base_init_basic_alt3.nit:47,7: Error: conflict for inherited inits base_init_basic_alt3#E#init(c=, e=) and base_init_basic_alt3#D#init(c=, b=)
-alt/base_init_basic_alt3.nit:53,7: Error: conflict for inherited inits base_init_basic_alt3#E#init(c=, e=) and base_init_basic_alt3#D#init(c=, b=)
+alt/base_init_basic_alt3.nit:47,7: Error: cannot generate automatic init for class F. Conflict in the order in inherited initializers base_init_basic_alt3#E#init(c=, e=) and base_init_basic_alt3#D#init(c=, b=). Use `autoinit` to order initializers. eg `autoinit c=, b=, e=`
+alt/base_init_basic_alt3.nit:54,7: Error: cannot generate automatic init for class G. Conflict in the order in inherited initializers base_init_basic_alt3#E#init(c=, e=) and base_init_basic_alt3#D#init(c=, b=). Use `autoinit` to order initializers. eg `autoinit c=, b=, e=, g=`
diff --git a/tests/sav/base_init_basic_alt5.res b/tests/sav/base_init_basic_alt5.res
new file mode 100644 (file)
index 0000000..ae87071
--- /dev/null
@@ -0,0 +1 @@
+alt/base_init_basic_alt5.nit:79,9--11: Error: expected 2 argument(s) for `init(c: Int, b: Int)`; got 1. See introduction at `standard::Object::init`.
diff --git a/tests/sav/entropy.res b/tests/sav/entropy.res
new file mode 100644 (file)
index 0000000..29f9066
--- /dev/null
@@ -0,0 +1,2 @@
+1.846
+1.846
diff --git a/tests/sav/entropy_narcissist.res b/tests/sav/entropy_narcissist.res
new file mode 100644 (file)
index 0000000..ba66466
--- /dev/null
@@ -0,0 +1 @@
+0.0
diff --git a/tests/sav/fibonacci_word.res b/tests/sav/fibonacci_word.res
new file mode 100644 (file)
index 0000000..38a9498
--- /dev/null
@@ -0,0 +1,37 @@
+1      1       0.0000000000000000      1
+2      1       0.0000000000000000      0
+3      2       1.0000000000000000      01
+4      3       0.9182958340544894      010
+5      5       0.9709505944546686      01001
+6      8       0.9544340029249650      01001010
+7      13      0.9612366047228760      0100101001001
+8      21      0.9587118829771316      010010100100101001010
+9      34      0.9596868937742170      0100101001001010010100100101001001
+10     55      0.9593160320543778      ...
+11     89      0.9594579158386696      ...
+12     144     0.9594037542210228      ...
+13     233     0.9594244469559864      ...
+14     377     0.9594165437404406      ...
+15     610     0.9594195626031440      ...
+16     987     0.9594184095152248      ...
+17     1597    0.9594188499578102      ...
+18     2584    0.9594186817240320      ...
+19     4181    0.9594187459836638      ...
+20     6765    0.9594187214386752      ...
+21     10946   0.9594187308140276      ...
+22     17711   0.9594187272329618      ...
+23     28657   0.9594187286008076      ...
+24     46368   0.9594187280783368      ...
+25     75025   0.9594187282779028      ...
+26     121393  0.9594187282016752      ...
+27     196418  0.9594187282307916      ...
+28     317811  0.9594187282196702      ...
+29     514229  0.9594187282239182      ...
+30     832040  0.9594187282222962      ...
+31     1346269 0.9594187282229156      ...
+32     2178309 0.9594187282226794      ...
+33     3524578 0.9594187282227690      ...
+34     5702887 0.9594187282227344      ...
+35     9227465 0.9594187282227478      ...
+36     14930352        0.9594187282227430      ...
+37     24157817        0.9594187282227448      ...
index 6e97e37..ba7c7ea 100644 (file)
@@ -1,5 +1,3 @@
-../lib/standard/bytes.nit:49,7--19: Documentation warning: Undocumented property `with_capacity`
-../lib/standard/bytes.nit:162,6--13: Documentation warning: Undocumented property `to_bytes`
 ../lib/standard/stream.nit:426,6--17: Documentation warning: Undocumented property `buffer_reset`
 ../lib/standard/file.nit:455,6--19: Documentation warning: Undocumented property `read_all_bytes`
 test_advice_repeated_types.nit:36,15--20: Warning: useless type repetition on redefined attribute `_a`
index 7d3c3bb..fcde192 100644 (file)
@@ -1,4 +1,4 @@
-Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:957)
+Runtime error: Cast failed. Expected `E`, got `Bool` (../lib/standard/collection/array.nit:960)
 NativeString
 0x4e
 Nit