Merge: Augmented literal strings
authorJean Privat <jean@pryen.org>
Thu, 7 Apr 2016 12:50:34 +0000 (08:50 -0400)
committerJean Privat <jean@pryen.org>
Thu, 7 Apr 2016 12:50:34 +0000 (08:50 -0400)
As a follow-up to #1991, here's the support mentioned in #1734 for bytestrings, regex and raw strings

NOTE: Depends on #1991 for integration, you may review the last 3 commits while #1991 is reviewed and eventually merged

TODO: Support degraded mode (Bytes and Byte only) for byte SuperStrings instead of refusing at compile-time

Pull-Request: #1992
Reviewed-by: Jean Privat <jean@pryen.org>
Reviewed-by: Alexis Laferrière <alexis.laf@xymus.net>

54 files changed:
benchmarks/bench_common.sh
benchmarks/languages/Makefile [new file with mode: 0644]
benchmarks/languages/bench_base.nit [deleted file]
benchmarks/languages/bench_languages.sh [moved from benchmarks/bench_languages.sh with 56% similarity]
benchmarks/languages/bench_typetest_covar.nit [deleted file]
benchmarks/languages/benches/bench_attr.nit [new file with mode: 0644]
benchmarks/languages/benches/bench_base.nit [new file with mode: 0644]
benchmarks/languages/benches/bench_meth.nit [new file with mode: 0644]
benchmarks/languages/benches/bench_null.nit [new file with mode: 0644]
benchmarks/languages/benches/bench_typetest_covar.nit [new file with mode: 0644]
benchmarks/languages/benches/bench_typetest_depth.nit [moved from benchmarks/languages/bench_typetest_depth.nit with 62% similarity]
benchmarks/languages/benches/bench_typetest_depth_not.nit [moved from benchmarks/languages/bench_typetest_depth_not.nit with 55% similarity]
benchmarks/languages/benches/bench_typetest_fts_nesting.nit [moved from benchmarks/languages/bench_typetest_fts_nesting.nit with 51% similarity]
contrib/benitlux/.gitignore
contrib/benitlux/Makefile
contrib/benitlux/src/benitlux_controller.nit
contrib/benitlux/src/benitlux_daily.nit
contrib/benitlux/src/benitlux_db.nit
contrib/benitlux/src/benitlux_model.nit
contrib/benitlux/src/benitlux_social.nit [new file with mode: 0644]
contrib/benitlux/src/benitlux_web.nit
contrib/benitlux/src/correct.nit
contrib/benitlux/src/report.nit
contrib/friendz/android/res/raw/music.ogg [moved from contrib/friendz/res/raw/music.ogg with 100% similarity]
lib/android/audio.nit
lib/core/text/abstract_text.nit
lib/json/serialization.nit
lib/nitcorn/examples/src/xymus_net.nit
lib/nitcorn/sessions.nit
lib/nitcorn/token.nit [new file with mode: 0644]
lib/performance_analysis.nit
lib/sqlite3/native_sqlite3.nit
lib/sqlite3/sqlite3.nit
src/compiler/abstract_compiler.nit
src/compiler/global_compiler.nit
src/compiler/separate_compiler.nit
src/interpreter/naive_interpreter.nit
src/transform.nit
tests/base_covar_adapt.nit [new file with mode: 0644]
tests/base_gen_nest.nit [new file with mode: 0644]
tests/base_vararg_transform.nit [new file with mode: 0644]
tests/base_with2.nit [new file with mode: 0644]
tests/sav/base_covar_adapt.res [new file with mode: 0644]
tests/sav/base_covar_adapt_alt1.res [new file with mode: 0644]
tests/sav/base_covar_adapt_alt2.res [new file with mode: 0644]
tests/sav/base_gen_nest.res [new file with mode: 0644]
tests/sav/base_vararg_transform.res [new file with mode: 0644]
tests/sav/base_with2.res [new file with mode: 0644]
tests/sav/nitc-common/fixme/base_with2.res [new file with mode: 0644]
tests/sav/nitce/base_covar_adapt_alt1.res [new file with mode: 0644]
tests/sav/nitce/base_covar_adapt_alt2.res [new file with mode: 0644]
tests/sav/nitcg/fixme/base_covar_adapt_alt2.res [new file with mode: 0644]
tests/sav/nitcg/test_text_stat.res [deleted file]
tests/test_sqlite3_native.nit

index 207edd0..dfdbc6b 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# Global variables
+
+# Number of times a command must be run with bench_command
+count=1
+
+# User time limit (in second) before a command is aborted
+usertimelimit=120
+
 # Common functions for all the bench scripts
 
 # Run a single command multiple time and store the execution times
@@ -41,21 +49,27 @@ function bench_command()
 
        # Execute the commands $count times
        for i in `seq 1 "$count"`; do
-               /usr/bin/time -f "%U" -o "$timeout" -a "$@" > $outputopts 2>&1 || { failed=true; die "$1: failed"; }
+               (
+                       ulimit -t "$usertimelimit" 2> /dev/null
+                       /usr/bin/time -f "%U" -o "$timeout" -a "$@" > $outputopts 2>&1
+               ) || { err=$?; failed=true; die "$1: failed with $err"; }
                echo -n "$i. "
                tail -n 1 "$timeout"
+               test -n "$failed" && break
        done
 
        line=`compute_stats "$timeout"`
        echo "$line ($res)"
        echo $line >> "$res"
 
-       test -z "$xml" && return
-       echo >>"$xml" "<testcase classname='bench.`basename $res .dat`' name='$title' time='`echo $line | cut -f 1 -d " "`' timestamp='`date -Iseconds`'>"
-       if test -n "$failed"; then
-               echo >>"$xml" "<error message='Command failed'/>"
+       if test -n "$xml"; then
+               echo >>"$xml" "<testcase classname='bench.`basename $res .dat`' name='$title' time='`echo $line | cut -f 1 -d " "`' timestamp='`date -Iseconds`'>"
+               if test -n "$failed"; then
+                       echo >>"$xml" "<error message='Command failed'/>"
+               fi
+               echo >>"$xml" "</testcase>"
        fi
-       echo >>"$xml" "</testcase>"
+       test -z "$failed"
 }
 
 # Run a simble command witout storing the execution time
@@ -86,7 +100,7 @@ function skip_test()
                return 0
        fi
        if test -n "$html"; then
-               echo >>"$html" "<h2>$1</h2>"
+               echo >>"$html" "<h2 id="$1">$1</h2>"
        fi
        echo "*"
        echo "* $1 *****"
@@ -99,4 +113,5 @@ function die()
 {
        echo >&2 "error: $*"
        died=1
+       return 1
 }
diff --git a/benchmarks/languages/Makefile b/benchmarks/languages/Makefile
new file mode 100644 (file)
index 0000000..ca35e94
--- /dev/null
@@ -0,0 +1,5 @@
+all:
+       ./bench_languages.sh all
+
+check:
+       ./bench_languages.sh --fast bench_meth
diff --git a/benchmarks/languages/bench_base.nit b/benchmarks/languages/bench_base.nit
deleted file mode 100644 (file)
index 9f79133..0000000
+++ /dev/null
@@ -1,483 +0,0 @@
-#!/usr/bin/env nit
-
-# Microbenchmak generation for multiple language
-# Just a quick an dirty Nit script file :)
-
-# This benchmark focuses on effects of the hierarchy dept
-# on the typetest performances
-
-# class Root
-# class Klass[E] super Root
-# class C1...CX super Root
-# Klass[CX] isa Klass[C1]
-
-class Klass
-       var id: Int
-       var supers = new Array[Klass]
-       var all_supers = new HashSet[Klass]
-       redef fun to_s
-       do
-               return "C{id}"
-       end
-end
-
-class Generator
-
-       var classes = new Array[Klass]
-
-       var dept writable = 5
-       var loops writable = 50000
-       var middle writable = 0
-       var dry writable = false
-       var check writable = false
-
-       fun genhier
-       do
-               var s: nullable Klass = null
-               for d in [1..dept] do
-                       var c = new Klass(d)
-
-                       classes.add(c)
-                       c.all_supers.add(c)
-                       if s != null then
-                               c.supers.add(s)
-                               c.all_supers.add_all(s.all_supers)
-                       end
-                       s = c
-               end
-               middle = (dept + 1) / 2
-       end
-
-       var file: nullable FileWriter = null
-       fun write(str: String)
-       do
-               file.write(str)
-               file.write("\n")
-       end
-
-       fun initnit(res: Array[String]) is abstract
-       fun testnit: String do return "true"
-
-       fun writenit(dir: String, name: String)
-       do
-               dir = "{dir}/nit"
-               dir.mkdir
-               file = new FileWriter.open("{dir}/{name}.nit")
-
-               write "class Root\n\tfun id: Int do return 0\nend"
-               for c in classes do
-                       write "class {c}[E]"
-                       if c.supers.is_empty then
-                               write "\tsuper Root"
-                       else for s in c.supers do
-                               write "\tsuper {s}[E]"
-                       end
-                       write "\tredef fun id do return {c.id}"
-                       write "end"
-               end
-
-               write "fun test(a,b: Root, loops, start: Int)"
-               write "do"
-               write "var x = start"
-               write "var i = 0"
-               write "while i < loops do"
-               write "\tvar j = 0"
-               write "\twhile j < loops do"
-               var test = "true"
-               if not dry then test = testnit
-               write "\t\tif {test} and x >= 0 then"
-               if check then write "\t\tx += 1"
-               write "\telse"
-               write "\t\t\tx = x - 1 + i - j"
-               write "\t\t\ta = b"
-               write "\t\tend"
-               write "\t\tj += 1"
-               write "\tend"
-               write "\ti += 1"
-               write "end"
-               write "print x"
-               write "end"
-
-               var ia = new Array[String]
-               initnit(ia)
-               write "var a: Root = {ia.first}"
-               write "var b: Root = a"
-               for i in ia do
-                       write "\t\t\tif a.id > 0 then a = {i}"
-               end
-               write "test(b, b, 10, -100)"
-               write "test(a, a, {loops}, 0)"
-
-               file.close
-       end
-
-       fun initjava(res: Array[String], interfaces: Bool) is abstract
-       fun testjava(interfaces: Bool): String do return "true"
-       fun writejava(dir: String, name: String, interfaces: Bool)
-       do
-               dir = "{dir}/java"
-               dir.mkdir
-               file = new FileWriter.open("{dir}/{name}.java")
-
-               var cl = ""
-               if interfaces then cl = "X"
-               write "class {name} \{"
-               if interfaces then
-                       write "static interface Root\n\t\{ int id(); \}"
-               else
-                       write "static class Root\n\t\{ int id() \{ return 0;\} \}"
-               end
-               for c in classes do
-                       if interfaces then
-                               write "static interface {c}<E> "
-                       else
-                               write "static class {c}<E> "
-                       end
-                       if c.supers.is_empty then
-                               write "\textends Root"
-                       else for s in [c.supers.first] do
-                               write "\textends {s}<E>"
-                       end
-                       if interfaces then
-                               write "\{\}"
-                               write "static class X{c}<E> implements {c}<E>"
-                       end
-                       write "\{"
-                       write "\tpublic int id() \{ return {c.id}; \}"
-                       write "\}"
-               end
-
-               write "static public void main(String args[]) \{"
-               var ia = new Array[String]
-               initjava(ia, interfaces)
-               write "Root a = {ia.first};"
-               write "Root b = a;"
-               for i in ia do
-                       write "\t\t\tif (a.id() > 0) a = {i};"
-               end
-               write "\ttest(b, b, 10, -100);"
-               write "\ttest(a, a, {loops}, 0);"
-               write "\}"
-
-               write "static public void test(Root a, Root b, int loops, int start) \{"
-               write "\tint x = start;"
-               write "\tfor(int i = 0; i < loops; i++) \{"
-               write "\t\tfor(int j = 0; j < loops; j++) \{"
-               var test = "true"
-               if not dry then test = testjava(interfaces)
-               write "\t\t\tif({test} && x>=0) \{"
-               if check then write "\t\t\t\tx = x + 1;"
-               write "\t\t\t\} else \{ x = x - 1 + i - j; a = b;\}"
-               #write "\t\t\t\} else \{ x = x - 1; a = b;\}"
-               write "\t\t}"
-               write "\t\}"
-               write "\tSystem.out.println(x);"
-               write "\}"
-               write "\}"
-               file.close
-       end
-
-       fun initcsharp(res: Array[String], interfaces: Bool) is abstract
-       fun testcsharp(interfaces: Bool): String do return "true"
-       fun writecsharp(dir: String, name: String, interfaces: Bool)
-       do
-               dir = "{dir}/cs"
-               dir.mkdir
-               file = new FileWriter.open("{dir}/{name}.cs")
-
-               var cl = ""
-               if interfaces then cl = "X"
-               write "class {name} \{"
-               if interfaces then
-                       write "interface Root\n\t\{ int Id(); \}"
-               else
-                       write "class Root\n\t\{ public int Id() \{ return 0;\} \}"
-               end
-               for c in classes do
-                       if interfaces then
-                               write "interface {c}<out E> "
-                       else
-                               write "class {c}<E> "
-                       end
-                       if c.supers.is_empty then
-                               write "\t: Root"
-                       else for s in [c.supers.first] do
-                               write "\t: {s}<E>"
-                       end
-                       if interfaces then
-                               write "\{\}"
-                               write "class X{c}<E> : {c}<E>"
-                       end
-                       write "\{"
-                       write "\tpublic int Id() \{ return {c.id}; \}"
-                       write "\}"
-               end
-
-               write "static void Main(string[] args) \{"
-               var ia = new Array[String]
-               initcsharp(ia, interfaces)
-               write "Root a = {ia.first};"
-               write "Root b = a;"
-               for i in ia do
-                       write "\t\t\tif (a.Id() > 0) a = {i};"
-               end
-               write "\tTest(b, b, 10, -100);"
-               write "\tTest(a, a, {loops}, 0);"
-               write "\}"
-
-               write "static void Test(Root a, Root b, int loops, int start) \{"
-               write "\tint x = start;"
-               write "\tfor(int i = 0; i < loops; i++) \{"
-               write "\t\tfor(int j = 0; j < loops; j++) \{"
-               var test = "true"
-               if not dry then test = testcsharp(interfaces)
-               write "\t\t\tif({test} && x>=0) \{"
-               if check then write "\t\t\t\tx++;"
-               write "\} else \{ x = x - 1 + i - j; a = b; \};"
-               write "\t\t}"
-               write "\t\}"
-               write "\tSystem.Console.WriteLine(x);"
-               write "\}"
-               write "\}"
-               file.close
-       end
-
-       fun initscala(res: Array[String], interfaces: Bool) is abstract
-       fun testscala(interfaces: Bool): String do return "true"
-       fun writescala(dir: String, name: String, interfaces: Bool)
-       do
-               dir = "{dir}/scala"
-               dir.mkdir
-               file = new FileWriter.open("{dir}/{name}.scala")
-
-               var cl = ""
-               write "object {name} \{"
-               write "class Root\n\t\{ def id: Int = 0 \}"
-               for c in classes do
-                       if interfaces then
-                               write "trait {c}[+E] "
-                       else
-                               write "class {c}[+E] "
-                       end
-                       if c.supers.is_empty then
-                               write "\textends Root"
-                       else for s in [c.supers.first] do
-                               write "\textends {s}[E]"
-                       end
-                       if interfaces then
-                               write "\{\}"
-                               write "class X{c}[E] extends {c}[E]"
-                       end
-                       write "\{"
-                       write "\toverride def id: Int = {c.id}"
-                       write "\}"
-               end
-
-               write "def main(args: Array[String]) = \{"
-               var ia = new Array[String]
-               initscala(ia, interfaces)
-               write "var a: Root = {ia.first};"
-               write "var b: Root = a;"
-               for i in ia do
-                       write "\t\t\tif (a.id > 0) a = {i};"
-               end
-               write "\ttest(b, b, 10, -100)"
-               write "\ttest(a, a, {loops}, 0)"
-               write "\}"
-
-               write "def test(aa:Root, b:Root, l: Int, start: Int) = \{"
-               write "\tvar a = aa"
-               write "\tvar x = start"
-               write "\tvar loops = l"
-               write "\tvar i = 0"
-               write "\twhile (i < loops) \{"
-               write "\t\tvar j = 0"
-               write "\t\twhile (j < loops) \{"
-               var test = "true"
-               if not dry then test = testscala(interfaces)
-               write "\t\tif ({test} && x>=0) \{"
-               if check then write "\t\t\tx += 1;"
-               #write "\} else \{ x = x - 1 + i - j; a = b; \}"
-               write "\} else \{ x = x - 1; a = b; \}"
-               write "\t\tj += 1"
-               write "\t\t\}"
-               write "\ti += 1"
-               write "\t\}"
-               write "\t\t\tprintln(x)"
-               write "\}"
-               write "\}"
-
-               file.close
-       end
-
-       fun initcpp(res: Array[String]) is abstract
-       fun testcpp: String do return "true"
-       fun writecpp(dir: String, name: String)
-       do
-               dir = "{dir}/cpp"
-               dir.mkdir
-               file = new FileWriter.open("{dir}/{name}.cpp")
-
-               write "#include <iostream>"
-               write "#include <stdlib.h>"
-               write "class Root\n\t\{ public: virtual int id() \{ return 0;\} \};"
-               for c in classes do
-                       write "template<class E>"
-                       write "class {c} "
-                       if c.supers.is_empty then
-                               write "\t: public virtual Root"
-                       else for s in [c.supers.first] do
-                               write "\t: public virtual {s}<E>"
-                       end
-                       write "\{"
-                       write "\tpublic: virtual int id() \{ return {c.id}; \}"
-                       write "\};"
-               end
-
-               write "void test(Root *a, Root *b, int loops, int start) \{"
-               write "\tint x = start;"
-               write "\tfor(int i = 0; i < loops; i++) \{"
-               write "\t\tfor(int j = 0; j < loops; j++) \{"
-               var test = "true"
-               if not dry then test = testcpp
-               write "\t\tif({test} && x>=0) \{"
-               if check then write "\t\t\tx += 1;"
-               write "\} else \{ x = x - 1 + i - j; a = b;\}"
-               write "\t\t}"
-               write "\t\}"
-               write "\tstd::cout << x << std::endl;"
-               write "\}"
-
-               write "int main(int argc, char **argv) \{"
-               var ia = new Array[String]
-               initcpp(ia)
-               write "Root *a = {ia.first};"
-               write "Root *b = a;"
-               for i in ia do
-                       write "\t\t\tif (a->id() > 0) a = {i};"
-               end
-               write "\ttest(b, b, 10, -100);"
-               write "\ttest(a, a, {loops}, 0);"
-               write "\}"
-
-               file.close
-       end
-
-       fun inite(res: Array[String], se: Bool) is abstract
-       fun teste(se: Bool): String do return "true"
-       fun locale(se: Bool) do end
-       fun writee(dir: String, name: String, se: Bool)
-       do
-               if se then
-                       dir = "{dir}/se/{name}"
-               else
-                       dir = "{dir}/es/{name}"
-               end
-               dir.mkdir
-               file = new FileWriter.open("{dir}/root.e")
-
-               var istk = ""
-               if se then istk = " is"
-               write "class ROOT"
-               write "feature id: INTEGER {istk} do Result := 0 end"
-               write "end"
-               file.close
-
-               for c in classes do
-                       file = new FileWriter.open("{dir}/{c}.e")
-                       write "class {c}[E] "
-                       if c.supers.is_empty then
-                               write "\tinherit ROOT"
-                       else for s in [c.supers.first] do
-                               write "\tinherit {s}[E]"
-                       end
-                       write "\t\tredefine id end"
-                       write "feature"
-                       write "\tid: INTEGER {istk} do Result := {c.id} end"
-                       write "end"
-                       file.close
-               end
-
-               file = new FileWriter.open("{dir}/app{name}.e")
-               write "class APP{name.to_upper}"
-               if se then
-                       write "insert ARGUMENTS"
-               end
-               write "create make"
-               write "feature"
-
-               if se then
-                       write "\tmake{istk}"
-               else
-                       write "\tmake(args: ARRAY[STRING]){istk}"
-               end
-               write "\t\tlocal"
-               write "\t\t\ta: ROOT"
-               write "\t\t\tb: ROOT"
-               write "\t\tdo"
-               var ia = new Array[String]
-               inite(ia,se)
-               write "{ia.first}"
-               write "b := a"
-               for i in ia do
-                       write "\t\t\tif a.id > 0 then {i} end"
-               end
-               write "\t\t\ttest(b, b, 10, -100)"
-               write "\t\t\ttest(a, a, {loops}, 0)"
-               write "\t\tend"
-
-               write "\ttest(aa: ROOT; b: ROOT; l: INTEGER; start: INTEGER){istk}"
-               write "\t\tlocal"
-               write "\t\t\ta: ROOT"
-               write "\t\t\ti: INTEGER"
-               write "\t\t\tj: INTEGER"
-               write "\t\t\tx: INTEGER"
-               locale(se)
-               write "\t\t\tloops: INTEGER"
-               write "\t\tdo"
-               write "\t\t\ta := aa"
-               write "\t\t\tx := start"
-               write "\t\t\tloops := l"
-               write "\t\t\tfrom i := 0 until i>=loops loop"
-               write "\t\t\t\tfrom j := 0 until j>=loops loop"
-               var test = "True"
-               if not dry then test = teste(se)
-               write "\t\t\t\t\tif {test} and then x >= 0 then" 
-               if check then write "\t\t\t\t\tx := x + 1"
-               write "\t\t\t\t\telse x := x - 1 + i - j; a := b end"
-               write "\t\t\t\t\tj := j + 1"
-               write "\t\t\t\tend"
-               write "\t\t\t\ti := i + 1"
-               write "\t\t\tend"
-               write "\t\t\tprint(x.out)"
-               write "\t\t\tprint(\"%N\")"
-               write "\t\tend"
-               write "end"
-               file.close
-       end
-
-       var count = false
-
-       fun genall
-       do
-               var g = self
-               var outdir = args.first
-               var name = args[1]
-               var use_interfaces = true
-               if args.length > 2 then g.dept = args[2].to_i
-               if args.length > 3 then use_interfaces = false
-
-               g.genhier
-
-               g.writenit(outdir, name)
-               g.writejava(outdir, name, use_interfaces)
-               g.writecsharp(outdir, name, use_interfaces)
-               g.writescala(outdir, name, use_interfaces)
-               g.writecpp(outdir, name)
-               g.writee(outdir, "{name}_se", true)
-               g.writee(outdir, name, false)
-       end
-end
-
-var g = new Generator
-g.genall
similarity index 56%
rename from benchmarks/bench_languages.sh
rename to benchmarks/languages/bench_languages.sh
index 2a33170..e204133 100755 (executable)
@@ -17,8 +17,8 @@
 
 # TODO: cleanup and libify the helper-parts
 
-source ./bench_common.sh
-source ./bench_plot.sh
+source ../bench_common.sh
+source ../bench_plot.sh
 
 ## CONFIGURATION OPTIONS ##
 
@@ -26,6 +26,13 @@ source ./bench_plot.sh
 # Can be overrided with 'the option -n'
 count=2
 
+xml=bench_languages.xml
+html=bench_languages.html
+verbose=true
+
+echo "" > $xml
+echo "<html><body>" > $html
+
 ## HANDLE OPTIONS ##
 
 function usage()
@@ -38,10 +45,31 @@ function usage()
        echo "  -h: this help"
 }
 
+function system_info()
+{
+       ( # use a subshell to protect the set -x
+       export LANG=C
+       set -x
+       uname -a
+       lscpu
+       git describe --always HEAD
+       ./nitc --version
+       gcc --version
+       clang --version
+       javac -version
+       java -version
+       gcj --version
+       scalac -version
+       scala -version
+       ec -version
+       pypy --version
+       )
+}
+
 stop=false
 while [ "$stop" = false ]; do
        case "$1" in
-               -v) verbose=true; shift;;
+               -v) verbose=true; system_info; shift;;
                -h) usage; exit;;
                -n) count="$2"; shift; shift;;
                --dry) dry_run=true; shift;;
@@ -59,10 +87,12 @@ if test -z "$NOTSKIPED"; then
 fi
 
 ## COMPILE ENGINES
-cd ../src
-test -f ./nitc_3 || ./ncall.sh -O
-cd ../benchmarks
-test -f ./nitc || ../src/nitc_3 ../src/nitc.nit -O -v
+test -f ./nitc || ../../bin/nitc ../../src/nitc.nit --semi-global -v
+
+today=`date +%Y-%m-%d_%H-%M-%S`
+bdir="languages.${today}.out"
+rm -r "$bdir" "./languages.out" 2> /dev/null
+ln -sf "$bdir" "./languages.out"
 
 ## EFFECTIVE BENCHS ##
 
@@ -71,107 +101,124 @@ function bench_language()
        name="$1"
        skip_test "$name" && return
        rootdir=`pwd`
-       basedir="./${name}.out"
-
-       mkdir $basedir
+       basedir="${bdir}/${name}"
+       mkdir -p "$basedir"
 
        t=t
-       s=20
-       seq="2 4 8"
+       s=50000
+       seq="3 4 8 16"
+
+       if [ "$fast" = "true" ]; then
+               s=10000
+               seq=3
+       fi
+
        for b in $seq; do
-               run_command ./nitc languages/$name.nit -o $basedir/$name.bin
-               run_command $basedir/$name.bin $basedir "${t}_$b" "$b"
+               run_command ./nitc benches/$name.nit -o $basedir/$name.bin &&
+               run_command $basedir/$name.bin $basedir "${t}_$b" "$b" "$s"
        done
 
        prepare_res $basedir/$name-g++.dat "g++" "g++"
        cppdir="${basedir}/cpp"
-       for b in $seq; do
-               run_command g++ "${cppdir}/${t}_$b.cpp" -O2 -o "${cppdir}/${t}_$b.g++.bin"
+       test -d $cppdir && for b in $seq; do
+               run_command g++ "${cppdir}/${t}_$b.cpp" -O2 -o "${cppdir}/${t}_$b.g++.bin" &&
                bench_command "$b" "" "${cppdir}/${t}_$b.g++.bin" $s
        done
 
        prepare_res $basedir/$name-clang++.dat "clang++" "clang++"
-       for b in $seq; do
-               run_command clang++ "${cppdir}/${t}_$b.cpp" -O2 -o "${cppdir}/${t}_$b.clang++.bin"
+       test -d $cppdir && for b in $seq; do
+               run_command clang++ "${cppdir}/${t}_$b.cpp" -O2 -o "${cppdir}/${t}_$b.clang++.bin" &&
                bench_command "$b" "" "${cppdir}/${t}_$b.clang++.bin" $s
        done
 
        prepare_res $basedir/$name-java.dat "java" "java"
        javadir="${basedir}/java"
-       for b in $seq; do
-               run_command javac "${javadir}/${t}_$b.java"
+       test -d $javadir && for b in $seq; do
+               run_command javac "${javadir}/${t}_$b.java" &&
                bench_command "$b" "" java -cp "${javadir}/" "${t}_$b" $s
        done
 
        prepare_res $basedir/$name-gcj.dat "gcj" "gcj"
-       for b in $seq; do
-               run_command gcj --main=${t}_$b -O2 "${javadir}/${t}_$b.java" -o "${javadir}/${t}_$b.gcj.bin"
+       test -d $javadir && for b in $seq; do
+               run_command gcj --main=${t}_$b -O2 "${javadir}/${t}_$b.java" -o "${javadir}/${t}_$b.gcj.bin" &&
                bench_command "$b" "" "${javadir}/${t}_$b.gcj.bin" $s
        done
 
        prepare_res $basedir/$name-scala.dat "scala" "scala"
        scaladir="${basedir}/scala"
-       for b in $seq; do
-               run_command scalac "${scaladir}/${t}_$b.scala" -d "${scaladir}"
+       test -d $scaladir && for b in $seq; do
+               run_command scalac "${scaladir}/${t}_$b.scala" -d "${scaladir}" &&
                bench_command "$b" "" scala -cp "${scaladir}/" "${t}_$b" $s
        done
 
        prepare_res $basedir/$name-cs.dat "c#" "c#"
        csdir="${basedir}/cs"
-       for b in $seq; do
-               run_command gmcs "$csdir/${t}_$b.cs"
+       test -d $csdir && for b in $seq; do
+               run_command mcs "$csdir/${t}_$b.cs" &&
                bench_command "$b" "" mono "$csdir/${t}_$b.exe" $s
        done
 
        prepare_res $basedir/$name-es.dat "es" "es"
        esdir="${basedir}/es"
-       for b in $seq; do
-               cd $esdir
-               run_command ec -clean -finalize ${t}_$b/app${t}_$b.e
-               chmod +x app${t}_$b
-               mv app${t}_$b ${t}_$b.es.bin
-               cd $rootdir
+       test -d $esdir && for b in $seq; do
+               run_command ec -clean -finalize "$esdir/${t}_$b/app${t}_$b.e" &&
+               chmod +x "app${t}_$b" &&
+               mv "app${t}_$b" "$esdir/${t}_$b.es.bin" &&
                bench_command "$b" "" "$esdir/${t}_$b.es.bin" $s
        done
 
+<<XXX
+       # SmartEiffel is so old...
        prepare_res $basedir/$name-se.dat "se" "se"
        sedir="${basedir}/se"
-       for b in $seq; do
+       test -d $sedir && for b in $seq; do
                cd $sedir
                run_command se compile -no_check app${t}_${b}_se.e -loadpath ${t}_${b}_se -o ${t}_$b.se.bin
                cd $rootdir
                bench_command "$b" "" "$sedir/${t}_$b.se.bin" $s
        done
+XXX
+
+       prepare_res $basedir/$name-python.dat "python" "python"
+       pythondir="${basedir}/python"
+       test -d $pythondir && for b in $seq; do
+               bench_command "$b" "" "pypy" "$pythondir/${t}_$b.py" $s
+       done
 
        nitdir="${basedir}/nit"
-       prepare_res $nitdir/$name-nitc.dat "nitc" "nitc"
-       for b in $seq; do
-               run_command ./nitc $nitdir/${t}_$b.nit --global -o "$nitdir/${t}_$b.nitc.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitc.bin" $s
+       prepare_res $basedir/$name-nitc-g.dat "nitc-g" "nitc-g"
+       test -d $nitdir && for b in $seq; do
+               run_command ./nitc $nitdir/${t}_$b.nit --global -o "$nitdir/${t}_$b.nitc-g.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\"" &&
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-g.bin" $s
        done
 
-       prepare_res $nitdir/$name-nitc-s.dat "nitc-s" "nitc-s"
-       for b in $seq; do
-               run_command ./nitc $nitdir/${t}_$b.nit --separate -o "$nitdir/${t}_$b.nitc-s.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
-               bench_command "$b" "" "$nitdir/${t}_$b.nitc-s.bin" $s
+       prepare_res $basedir/$name-nitc-sg.dat "nitc-sg" "nitc-sg"
+       test -d $nitdir && for b in $seq; do
+               run_command ./nitc $nitdir/${t}_$b.nit --semi-global -o "$nitdir/${t}_$b.nitc-sg.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\"" &&
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-sg.bin" $s
        done
 
+       prepare_res $basedir/$name-nitc-s.dat "nitc-s" "nitc-s"
+       test -d $nitdir && for b in $seq; do
+               run_command ./nitc $nitdir/${t}_$b.nit --separate -o "$nitdir/${t}_$b.nitc-s.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\"" &&
+               bench_command "$b" "" "$nitdir/${t}_$b.nitc-s.bin" $s
+       done
 <<XXX
-       prepare_res $nitdir/$name-nitc-su.dat "nitc-su" "nitc-su"
-       for b in $seq; do
-               run_command ./nitc $nitdir/${t}_$b.nit --separate --no-check-covariance -o "$nitdir/${t}_$b.nitc-su.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       prepare_res $basedir/$name-nitc-su.dat "nitc-su" "nitc-su"
+       test -d $nitdir && for b in $seq; do
+               run_command ./nitc $nitdir/${t}_$b.nit --separate --no-check-covariance -o "$nitdir/${t}_$b.nitc-su.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\"" &&
                bench_command "$b" "" "$nitdir/${t}_$b.nitc-su.bin" $s
        done
 
-       prepare_res $nitdir/$name-nitc-e.dat "nitc-e" "nitc-e"
-       for b in $seq; do
-               run_command ./nitc $nitdir/${t}_$b.nit --erasure -o "$nitdir/${t}_$b.nitc-e.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       prepare_res $basedir/$name-nitc-e.dat "nitc-e" "nitc-e"
+       test -d $nitdir && for b in $seq; do
+               run_command ./nitc $nitdir/${t}_$b.nit --erasure -o "$nitdir/${t}_$b.nitc-e.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\"" &&
                bench_command "$b" "" "$nitdir/${t}_$b.nitc-e.bin" $s
        done
 
-       prepare_res $nitdir/$name-nitc-eu.dat "nitc-eu" "nitc-eu"
-       for b in $seq; do
-               run_command ./nitc $nitdir/${t}_$b.nit --erasure --no-check-covariance --no-check-erasure-cast -o "$nitdir/${t}_$b.nitc-eu.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\""
+       prepare_res $basedir/$name-nitc-eu.dat "nitc-eu" "nitc-eu"
+       test -d $nitdir && for b in $seq; do
+               run_command ./nitc $nitdir/${t}_$b.nit --erasure --no-check-covariance --no-check-erasure-cast -o "$nitdir/${t}_$b.nitc-eu.bin" --make-flags "CFLAGS=\"-g -O2 -DNOBOEHM\"" &&
                bench_command "$b" "" "$nitdir/${t}_$b.nitc-eu.bin" $s
        done
 XXX
@@ -179,11 +226,14 @@ XXX
        plot $basedir/$name.gnu
 }
 
-for name in languages/*.nit; do
+for name in benches/*.nit; do
        n=`basename $name .nit`
-       bench_language $n
+       bench_language $n < /dev/null
 done
 
+echo "<h2>System Info</h2>" >> "$html"
+system_info 2>&1 | sed -e 's/$/<br>/;s|+\( .*\)|<h3>\1</h3>|' >> "$html"
+
 if test -n "$died"; then
        echo "Some commands failed"
        exit 1
diff --git a/benchmarks/languages/bench_typetest_covar.nit b/benchmarks/languages/bench_typetest_covar.nit
deleted file mode 100644 (file)
index d0bb2b8..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-#!/usr/bin/env nit
-
-import bench_base
-
-class TypeTestCovarGenerator
-       super Generator
-
-       redef fun initnit(res)
-       do
-               for c in classes do
-                       res.add "new {classes.first}[{c}[Root]]"
-               end
-
-       end
-
-       redef fun testnit
-       do
-               return "a isa {classes.first}[{classes[middle]}[Root]]"
-       end
-
-       redef fun initjava(res, interfaces)
-       do
-
-               var tagc = ""
-               if interfaces then tagc = "X"
-               for c in classes do
-                       res.add "new {tagc}{classes.first}<{c}<Root>>()"
-               end
-       end
-
-       redef fun testjava(interfaces)
-       do
-               return "a instanceof {classes.first}"
-       end
-
-       redef fun initcsharp(res, interfaces)
-       do
-               var tagc = ""
-               if interfaces then tagc = "X"
-               for c in classes do
-                       res.add "new {tagc}{classes.first}<{c}<Root>>()"
-               end
-       end
-
-       redef fun testcsharp(interfaces)
-       do
-               return "a is {classes.first}<{classes[middle]}<Root>>"
-       end
-
-       redef fun initscala(res, interfaces)
-       do
-               var tagc = ""
-               if interfaces then tagc = "X"
-               for c in classes do
-                       res.add "new {tagc}{classes.first}[{c}[Root]]()"
-               end
-       end
-
-       redef fun testscala(interfaces)
-       do
-               return "a.isInstanceOf[{classes.first}[{classes[middle]}[Root]]]"
-       end
-
-       redef fun initcpp(res)
-       do
-               for c in classes do
-                       res.add "new {classes.first}<{c}<Root>*>()"
-               end
-       end
-
-       redef fun testcpp
-       do
-               write "\t\t\t{classes.first}<{classes[middle]}<Root>*> *to = dynamic_cast<{classes.first}<{classes[middle]}<Root>*>*>(a);"
-               return "to != 0"
-       end
-
-       redef fun inite(res, se)
-       do
-               for c in classes do
-                       res.add "create \{{classes.first}[{c}[ROOT]]\} a"
-               end
-       end
-
-       redef fun teste(se)
-       do
-               write "\t\t\t\t\tto ?= a"
-               return "to /= Void"
-       end
-
-       redef fun locale(se)
-       do
-               write "\t\t\tto: {classes.first}[{classes[middle]}[ROOT]]"
-       end
-end
-
-var g = new TypeTestCovarGenerator
-g.genall
diff --git a/benchmarks/languages/benches/bench_attr.nit b/benchmarks/languages/benches/bench_attr.nit
new file mode 100644 (file)
index 0000000..22cf626
--- /dev/null
@@ -0,0 +1,115 @@
+#!/usr/bin/env nit
+# 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 bench_base
+
+class TypeTestDepthGenerator
+       super Generator
+
+       redef fun initnit(res)
+       do
+               for c in classes do
+                       res.add "new {c}[Root]"
+               end
+       end
+
+       redef fun testnit
+       do
+               return "a._aid >= {middle}"
+       end
+
+       redef fun initjava(res, interfaces)
+       do
+
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}<Root>()"
+               end
+       end
+
+       redef fun testjava(interfaces)
+       do
+               return "a.aid >= {middle}"
+       end
+
+       redef fun initcsharp(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}<Root>()"
+               end
+       end
+
+       redef fun testcsharp(interfaces)
+       do
+               return "a.aid >= {middle}"
+       end
+
+       redef fun initscala(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}[Root]()"
+               end
+       end
+
+       redef fun testscala(interfaces)
+       do
+               return "a.aid >= {middle}"
+       end
+
+       redef fun initcpp(res)
+       do
+               for c in classes do
+                       res.add "new {c}<Root>()"
+               end
+       end
+
+       redef fun testcpp
+       do
+               return "a->aid >= {middle}"
+       end
+
+       redef fun inite(res, se)
+       do
+               for c in classes do
+                       res.add "create \{{c}[ROOT]\}"
+               end
+       end
+
+       redef fun teste(se)
+       do
+               return "a.aid >= {middle}"
+       end
+
+       redef fun initpython(res)
+       do
+               for c in classes do
+                       res.add "{c}()"
+               end
+       end
+
+       redef fun testpython
+       do
+               return "a.aid >= {middle}"
+       end
+end
+
+var g = new TypeTestDepthGenerator
+g.use_interfaces = false
+g.genall
diff --git a/benchmarks/languages/benches/bench_base.nit b/benchmarks/languages/benches/bench_base.nit
new file mode 100644 (file)
index 0000000..f97bd65
--- /dev/null
@@ -0,0 +1,797 @@
+#!/usr/bin/env nit
+# 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.
+
+# Microbenchmak generation for multiple language
+# Just a quick an dirty Nit script file :)
+
+# This benchmark focuses on effects of the hierarchy dept
+# on the typetest performances
+
+# class Root
+# class Klass[E] super Root
+# class C1...CX super Root
+# Klass[CX] isa Klass[C1]
+
+class Klass
+       var id: Int
+       var supers = new Array[Klass]
+       var all_supers = new HashSet[Klass]
+       redef fun to_s
+       do
+               return "C{id}"
+       end
+end
+
+class Generator
+
+       var classes = new Array[Klass]
+
+       var dept = 5 is writable
+       var loops = 50000 is writable
+       var middle = 0 is writable
+       var dry = false is writable
+       var check = false is writable
+
+       # Polymorphism level
+       # This is the number of distinct dynamic types for the receiver
+       var poly = 2 is writable
+
+       # Use only interfaces (or traits) in the hierarchy
+       #
+       # If `false` then java&cs&scala will use classes, thus will break if MH is used
+       var use_interfaces = true is writable
+
+       # Add Root0, the superclass to Root that introduce common services.
+       #
+       # This add another level of un-optimization since the services are not introduced
+       # by the static type of the receiver.
+       var use_root0 = true is writable
+
+       fun genhier
+       do
+               var s: nullable Klass = null
+               for d in [1..dept] do
+                       var c = new Klass(d)
+
+                       classes.add(c)
+                       c.all_supers.add(c)
+                       if s != null then
+                               c.supers.add(s)
+                               c.all_supers.add_all(s.all_supers)
+                       end
+                       s = c
+               end
+               middle = (dept) / 2
+       end
+
+       var file: nullable FileWriter = null
+       fun write(str: String)
+       do
+               file.write(str)
+               file.write("\n")
+       end
+
+       fun initnit(res: Array[String]) do return
+       fun testnit: String do return "true"
+
+       fun writenit(dir: String, name: String)
+       do
+               var ia = new Array[String]
+               initnit(ia)
+               if ia.is_empty then
+                       print "Nit disabled"
+                       return
+               end
+
+               dir = "{dir}/nit"
+               dir.mkdir
+               file = new FileWriter.open("{dir}/{name}.nit")
+
+               if use_root0 then
+                       write "class Root0"
+               else
+                       write "class Root"
+               end
+               write "\tfun id: Int do return 0"
+               write "\tvar aid: Int = id"
+               write "\tvar next: Root is noautoinit"
+               write "\tfun set_next(next: Root): Root do\n\t\tself.next = next\n\t\treturn next\n\tend"
+               write "end"
+               if use_root0 then
+                       write "class Root\n\tsuper Root0\nend"
+               end
+               for c in classes do
+                       write "class {c}[E]"
+                       if c.supers.is_empty then
+                               write "\tsuper Root"
+                       else for s in c.supers do
+                               write "\tsuper {s}[E]"
+                       end
+                       write "\tredef fun id do return {c.id}"
+                       write "\tfun f{c.id}: Int do return {c.id}"
+                       write "\tvar a{c.id}: Int = {c.id}"
+                       write "end"
+               end
+
+               write "fun test(a,b: Root, loops, start: Int)"
+               write "do"
+               write "var x = start"
+               write "var i = loops"
+               write "while i > 0 do"
+               write "\tvar j = loops"
+               write "\twhile j > 0 do"
+               var test = "x >= 0"
+               if not dry then test = testnit
+               write "\t\tif {test} then"
+               write "\t\t\tvar tmp = a; a = b; b = tmp"
+               if check then write "\t\tx += 1"
+               write "\t\telse"
+               write "\t\t\tx += 1"
+               write "\t\t\ta = a.next"
+               write "\t\t\tb = b.next"
+               write "\t\tend"
+               write "\t\tj -= 1"
+               write "\tend"
+               write "\tx += 1"
+               write "\ta = a.next"
+               write "\tb = b.next"
+               write "\ti -= 1"
+               write "end"
+               write "print x"
+               write "end"
+
+               write "var b: Root = {ia.first}"
+               write "var tmp = b"
+               for i in ia do
+                       write "b = b.set_next({i})"
+               end
+               write "b.next = tmp"
+               write "var a: Root = {ia.last}"
+               write "tmp = a"
+               for i in [1..poly[ do
+                       write "a = a.set_next({ia[ia.length - 1 - i]})"
+               end
+               write "a.next = tmp"
+               write "test(b, b.next, 10, -100)"
+               write "test(a, a.next, {loops}, 0)"
+
+               file.close
+       end
+
+       fun initjava(res: Array[String], interfaces: Bool) do return
+       fun testjava(interfaces: Bool): String do return "true"
+       fun writejava(dir: String, name: String, interfaces: Bool)
+       do
+               var ia = new Array[String]
+               initjava(ia, interfaces)
+               if ia.is_empty then
+                       print "Java disabled"
+                       return
+               end
+
+               dir = "{dir}/java"
+               dir.mkdir
+               file = new FileWriter.open("{dir}/{name}.java")
+
+               write "class {name} \{"
+               var root0 = "Root"
+               if use_root0 then root0 = "Root0"
+               if interfaces then
+                       write "static interface {root0} \{ long id(); Root getNext(); Root setNext(Root next); \}"
+                       write "static class XRoot implements {root0} \{"
+               else
+                       write "static class {root0} \{"
+               end
+               write "\tpublic long id() \{ return 0;\}"
+               write "\tpublic long aid = id();"
+               write "\tpublic Root next;"
+               write "\tpublic Root getNext() \{ return next;\}"
+               write "\tpublic Root setNext(Root next) \{ this.next = next; return next; \}"
+               write "\}"
+
+               if use_root0 then
+                       if interfaces then
+                               write "static interface Root extends Root0 \{\}"
+                       else
+                               write "static class Root extends Root0 \{\}"
+                       end
+               end
+
+               for c in classes do
+                       if interfaces then
+                               write "static interface {c}<E> "
+                       else
+                               write "static class {c}<E> "
+                       end
+                       if c.supers.is_empty then
+                               write "\textends Root"
+                       else for s in [c.supers.first] do
+                               write "\textends {s}<E>"
+                       end
+                       if interfaces then
+                               write "\{\}"
+                               if c.supers.is_empty then
+                                       write "static class X{c}<E> extends XRoot implements {c}<E>"
+                               else
+                                       write "static class X{c}<E> extends X{c.supers.first}<E> implements {c}<E>"
+                               end
+                       end
+                       write "\{"
+                       write "\tpublic long id() \{ return {c.id}; \}"
+                       write "\tpublic long f{c.id}() \{ return {c.id}; \}"
+                       write "\tpublic long a{c.id} = {c.id};"
+                       write "\}"
+               end
+
+               write "static public void main(String args[]) \{"
+               write "\tRoot b = {ia.first};"
+               write "\tRoot tmp = b;"
+               for i in ia do
+                       write "\tb = b.setNext({i});"
+               end
+               write "\tb.setNext(tmp);"
+               write "\tRoot a = {ia.last};"
+               write "\ttmp = a;"
+               for i in [1..poly[ do
+                       write "\ta = a.setNext({ia[ia.length - 1 - i]});"
+               end
+               write "\ta.setNext(tmp);"
+               write "\ttest(b, b.getNext(), 10, -100);"
+               write "\ttest(a, a.getNext(), {loops}, 0);"
+               write "\}"
+
+               write "static public void test(Root a, Root b, long loops, long start) \{"
+               write "\tlong x = start;"
+               write "\tfor(long i = loops; i > 0; i--) \{"
+               write "\t\tfor(long j = loops; j > 0; j--) \{"
+               var test = "x>=0"
+               if not dry then test = testjava(interfaces)
+               write "\t\t\tif({test}) \{"
+               write "\t\t\t\tRoot tmp = a; a = b; b = tmp;"
+               if check then write "\t\t\t\tx = x + 1;"
+               write "\t\t\t\} else \{ x++; a = a.getNext(); b = b.getNext(); \}"
+               write "\t\t\}"
+               write "\t\tx++; a = a.getNext(); b = b.getNext();"
+               write "\t\}"
+               write "\tSystem.out.println(x);"
+               write "\}"
+               write "\}"
+               file.close
+       end
+
+       fun initcsharp(res: Array[String], interfaces: Bool) do return
+       fun testcsharp(interfaces: Bool): String do return "true"
+       fun writecsharp(dir: String, name: String, interfaces: Bool)
+       do
+               var ia = new Array[String]
+               initcsharp(ia, interfaces)
+               if ia.is_empty then
+                       print "C# disabled"
+                       return
+               end
+
+               dir = "{dir}/cs"
+               dir.mkdir
+               file = new FileWriter.open("{dir}/{name}.cs")
+
+               write "class {name} \{"
+               var root0 = "Root"
+               if use_root0 then root0 = "Root0"
+               if interfaces then
+                       write "interface {root0} \{ long Id(); Root GetNext(); Root SetNext(Root next); \}"
+                       write "class XRoot: {root0} \{ "
+               else
+                       write "class {root0}\n\t\{ "
+               end
+               write "\tvirtual public long Id() \{ return 0; \}"
+               write "\tpublic long aid;"
+               write "\tpublic Root next;"
+               write "\tvirtual public Root GetNext() \{ return next; \}"
+               write "\tvirtual public Root SetNext(Root next) \{ this.next = next; return next; \}"
+               write "\}"
+
+               if use_root0 then
+                       if interfaces then
+                               write "interface Root: Root0 \{\}"
+                       else
+                               write "class Root: Root0 \{\}"
+                       end
+               end
+
+               for c in classes do
+                       var cname
+                       if interfaces then
+                               write "interface {c}<out E> "
+                               cname = "X"+c.to_s
+                       else
+                               write "class {c}<E> "
+                               cname = c.to_s
+                       end
+                       if c.supers.is_empty then
+                               write "\t: Root"
+                       else for s in [c.supers.first] do
+                               write "\t: {s}<E>"
+                       end
+                       if interfaces then
+                               write "\{\}"
+                               if c.supers.is_empty then
+                                       write "class X{c}<E>: XRoot, {c}<E>"
+                               else
+                                       write "class X{c}<E>: X{c.supers.first}<E>, {c}<E>"
+                               end
+                       end
+                       write "\{"
+                       write "\toverride public long Id() \{ return {c.id}; \}"
+                       write "\tvirtual public long F{c.id}() \{ return {c.id}; \}"
+                       write "\tpublic long A{c.id} = {c.id};"
+                       write "\tpublic {cname}() \{ aid = {c.id}; \}"
+                       write "\}"
+               end
+
+               write "static void Main(string[] args) \{"
+               write "\tRoot b = {ia.first};"
+               write "\tRoot tmp = b;"
+               for i in ia do
+                       write "\tb = b.SetNext({i});"
+               end
+               write "\tb.SetNext(tmp);"
+               write "\tRoot a = {ia.last};"
+               write "\ttmp = a;"
+               for i in [1..poly[ do
+                       write "\ta = a.SetNext({ia[ia.length - 1 - i]});"
+               end
+               write "\ta.SetNext(tmp);"
+               write "\tTest(b, b.GetNext(), 10, -100);"
+               write "\tTest(a, a.GetNext(), {loops}, 0);"
+               write "\}"
+
+               write "static void Test(Root a, Root b, long loops, long start) \{"
+               write "\tlong x = start;"
+               write "\tfor(long i = loops; i > 0; i--) \{"
+               write "\t\tfor(long j = loops; j >0; j--) \{"
+               var test = "x>=0"
+               if not dry then test = testcsharp(interfaces)
+               write "\t\t\tif({test}) \{"
+               write "\t\t\t\tRoot tmp = a; a = b; b = tmp;"
+               if check then write "\t\t\t\tx++;"
+               write "\t\t\t\} else \{ x += 1; a = a.GetNext(); b = b.GetNext();\};"
+               write "\t\t\}"
+               write "\t\tx += 1; a = a.GetNext(); b = b.GetNext();"
+               write "\t\}"
+               write "\tSystem.Console.WriteLine(x);"
+               write "\}"
+               write "\}"
+               file.close
+       end
+
+       fun initscala(res: Array[String], interfaces: Bool) do return
+       fun testscala(interfaces: Bool): String do return "true"
+       fun writescala(dir: String, name: String, interfaces: Bool)
+       do
+               var ia = new Array[String]
+               initscala(ia, interfaces)
+               if ia.is_empty then
+                       print "Scala disabled"
+                       return
+               end
+
+               dir = "{dir}/scala"
+               dir.mkdir
+               file = new FileWriter.open("{dir}/{name}.scala")
+
+               write "object {name} \{"
+               if use_root0 then
+                       write "class Root0 \{"
+               else
+                       write "class Root \{"
+               end
+               write "\tdef id: Long = 0; var aid: Long = id; var next: Root = null; def getNext: Root = next; def setNext(next: Root): Root = \{ this.next = next; return next; \} \}"
+
+               if use_root0 then
+                       write "class Root extends Root0 \{\}"
+               end
+
+               for c in classes do
+                       if interfaces then
+                               write "trait {c}[+E] "
+                       else
+                               write "class {c}[+E] "
+                       end
+                       if c.supers.is_empty then
+                               write "\textends Root"
+                       else for s in [c.supers.first] do
+                               write "\textends {s}[E]"
+                       end
+                       if interfaces then
+                               write "\{\}"
+                               if c.supers.is_empty then
+                                       write "class X{c}[E] extends {c}[E]"
+                               else
+                                       write "class X{c}[E] extends X{c.supers.first}[E] with {c}[E]"
+                               end
+                       end
+                       write "\{"
+                       write "\toverride def id: Long = {c.id}"
+                       write "\tdef f{c.id}: Long = {c.id}"
+                       if interfaces then
+                               write "\tvar a{c.id}: Long = {c.id}"
+                       end
+                       write "\}"
+               end
+
+               write "def main(args: Array[String]) = \{"
+               write "\tvar b: Root = {ia.first};"
+               write "\tvar tmp = b;"
+               for i in ia do
+                       write "\tb = b.setNext({i});"
+               end
+               write "\tb.setNext(tmp);"
+               write "\tvar a: Root = {ia.last};"
+               write "\ttmp = a;"
+               for i in [1..poly[ do
+                       write "\ta = a.setNext({ia[ia.length - 1 - i]});"
+               end
+               write "\ta.setNext(tmp);"
+               write "\ttest(b, b.getNext, 10, -100);"
+               write "\ttest(a, a.getNext, {loops}, 0);"
+               write "\}"
+
+               write "def test(aa:Root, bb:Root, l: Long, start: Long) = \{"
+               write "\tvar a = aa"
+               write "\tvar b = bb"
+               write "\tvar x = start"
+               write "\tvar loops = l"
+               write "\tvar i = loops"
+               write "\twhile (i > 0) \{"
+               write "\t\tvar j = loops"
+               write "\t\twhile (j > 0) \{"
+               var test = "x>=0"
+               if not dry then test = testscala(interfaces)
+               write "\t\t\tif ({test}) \{"
+               write "\t\t\t\tval tmp = a; a = b; b = tmp;"
+               if check then write "\t\t\t\tx += 1;"
+               write "\t\t\t\} else \{ x += 1; a = a.getNext; b = b.getNext;\}"
+               write "\t\t\tj -= 1"
+               write "\t\t\}"
+               write "\t\tx += 1; a = a.getNext; b = b.getNext;"
+               write "\t\ti -= 1"
+               write "\t\t\}"
+               write "\tprintln(x)"
+               write "\}"
+               write "\}"
+
+               file.close
+       end
+
+       fun initcpp(res: Array[String]) do return
+       fun testcpp: String do return "true"
+       fun writecpp(dir: String, name: String)
+       do
+               var ia = new Array[String]
+               initcpp(ia)
+               if ia.is_empty then
+                       print "C++ diabled"
+                       return
+               end
+
+               dir = "{dir}/cpp"
+               dir.mkdir
+               file = new FileWriter.open("{dir}/{name}.cpp")
+
+               write "#include <iostream>"
+               write "#include <stdlib.h>"
+               if use_root0 then
+                       write "class Root;"
+                       write "class Root0"
+               else
+                       write "class Root"
+               end
+               write "\t\{ public: virtual long id() \{ return 0;\} long aid; Root *next; virtual Root *setNext(Root *n) \{this->next = n; return n;\} \};"
+
+               if use_root0 then
+                       write "class Root: public virtual Root0 \{\};"
+               end
+
+               for c in classes do
+                       write "template<class E>"
+                       write "class {c} "
+                       if c.supers.is_empty then
+                               write "\t: public virtual Root"
+                       else for s in [c.supers.first] do
+                               write "\t: public virtual {s}<E>"
+                       end
+                       write "\{"
+                       write "\tpublic: virtual long id() \{ return {c.id}; \}"
+                       write "\tvirtual long f{c.id}() \{ return {c.id}; \}"
+                       write "\tlong a{c.id};"
+                       write "\t{c}(): a{c.id}({c.id}) \{ this->aid = {c.id}; \}"
+                       write "\};"
+               end
+
+               write "void test(Root *a, Root *b, long loops, long start) \{"
+               write "\tlong x = start;"
+               write "\tfor(long i = loops; i > 0; i--) \{"
+               write "\t\tfor(int j = loops; j > 0; j--) \{"
+               var test = "x>=0"
+               if not dry then test = testcpp
+               write "\t\t\tif({test}) \{"
+               write "\t\t\t\tRoot *tmp = a; a = b; b = tmp;"
+               if check then write "\t\t\t\tx += 1;"
+               write "\t\t\t\} else \{ x++; a = a->next; b = b->next;\}"
+               write "\t\t\}"
+               write "\t\tx++; a = a->next; b = b->next;"
+               write "\t\}"
+               write "\tstd::cout << x << std::endl;"
+               write "\}"
+
+               write "int main(int argc, char **argv) \{"
+               write "\tRoot *b = {ia.first};"
+               write "\tRoot *tmp = b;"
+               for i in ia do
+                       write "\tb = b->setNext({i});"
+               end
+               write "\tb->setNext(tmp);"
+               write "\tRoot *a = {ia.last};"
+               write "\ttmp = a;"
+               for i in [1..poly[ do
+                       write "\ta = a->setNext({ia[ia.length - 1 - i]});"
+               end
+               write "\ta->setNext(tmp);"
+               write "\ttest(b, b->next, 10, -100);"
+               write "\ttest(a, a->next, {loops}, 0);"
+               write "\}"
+
+               file.close
+       end
+
+       fun inite(res: Array[String], se: Bool) do return
+       fun teste(se: Bool): String do return "true"
+       fun locale(se: Bool) do end
+       fun writee(dir: String, name: String, se: Bool)
+       do
+               var ia = new Array[String]
+               inite(ia,se)
+               if ia.is_empty then
+                       print "Eiffel disabled"
+                       return
+               end
+
+               if se then
+                       dir = "{dir}/se/{name}"
+               else
+                       dir = "{dir}/es/{name}"
+               end
+               dir.mkdir
+
+               var root0
+               if use_root0 then
+                       root0 = "ROOT0"
+                       file = new FileWriter.open("{dir}/root0.e")
+               else
+                       root0 = "ROOT"
+                       file = new FileWriter.open("{dir}/root.e")
+               end
+
+               var istk = ""
+               if se then istk = " is"
+               write "class {root0}"
+               write "feature id: INTEGER_64 {istk} do Result := 0 end"
+               write "aid: INTEGER_64"
+               write "xnext: detachable ROOT"
+               write "next: ROOT do check attached xnext as n then Result := n end end"
+               write "set_next(n: ROOT): ROOT do xnext := n Result := n end"
+               write "make do aid := id end"
+               write "end"
+               file.close
+
+               if use_root0 then
+                       file = new FileWriter.open("{dir}/root.e")
+                       write "class ROOT inherit ROOT0 end "
+                       file.close
+               end
+
+               for c in classes do
+                       file = new FileWriter.open("{dir}/{c}.e")
+                       write "class {c}[E] "
+                       if c.supers.is_empty then
+                               write "\tinherit ROOT"
+                       else for s in [c.supers.first] do
+                               write "\tinherit {s}[E]"
+                       end
+                       write "\t\tredefine id, make end"
+                       write "create make"
+                       write "feature"
+                       write "\tid: INTEGER_64 {istk} do Result := {c.id} end"
+                       write "\tf{c.id}: INTEGER_64 {istk} do Result := {c.id} end"
+                       write "\ta{c.id}: INTEGER_64 {istk} attribute Result := {c.id} end"
+                       write "make do aid := {c.id} end"
+                       write "end"
+                       file.close
+               end
+
+               file = new FileWriter.open("{dir}/app{name}.e")
+               write "class APP{name.to_upper}"
+               if se then
+                       write "insert ARGUMENTS"
+               end
+               write "create make"
+               write "feature"
+
+               if se then
+                       write "\tmake{istk}"
+               else
+                       write "\tmake(args: ARRAY[STRING]){istk}"
+               end
+               write "\t\tlocal"
+               write "\t\t\ta: ROOT"
+               write "\t\t\tb: ROOT"
+               write "\t\t\ttmp: ROOT"
+               write "\t\tdo"
+               write "\t\t\tb := {ia.first} .make"
+               write "\t\t\ttmp := b"
+               for i in ia do
+                       write "\t\t\tb := b.set_next({i} .make)"
+               end
+               write "\t\t\ttmp := b.set_next(tmp)"
+
+               write "\t\t\ta := {ia.last} .make"
+               write "\t\t\ttmp := a"
+               for i in [1..poly[ do
+                       write "\t\t\ta := a.set_next({ia[ia.length - 1 - i]} .make)"
+               end
+               write "\t\t\ttmp := a.set_next(tmp);"
+               write "\t\t\ttest(b, b.next, 10, -100)"
+               write "\t\t\ttest(a, a.next, {loops}, 0)"
+               write "\t\tend"
+
+               write "\ttest(aa: ROOT; bb: ROOT; l: INTEGER_64; start: INTEGER_64){istk}"
+               write "\t\tlocal"
+               write "\t\t\ta: ROOT"
+               write "\t\t\tb: ROOT"
+               write "\t\t\ttmp: ROOT"
+               write "\t\t\ti: INTEGER_64"
+               write "\t\t\tj: INTEGER_64"
+               write "\t\t\tx: INTEGER_64"
+               locale(se)
+               write "\t\t\tloops: INTEGER_64"
+               write "\t\tdo"
+               write "\t\t\ta := aa"
+               write "\t\t\tb := bb"
+               write "\t\t\tx := start"
+               write "\t\t\tloops := l"
+               write "\t\t\tfrom i := loops until i<=0 loop"
+               write "\t\t\t\tfrom j := loops until j<=0 loop"
+               var test = "x >= 0"
+               if not dry then test = teste(se)
+               write "\t\t\t\t\tif {test} then"
+               write "\t\t\t\t\t\ttmp := a; a := b; b := tmp"
+               if check then write "\t\t\t\t\tx := x + 1"
+               write "\t\t\t\t\telse x := x + 1; a := a.next; b := b.next end"
+               write "\t\t\t\t\tj := j - 1"
+               write "\t\t\t\tend"
+               write "\t\t\t\tx := x + 1; a := a.next; b := b.next"
+               write "\t\t\t\ti := i - 1"
+               write "\t\t\tend"
+               write "\t\t\tprint(x.out)"
+               write "\t\t\tprint(\"%N\")"
+               write "\t\tend"
+               write "end"
+               file.close
+       end
+
+       fun initpython(res: Array[String]) do return
+       fun testpython: String do return "true"
+       fun writepython(dir: String, name: String)
+       do
+               var ia = new Array[String]
+               initpython(ia)
+               if ia.is_empty then
+                       print "Python disabled"
+                       return
+               end
+
+               dir = "{dir}/python"
+               dir.mkdir
+               file = new FileWriter.open("{dir}/{name}.py")
+
+               if use_root0 then
+                       write "class Root0(object):"
+               else
+                       write "class Root(object):"
+               end
+               write "\tdef id(self): return 0"
+               write "\tdef set_next(self, n):\n\t\tself.next = n\n\t\treturn n"
+               write "\tdef __init__(self): self.aid = self.id()"
+
+               if use_root0 then
+                       write "class Root(Root0): pass"
+               end
+
+               for c in classes do
+                       if c.supers.is_empty then
+                               write "class {c}(Root):"
+                       else
+                               write "class {c}({c.supers.join(",")}):"
+                       end
+                       write "\tdef id(self): return {c.id}"
+                       write "\tdef f{c.id}(self): return {c.id}"
+                       write "\tdef __init__(self):"
+                       write "\t\tsuper({c}, self).__init__()"
+                       write "\t\ta{c.id} = {c.id}"
+               end
+
+               write "def test(a, b, loops, start):"
+               write "\tx = start"
+               write "\ti = loops"
+               write "\twhile i > 0:"
+               write "\t\tj = loops"
+               write "\t\twhile j > 0:"
+               var test = "x >= 0"
+               if not dry then test = testpython
+               write "\t\t\tif {test}:"
+               write "\t\t\t\ttmp = a; a = b; b = tmp"
+               if check then write "\t\t\t\tx += 1"
+               write "\t\t\telse:"
+               write "\t\t\t\tx = x + 1; a = a.next; b = b.next"
+               write "\t\t\tj -= 1"
+               write "\t\tx = x + 1; a = a.next; b = b.next"
+               write "\t\ti -= 1"
+               write "\tprint(x)"
+
+               write "b = {ia.first}"
+               write "tmp = b"
+               for i in ia do
+                       write "b = b.set_next({i})"
+               end
+               write "b.next = tmp"
+               write "a = {ia.last}"
+               write "tmp = a"
+               for i in [1..poly[ do
+                       write "a = a.set_next({ia[ia.length - 1 - i]})"
+               end
+               write "a.next = tmp"
+               write "test(b, b.next, 10, -100)"
+               write "test(a, a.next, {loops}, 0)"
+
+               file.close
+       end
+
+       var count = false
+
+       fun genall
+       do
+               var g = self
+               var outdir = args.first
+               var name = args[1]
+               var use_interfaces = self.use_interfaces
+               if args.length > 2 then g.dept = args[2].to_i
+               if args.length > 3 then loops = args[3].to_i
+               if args.length > 4 then use_interfaces = false
+
+               g.genhier
+
+               g.writenit(outdir, name)
+               g.writejava(outdir, name, use_interfaces)
+               g.writecsharp(outdir, name, use_interfaces)
+               g.writescala(outdir, name, use_interfaces)
+               g.writecpp(outdir, name)
+               g.writee(outdir, "{name}_se", true)
+               g.writee(outdir, name, false)
+               g.writepython(outdir, name)
+       end
+end
+
+var g = new Generator
+g.genall
diff --git a/benchmarks/languages/benches/bench_meth.nit b/benchmarks/languages/benches/bench_meth.nit
new file mode 100644 (file)
index 0000000..3eaaa28
--- /dev/null
@@ -0,0 +1,114 @@
+#!/usr/bin/env nit
+# 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 bench_base
+
+class TypeTestDepthGenerator
+       super Generator
+
+       redef fun initnit(res)
+       do
+               for c in classes do
+                       res.add "new {c}[Root]"
+               end
+       end
+
+       redef fun testnit
+       do
+               return "a.id >= {middle}"
+       end
+
+       redef fun initjava(res, interfaces)
+       do
+
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}<Root>()"
+               end
+       end
+
+       redef fun testjava(interfaces)
+       do
+               return "a.id() >= {middle}"
+       end
+
+       redef fun initcsharp(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}<Root>()"
+               end
+       end
+
+       redef fun testcsharp(interfaces)
+       do
+               return "a.Id() >= {middle}"
+       end
+
+       redef fun initscala(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}[Root]()"
+               end
+       end
+
+       redef fun testscala(interfaces)
+       do
+               return "a.id >= {middle}"
+       end
+
+       redef fun initcpp(res)
+       do
+               for c in classes do
+                       res.add "new {c}<Root>()"
+               end
+       end
+
+       redef fun testcpp
+       do
+               return "a->id() >= {middle}"
+       end
+
+       redef fun inite(res, se)
+       do
+               for c in classes do
+                       res.add "create \{{c}[ROOT]\}"
+               end
+       end
+
+       redef fun teste(se)
+       do
+               return "a.id >= {middle}"
+       end
+
+       redef fun initpython(res)
+       do
+               for c in classes do
+                       res.add "{c}()"
+               end
+       end
+
+       redef fun testpython
+       do
+               return "a.id() >= {middle}"
+       end
+end
+
+var g = new TypeTestDepthGenerator
+g.genall
diff --git a/benchmarks/languages/benches/bench_null.nit b/benchmarks/languages/benches/bench_null.nit
new file mode 100644 (file)
index 0000000..a67c27b
--- /dev/null
@@ -0,0 +1,80 @@
+#!/usr/bin/env nit
+# 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 bench_base
+
+class TypeTestDepthGenerator
+       super Generator
+
+       redef fun initnit(res)
+       do
+               for c in classes do
+                       res.add "new {c}[Root]"
+               end
+       end
+
+       redef fun initjava(res, interfaces)
+       do
+
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}<Root>()"
+               end
+       end
+
+       redef fun initcsharp(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}<Root>()"
+               end
+       end
+
+       redef fun initscala(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{c}[Root]()"
+               end
+       end
+
+       redef fun initcpp(res)
+       do
+               for c in classes do
+                       res.add "new {c}<Root>()"
+               end
+       end
+
+       redef fun inite(res, se)
+       do
+               for c in classes do
+                       res.add "create \{{c}[ROOT]\}"
+               end
+       end
+
+       redef fun initpython(res)
+       do
+               for c in classes do
+                       res.add "{c}()"
+               end
+       end
+end
+
+var g = new TypeTestDepthGenerator
+g.dry = true
+g.genall
diff --git a/benchmarks/languages/benches/bench_typetest_covar.nit b/benchmarks/languages/benches/bench_typetest_covar.nit
new file mode 100644 (file)
index 0000000..4ccbcf3
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/env nit
+# 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 bench_base
+
+class TypeTestCovarGenerator
+       super Generator
+
+       redef fun initnit(res)
+       do
+               for c in classes do
+                       res.add "new {classes.first}[{c}[Root]]"
+               end
+
+       end
+
+       redef fun testnit
+       do
+               return "a isa {classes.first}[{classes[middle]}[Root]]"
+       end
+
+       redef fun initcsharp(res, interfaces)
+       do
+               var tagc = ""
+               if interfaces then tagc = "X"
+               for c in classes do
+                       res.add "new {tagc}{classes.first}<{c}<Root>>()"
+               end
+       end
+
+       redef fun testcsharp(interfaces)
+       do
+               return "a is {classes.first}<{classes[middle]}<Root>>"
+       end
+
+       redef fun inite(res, se)
+       do
+               for c in classes do
+                       res.add "create \{{classes.first}[{c}[ROOT]]\}"
+               end
+       end
+
+       redef fun teste(se)
+       do
+               write "\t\t\t\t\tto ?= a"
+               return "to /= Void"
+       end
+
+       redef fun locale(se)
+       do
+               write "\t\t\tto: detachable {classes.first}[{classes[middle]}[ROOT]]"
+       end
+end
+
+var g = new TypeTestCovarGenerator
+g.genall
@@ -1,4 +1,17 @@
 #!/usr/bin/env nit
+# 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 bench_base
 
@@ -77,7 +90,7 @@ class TypeTestDepthGenerator
        redef fun inite(res, se)
        do
                for c in classes do
-                       res.add "create \{{c}[ROOT]\} a"
+                       res.add "create \{{c}[ROOT]\}"
                end
        end
 
@@ -89,7 +102,18 @@ class TypeTestDepthGenerator
 
        redef fun locale(se)
        do
-               write "\t\t\tto: {classes[middle]}[ROOT]"
+               write "\t\t\tto: detachable {classes[middle]}[ROOT]"
+       end
+
+       redef fun initpython(res)
+       do
+               for c in classes do
+                       res.add "{c}()"
+               end
+       end
+       redef fun testpython
+       do
+               return "isinstance(a, {classes[middle]})"
        end
 end
 
@@ -1,4 +1,17 @@
 #!/usr/bin/env nit
+# 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 bench_base
 import pipeline
@@ -9,7 +22,7 @@ class TypeTestDepthNotGenerator
        redef fun initnit(res)
        do
                res.add "new {classes.last}[Root]"
-               for c in classes.skip_tail(1) do
+               for c in classes.iterator.skip_tail(1) do
                        res.add "new {c}[Root]"
                end
        end
@@ -25,7 +38,7 @@ class TypeTestDepthNotGenerator
                var tagc = ""
                if interfaces then tagc = "X"
                res.add "new {tagc}{classes.last}<Root>()"
-               for c in classes.skip_tail(1) do
+               for c in classes.iterator.skip_tail(1) do
                        res.add "new {tagc}{c}<Root>()"
                end
        end
@@ -40,7 +53,7 @@ class TypeTestDepthNotGenerator
                var tagc = ""
                if interfaces then tagc = "X"
                res.add "new {tagc}{classes.last}<Root>()"
-               for c in classes.skip_tail(1) do
+               for c in classes.iterator.skip_tail(1) do
                        res.add "new {tagc}{c}<Root>()"
                end
        end
@@ -55,7 +68,7 @@ class TypeTestDepthNotGenerator
                var tagc = ""
                if interfaces then tagc = "X"
                res.add "new {tagc}{classes.last}[Root]()"
-               for c in classes.skip_tail(1) do
+               for c in classes.iterator.skip_tail(1) do
                        res.add "new {tagc}{c}[Root]()"
                end
        end
@@ -68,7 +81,7 @@ class TypeTestDepthNotGenerator
        redef fun initcpp(res)
        do
                res.add "new {classes.last}<Root>()"
-               for c in classes.skip_tail(1) do
+               for c in classes.iterator.skip_tail(1) do
                        res.add "new {c}<Root>()"
                end
        end
@@ -81,9 +94,9 @@ class TypeTestDepthNotGenerator
 
        redef fun inite(res, se)
        do
-               res.add "create \{{classes.last}[ROOT]\} a"
-               for c in classes.skip_tail(1) do
-                       res.add "create \{{c}[ROOT]\} a"
+               res.add "create \{{classes.last}[ROOT]\}"
+               for c in classes.iterator.skip_tail(1) do
+                       res.add "create \{{c}[ROOT]\}"
                end
        end
 
@@ -95,7 +108,19 @@ class TypeTestDepthNotGenerator
 
        redef fun locale(se)
        do
-               write "\t\t\tto: {classes.last}[ROOT]"
+               write "\t\t\tto: detachable {classes.last}[ROOT]"
+       end
+
+       redef fun initpython(res)
+       do
+               res.add "{classes.last}()"
+               for c in classes.iterator.skip_tail(1) do
+                       res.add "{c}()"
+               end
+       end
+       redef fun testpython
+       do
+               return "not isinstance(a, {classes.last})"
        end
 end
 
@@ -1,4 +1,17 @@
 #!/usr/bin/env nit
+# 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 bench_base
 
@@ -8,15 +21,16 @@ class TypeTestFtsNestingGenerator
        fun clanit(i: Int): String
        do
                var s = new FlatBuffer
-               s.append("{classes.first}[" * i)
+               s.append("{classes[i]}[")
+               s.append("{classes.first}[" * (dept-1))
                s.append("Root")
-               s.append("]" * i)
+               s.append("]" * dept)
                return s.to_s
        end
 
        redef fun initnit(res)
        do
-               for i in [1..dept] do
+               for i in [0..dept[ do
                        res.add "new {clanit(i)}"
                end
 
@@ -30,32 +44,18 @@ class TypeTestFtsNestingGenerator
        fun clajava(i: Int): String
        do
                var s = new FlatBuffer
-               s.append("{classes.first}<" * i)
+               s.append("{classes[i]}<")
+               s.append("{classes.first}<" * (dept-1))
                s.append("Root")
-               s.append(">" * i)
+               s.append(">" * dept)
                return s.to_s
        end
 
-       redef fun initjava(res, interfaces)
-       do
-
-               var tagc = ""
-               if interfaces then tagc = "X"
-               for i in [1..dept] do
-                       res.add "new {tagc}{clajava(i)}()"
-               end
-       end
-
-       redef fun testjava(interfaces)
-       do
-               return "a instanceof {classes.first}<?>"
-       end
-
        redef fun initcsharp(res, interfaces)
        do
                var tagc = ""
                if interfaces then tagc = "X"
-               for i in [1..dept] do
+               for i in [0..dept[ do
                        res.add "new {tagc}{clajava(i)}()"
                end
        end
@@ -65,32 +65,19 @@ class TypeTestFtsNestingGenerator
                return "a is {clajava(middle)}"
        end
 
-       redef fun initscala(res, interfaces)
-       do
-               var tagc = ""
-               if interfaces then tagc = "X"
-               for i in [1..dept] do
-                       res.add "new {tagc}{clanit(i)}()"
-               end
-       end
-
-       redef fun testscala(interfaces)
-       do
-               return "a.isInstanceOf[{clanit(middle)}]"
-       end
-
        fun clacpp(i: Int): String
        do
                var s = new FlatBuffer
-               s.append("{classes.first}<" * i)
+               s.append("{classes[i]}<")
+               s.append("{classes.first}<" * (dept-1))
                s.append("Root")
-               s.append("*>" * i)
+               s.append("*>" * dept)
                return s.to_s
        end
 
        redef fun initcpp(res)
        do
-               for i in [1..dept] do
+               for i in [0..dept[ do
                        res.add "new {clacpp(i)}()"
                end
        end
@@ -104,16 +91,17 @@ class TypeTestFtsNestingGenerator
        fun clae(i: Int): String
        do
                var s = new FlatBuffer
-               s.append("{classes.first}[" * i)
+               s.append("{classes[i]}[")
+               s.append("{classes.first}[" * (dept-1))
                s.append("ROOT")
-               s.append("]" * i)
+               s.append("]" * dept)
                return s.to_s
        end
 
        redef fun inite(res, se)
        do
-               for i in [1..dept] do
-                       res.add "create \{{clae(i)}\} a"
+               for i in [0..dept[ do
+                       res.add "create \{{clae(i)}\}"
                end
        end
 
@@ -125,7 +113,7 @@ class TypeTestFtsNestingGenerator
 
        redef fun locale(se)
        do
-               write "\t\t\tto: {clae(middle)}"
+               write "\t\t\tto: detachable {clae(middle)}"
        end
 end
 
index 2ccbe7b..562fb6d 100644 (file)
@@ -1,4 +1,4 @@
-src/benitlux_serial.nit
+src/benitlux_restful.nit
 *.db
 *.email
 benitlux_corrections.txt
index be1543b..c14d648 100644 (file)
@@ -1,19 +1,19 @@
-all: server
+SERVER ?= localhost:8080
 
-pre-build: src/benitlux_serial.nit
+all: server
 
+server: bin/benitlux_daily bin/benitlux_web
 bin/benitlux_daily: $(shell ../../bin/nitls -M src/benitlux_daily.nit)
        mkdir -p bin/
        ../../bin/nitc -o $@ src/benitlux_daily.nit
 
-bin/benitlux_web: $(shell ../../bin/nitls -M src/benitlux_web.nit)
+bin/benitlux_web: $(shell ../../bin/nitls -M src/benitlux_web.nit) src/benitlux_restful.nit
        mkdir -p bin/
-       ../../bin/nitc -o $@ src/benitlux_web.nit
-
-server: bin/benitlux_daily bin/benitlux_web
+       ../../bin/nitc -o $@ src/benitlux_web.nit -D iface=$(SERVER)
 
-src/benitlux_serial.nit:
-       ../../bin/nitserial -o $@ src/benitlux_web.nit
+pre-build: src/benitlux_restful.nit
+src/benitlux_restful.nit: $(shell ../../bin/nitls -M src/benitlux_controller.nit)
+       ../../bin/nitrestful -o $@ src/benitlux_controller.nit
 
 # ---
 # Report
index 806b42e..5f5513a 100644 (file)
 module benitlux_controller
 
 import nitcorn
+import nitcorn::restful
 private import json::serialization
 
 import benitlux_model
 import benitlux_db
 import benitlux_view
+import benitlux_social
 
+# Server action for REST or Web, for a given location
 abstract class BenitluxAction
        super Action
 
-       # Path to the database
-       var db_path = "benitlux_sherbrooke.db"
+       # Database used for both the mailing list and the social network
+       var db: BenitluxDB
 
        # Path to the storage of the last email sent
        var sample_email_path = "benitlux_sherbrooke.email"
@@ -56,16 +59,12 @@ class BenitluxSubscriptionAction
                                template.message_level = "success"
                                template.message_content = "Subscription successful!"
 
-                               var db = new DB.open(db_path)
                                db.subscribe email
-                               db.close
                        else if unsub then
                                template.message_level = "warning"
                                template.message_content = "You've been unsubscribed."
 
-                               var db = new DB.open(db_path)
                                db.unsubscribe email
-                               db.close
                        end
                end
 
@@ -83,47 +82,248 @@ class BenitluxSubscriptionAction
        end
 end
 
-# RESTful interface to compare beer offer between given dates
-#
-# Expects request in the format of `since/2014-07-24`, will replay with a
-# `BeerEvents` serialized to Json with the necessary meta-data to be deserialized.
+# RESTful interface for the client app
 class BenitluxRESTAction
        super BenitluxAction
+       super RestfulAction
 
-       redef fun answer(request, turi)
-       do
-               var words = turi.split("/")
-               if not words.is_empty and words.first.is_empty then words.shift
+       # Sign up a new user
+       #
+       # signup?name=a&pass=b&email=c -> LoginResult | BenitluxError
+       fun signup(name, pass, email: String): HttpResponse
+       is restful do
+               # Validate input
+               if not name.name_is_ok then
+                       var error = new BenitluxError("Invalid username")
+                       return new HttpResponse.ok(error)
+               end
+
+               if not pass.pass_is_ok then
+                       var error = new BenitluxError("Invalid password")
+                       return new HttpResponse.ok(error)
+               end
+
+               # Query DB
+               var error_message = db.signup(name, pass, email)
+
+               var object: nullable Serializable
+               if error_message == null then
+                       object = db.login(name, pass)
+               else
+                       object = new BenitluxError(error_message)
+               end
+
+               if object == null then
+                       # There was an error in the call to login
+                       return new HttpResponse.server_error
+               end
+
+               # It went ok, may or may not be signed up
+               return new HttpResponse.ok(object)
+       end
+
+       # Attempt to login
+       #
+       # login?name=a&pass=b -> LoginResult | BenitluxError
+       fun login(name, pass: String): HttpResponse
+       is restful do
+               var log: nullable Serializable = db.login(name, pass)
+               if log == null then log = new BenitluxError("Login Failed", "Invalid username and password combination.")
+
+               return new HttpResponse.ok(log)
+       end
+
+       # Search a user
+       #
+       # search?token=b&query=a&offset=0 -> Array[UserAndFollowing] | BenitluxError
+       fun search(token: nullable String, query: String): HttpResponse
+       is restful do
+               var user_id = db.token_to_id(token)
+               var users = db.search_users(query, user_id)
+               if users == null then return new HttpResponse.server_error
+
+               return new HttpResponse.ok(users)
+       end
 
-               if words.length >= 2 and words[0] == "since" then
-                       var since = words[1].std_date
+       # List available beers
+       #
+       # list?token=a[&offset=0&count=1] -> Array[BeerAndRatings] | BenitluxError
+       fun list(token: nullable String): HttpResponse
+       is restful do
+               var user_id = db.token_to_id(token)
+               var list = db.list_beers_and_rating(user_id)
+               if list == null then return new HttpResponse.server_error
 
-                       var db = new DB.open(db_path)
-                       var events = db.beer_events_since(since.to_sql_string)
-                       db.close
+               return new HttpResponse.ok(list)
+       end
+
+       # Post a review of `beer`
+       #
+       # review?token=a&beer=b&rating=0 -> true | BenitluxError
+       fun review(token: String, rating, beer: Int): HttpResponse
+       is restful do
+               var user_id = db.token_to_id(token)
+               if user_id == null then return new HttpResponse.invalid_token
+
+               db.post_review(user_id, beer, rating, "")
+
+               return new HttpResponse.ok(true)
+       end
+
+       # Set whether user of `token` follows `user_to`, by default set as follow
+       #
+       # follow?token=a&user_to=0 -> true | BenitluxError
+       fun follow(token: String, user_to: Int, follow: nullable Bool): HttpResponse
+       is restful do
+               var user = db.token_to_id(token)
+               if user == null then return new HttpResponse.invalid_token
+
+               if follow or else true then
+                       db.add_followed(user, user_to)
+               else db.remove_followed(user, user_to)
+
+               return new HttpResponse.ok(true)
+       end
+
+       # List followers of the user of `token`
+       #
+       # followers?token=a -> Array[UserAndFollowing] | BenitluxError | BenitluxError
+       fun followers(token: String): HttpResponse
+       is restful do
+               var user = db.token_to_id(token)
+               if user == null then return new HttpResponse.invalid_token
 
-                       if events == null then
-                               var response = new HttpResponse(400)
-                               response.body = "Bad request"
-                               return response
+               var users = db.followers(user)
+               if users == null then return new HttpResponse.server_error
+
+               return new HttpResponse.ok(users)
+       end
+
+       # List users followed by the user of `token`
+       #
+       # followed?token=a -> Array[UserAndFollowing] | BenitluxError
+       fun followed(token: String): HttpResponse
+       is restful do
+               var user = db.token_to_id(token)
+               if user == null then return new HttpResponse.invalid_token
+
+               var users = db.followed(user)
+               if users == null then return new HttpResponse.server_error
+
+               return new HttpResponse.ok(users)
+       end
+
+       # List friends of the user of `token`
+       #
+       # friends?token=a -> Array[UserAndFollowing] | BenitluxError
+       fun friends(token: String, n: nullable Int): HttpResponse
+       is restful do
+               var user = db.token_to_id(token)
+               var users = db.friends(user, n)
+               if users == null then return new HttpResponse.server_error
+
+               return new HttpResponse.ok(users)
+       end
+
+       # Check user in or out
+       #
+       # checkin?token=a -> true | BenitluxError
+       fun checkin(token: String, is_in: nullable Bool): HttpResponse
+       is restful do
+               var id = db.token_to_id(token)
+               if id == null then return new HttpResponse.invalid_token
+
+               # Register in DB
+               db.checkin(id, is_in or else true)
+
+               # Update followed_followers
+               var common_followers = db.followed_followers(id)
+
+               # Sent push notifications to connected reciprocal friends
+               if common_followers != null then
+                       for friend in common_followers do
+                               var conn = push_connections.get_or_null(friend.id)
+                               if conn != null then
+                                       push_connections.keys.remove friend.id
+                                       if not conn.closed then
+                                               var report = db.checkedin_followed_followers(friend.id)
+                                               var response = if report == null then
+                                                               new HttpResponse.server_error
+                                                       else new HttpResponse.ok(report)
+                                               conn.respond response
+                                               conn.close
+                                       end
+                               end
                        end
+               end
+
+               return new HttpResponse.ok(true)
+       end
+
+       # List users currently checked in among friends of the user of `token`
+       #
+       # checkedin?token=a -> Array[UserAndFollowing]
+       fun checkedin(token: String): HttpResponse
+       is restful do
+               var user_id = db.token_to_id(token)
+               if user_id == null then return new HttpResponse.invalid_token
+
+               var report = db.checkedin_followed_followers(user_id)
+               if report == null then return new HttpResponse.server_error
+               return new HttpResponse.ok(report)
+       end
 
-                       var stream = new StringWriter
-                       var serializer = new JsonSerializer(stream)
-                       serializer.serialize events
-                       var serialized = stream.to_s
+       # List beer changes since `date` with information in relation to the user of `token`
+       #
+       # since?token=a&date=date -> BeerEvents
+       fun since(token, date: nullable String): HttpResponse
+       is restful do
+               # Query DB
+               var user_id = db.token_to_id(token)
+               var list = db.list_beers_and_rating(user_id, date)
+               if list == null then return new HttpResponse.server_error
+
+               return new HttpResponse.ok(list)
+       end
+
+       # Fallback answer on errors
+       redef fun answer(request, turi) do return new HttpResponse.bad_request
+end
+
+# ---
+# Push notification
+
+# Benitlux push notification interface
+class BenitluxPushAction
+       super BenitluxAction
+
+       # Intercept the full answer to set aside the connection and complete it later
+       redef fun prepare_respond_and_close(request, turi, connection)
+       do
+               var token = request.string_arg("token")
 
-                       var response = new HttpResponse(200)
-                       response.body = serialized
-                       return response
+               var user = db.token_to_id(token)
+               if user == null then
+                       # Report errors right away
+                       var response =  new HttpResponse.invalid_token
+                       connection.respond response
+                       connection.close
+                       return
                end
 
-               var response = new HttpResponse(400)
-               response.body = "Bad request"
-               return response
+               # Set aside the connection
+               push_connections[user] = connection
        end
 end
 
+redef class Sys
+       # Connections left open for a push notification, organized per user id
+       private var push_connections = new Map[Int, HttpServer]
+end
+
+# ---
+# Misc services
+
 redef class Text
        # Rewrite the date represented by `self` in the format expected by SQLite
        private fun std_date: String
@@ -141,3 +341,37 @@ redef class Text
                return "{y}-{m}-{d}"
        end
 end
+
+redef class HttpResponse
+
+       # Respond with `data` in Json and a code 200
+       init ok(data: Serializable)
+       do
+               init 200
+               body = data.to_json_string
+       end
+
+       # Respond with a `BenitluxError` in JSON and a code 403
+       init invalid_token
+       do
+               init 403
+               var error = new BenitluxTokenError("Forbidden", "Invalid or outdated token.")
+               body = error.to_json_string
+       end
+
+       # Respond with a `BenitluxError` in JSON and a code 400
+       init bad_request
+       do
+               init 400
+               var error = new BenitluxError("Bad Request", "Application error, or it needs to be updated.")
+               body = error.to_json_string
+       end
+
+       # Respond with a `BenitluxError` in JSON and a code 500
+       init server_error
+       do
+               init 500
+               var error = new BenitluxError("Internal Server Error", "Server error, try again later.")
+               body = error.to_json_string
+       end
+end
index 27b8169..7878de2 100644 (file)
@@ -102,7 +102,7 @@ class Benitlux
                        print beers
                end
 
-               var db = new DB.open(db_path)
+               var db = new BenitluxDB.open(db_path)
 
                # Update the database with the beers of the day
                db.insert_beers_of_the_day beers
@@ -110,8 +110,14 @@ class Benitlux
                # Query the beer-related events of today
                var beer_events = db.beer_events_today
 
+               if beer_events == null then
+                       print_error "Failed to read beer events from the DB"
+                       db.close
+                       return
+               end
+
                # Generate the email title and content, store them in attributes
-               generate_email(beer_events)
+               generate_email beer_events
 
                # Save as sample email to file
                var f = new FileWriter.open(sample_email_path)
index 14cf229..73739d9 100644 (file)
@@ -22,7 +22,7 @@ import sqlite3
 import benitlux_model
 
 # The database of this project
-class DB
+class BenitluxDB
        super Sqlite3DB
 
        redef init open(path)
@@ -36,15 +36,16 @@ class DB
        fun create_tables
        do
                assert create_table("IF NOT EXISTS beers (name TEXT PRIMARY KEY, desc TEXT)") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
 
+               # Beers availability on each day
                assert create_table("IF NOT EXISTS daily (beer INTEGER, day DATE)") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
 
                assert create_table("IF NOT EXISTS subscribers (email TEXT UNIQUE PRIMARY KEY, joined DATETIME DEFAULT CURRENT_TIMESTAMP)") else
-                       print error or else "?"
+                       print_error error or else "?"
                end
        end
 
@@ -75,7 +76,7 @@ class DB
        end
 
        # Build and return a `BeerEvents` for today compared to the last weekday
-       fun beer_events_today: BeerEvents
+       fun beer_events_today: nullable BeerEvents
        do
                var tm = new Tm.localtime
                var last_weekday
@@ -84,7 +85,7 @@ class DB
                        last_weekday = "date('now', 'weekday 6', '-7 day')"
                else last_weekday = "date('now', '-1 day')"
 
-               return beer_events_since(last_weekday).as(not null) # This is used by daily
+               return beer_events_since(last_weekday)
        end
 
        # Build and return a `BeerEvents` for today compared to `prev_day`
index a186212..987047f 100644 (file)
@@ -18,6 +18,7 @@
 module benitlux_model
 
 import serialization
+import md5
 
 # A beer, with a name and description
 class Beer
@@ -85,3 +86,190 @@ class BeerEvents
                return ""
        end
 end
+
+# Basic user public information
+class User
+       serialize
+
+       # ID of this user in the DB
+       var id: Int
+
+       # Visible user name
+       var name: String
+
+       redef fun to_s do return "<{class_name} {id} '{name}'>"
+end
+
+# User public information in reference to a user (medium details level)
+class UserAndFollowing
+       serialize
+
+       # Basic user information
+       var user: User
+
+       # Favorite beers as a string
+       var favs: String
+
+       # Is the reference user following `user`?
+       var following: Bool
+
+       # Is the reference user followed by `user`?
+       var followed: Bool
+
+       # Time of the last check in, if `followed`
+       var last_check_in: nullable String = null
+end
+
+# Review by `author` on a `beer`
+class Review
+       serialize
+
+       # Basic author information
+       var author: User
+
+       # Basic beer information
+       var beer: Beer
+
+       # Rating out of 5
+       var rating: Int
+
+       # Text content of this review, may be empty
+       var text: String
+end
+
+# Statistics of the reviews on `beer`
+class BeerStats
+       serialize
+
+       # Basic beer information
+       var beer: Beer
+
+       # Average rating out of 5
+       var average: Float
+
+       # Number of reviews used for these statistics
+       var count: Int
+end
+
+# Beer information in reference to a user (medium detail level)
+class BeerAndRatings
+       serialize
+
+       # Basic beer information
+       var beer: Beer
+
+       # Global statistics on `beer`
+       var global: nullable BeerStats
+
+       # Statistics on `beer` among followed users only
+       var followed: nullable BeerStats
+
+       # Rating left by the referenced user, if any
+       var user_rating: nullable Int
+
+       # Do we predict this `beer` to go quickly?
+       var will_go_fast: Bool
+
+       # Data of appearance of this batch TODO
+       var batch: String
+
+       # Is this beer new since the last work day?
+       var is_new: Bool
+
+       # Has this beer been here since the last work day?
+       var is_fix: Bool
+
+       # Has this beer left the menu since the last work day?
+       var is_gone: Bool
+
+       # Noteworthy comments on this beer, often relative to a user
+       var badges: nullable Array[BeerBadge] = null is writable
+end
+
+# Noteworthy comment on a beer
+abstract class BeerBadge
+       serialize
+end
+
+# A beer is the favorite of `users`
+class FavoriteBeerBadge
+       super BeerBadge
+       serialize
+
+       # Name of the user fan of the beer
+       var users = new Array[String]
+end
+
+# A beer is similar to `beers`
+class SimilarBeerBadge
+       super BeerBadge
+       serialize
+
+       # Names of the similar beers
+       var beers = new Array[String]
+end
+
+# This beer is the best on the menu
+class BestBeerBadge
+       super BeerBadge
+       serialize
+end
+
+# Result of a login or signup request upon success
+class LoginResult
+       serialize
+
+       # Basic user information
+       var user: User
+
+       # Reference token
+       var token: String
+end
+
+# Report pushed to followed_followers when a user checks in or out
+class CheckinReport
+       serialize
+
+       # Users currently in
+       var users = new Array[User]
+end
+
+# Server or API usage error
+class BenitluxError
+       super Error
+       serialize
+
+       # Error message that can be shown to the user
+       var user_message: nullable String
+end
+
+# Client sent an invalid token
+class BenitluxTokenError
+       super BenitluxError
+       serialize
+end
+
+redef class Text
+       # Is `self` a valid user name?
+       fun name_is_ok: Bool
+       do
+               if length < 4 or length > 16 then return false
+
+               for c in chars do
+                       if c.is_whitespace or c.code_point == 0xFFFD then return false
+               end
+
+               return true
+       end
+
+       # Is `self` a valid password?
+       fun pass_is_ok: Bool do return length >= 6
+
+       # Hashed Benitlux password
+       fun pass_hash: String do return (to_s+benitlux_salt).md5
+end
+
+# Salt used on passwords
+#
+# Should be refined by user modules.
+fun benitlux_salt: String do return "Vive le ben!"
diff --git a/contrib/benitlux/src/benitlux_social.nit b/contrib/benitlux/src/benitlux_social.nit
new file mode 100644 (file)
index 0000000..ae409a8
--- /dev/null
@@ -0,0 +1,511 @@
+# 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.
+
+# Social features acting on the database
+module benitlux_social
+
+import nitcorn::token
+
+import benitlux_db
+
+redef class BenitluxDB
+
+       redef fun create_tables
+       do
+               super
+
+               # User and password table
+               # Contains only core user/account info. Extra preferences
+               # should be added to a different table.
+               assert create_table("IF NOT EXISTS users (name TEXT PRIMARY KEY, pass TEXT, email TEXT, joined DATETIME DEFAULT CURRENT_TIMESTAMP, last_checkin DATETIME)") else
+                       print_error "Create 'users' failed with: {error or else "?"}"
+               end
+
+               # User follow table
+               assert create_table("IF NOT EXISTS follows (user_from INTEGER, user_to INTEGER)") else
+                       print_error "Create 'follows' failed with: {error or else "?"}"
+               end
+
+               # Reviews table
+               # TODO the `text` block is reserved for future use.
+               assert create_table("""
+IF NOT EXISTS reviews (author INTEGER, beer INTEGER, posted DATETIME DEFAULT CURRENT_TIMESTAMP, rating INTEGER, text TEXT)
+               """) else
+                       print_error "Create 'reviews' failed with: {error or else "?"}"
+               end
+
+               assert execute("CREATE UNIQUE INDEX IF NOT EXISTS unique_reviews ON reviews(author, beer)") else
+                       print_error "Create 'unique_reviews' failed with: {error or else "?"}"
+               end
+
+               # Checkins table
+               # Hold the history of check ins and outs.
+               assert create_table("IF NOT EXISTS checkins (user INTEGER, time DATETIME DEFAULT CURRENT_TIMESTAMP, is_in BOOLEAN)") else
+                       print_error "Create 'checkins' failed with: {error or else "?"}"
+               end
+
+               # User tokens
+               # All tokens ever issued.
+               assert create_table("IF NOT EXISTS tokens (user INTEGER, token TEXT, last_access DATETIME DEFAULT CURRENT_TIMESTAMP)") else
+                       print_error "Create 'tokens' failed with: {error or else "?"}"
+               end
+
+               # View similar to the table `review` with only the most recent review per author & beer
+               assert execute("""
+CREATE VIEW IF NOT EXISTS latest_reviews AS
+       SELECT reviews.author, reviews.beer, reviews.rating, reviews.posted, text
+               FROM reviews, (SELECT author, beer, max(posted) as posted
+                       FROM reviews GROUP BY author, beer) AS uniqs
+               WHERE reviews.author = uniqs.author and
+                       reviews.beer = uniqs.beer and
+                       reviews.posted = uniqs.posted
+""") else
+                       print_error "Create 'latest_reviews' failed with: {error or else "?"}"
+               end
+
+               # Distance in the rating between each beers by each author
+               assert execute("""
+CREATE VIEW IF NOT EXISTS user_dist AS
+SELECT r0.author, r0.beer AS beer0, r1.beer AS beer1, ABS(r1.rating - r0.rating) AS dist
+FROM latest_reviews AS r0, latest_reviews as r1
+WHERE r0.author = r1.author""") else
+                       print_error "Create 'user_dist' failed with: {error or else "?"}"
+               end
+
+               # Average distance in the rating between each beers by all authors
+               assert execute("""
+CREATE VIEW IF NOT EXISTS global_dist AS
+SELECT beer0, beer1, COUNT(dist) AS count, AVG(dist) as average
+FROM user_dist
+GROUP BY beer0, beer1""") else
+                       print_error "Create 'global_dist' failed with: {error or else "?"}"
+               end
+       end
+
+       # Check if the login credentials are valid
+       #
+       # If valid, returns the username with the capitalization used at registration.
+       # Returns `null` on invalid password or anormal errors.
+       fun login(user, pass: String): nullable LoginResult
+       do
+               var stmt = select("ROWID, name FROM users WHERE lower({user.to_sql_string}) = lower(name) " +
+                                 "AND {pass.to_sql_string} = pass")
+               if stmt == null then
+                       print_error "Select 'login' failed with: {error or else "?"}"
+                       return null
+               end
+
+               for row in stmt do
+                       var user_id = row[0].to_i
+                       var token = new_token(user_id)
+                       var u = new User(user_id, row[1].to_s)
+                       return new LoginResult(u, token)
+               end
+               return null
+       end
+
+       # Get a new token and associate it to `user_id`
+       fun new_token(user_id: Int): String
+       do
+               loop
+                       var token = generate_token
+
+                       # Check if token already exists
+                       var stmt = select("ROWID FROM tokens WHERE token={token.to_sql_string}")
+                       assert stmt != null
+                       if stmt.iterator.to_a.not_empty then continue
+
+                       # Register token
+                       var res = insert("INTO tokens(user, token) VALUES({user_id}, {token.to_sql_string})")
+                       assert res
+
+                       # TODO merge the 2 requests to avoid race conditions,
+                       # if we ever share the BD between 2 servers/threads
+
+                       return token
+               end
+       end
+
+       # Get the user id associated to `token`, if any
+       #
+       # Accepts `null` token to simplify call sites, but always returns `null` in such cases.
+       fun token_to_id(token: nullable String): nullable Int
+       do
+               if token == null then return null
+
+               var stmt = select("user FROM tokens WHERE token={token.to_sql_string}")
+               if stmt == null then
+                       print_error "Select 'token_to_id' failed with: {error or else "?"}"
+                       return null
+               end
+
+               # TODO update token timestamp and platform/client hint of last connection.
+               # These informations could help detect malicious access to the account.
+
+               for row in stmt do return row[0].to_i
+               return null
+       end
+
+       # Get `User` data from the integer `id`
+       fun id_to_user(id: Int): nullable User
+       do
+               var stmt = select("name FROM users WHERE ROWID = {id}")
+               assert stmt != null
+
+               for row in stmt do return new User(id, row[0].to_s)
+               return null
+       end
+
+       # Try to sign up a new user, return `true` on success
+       fun signup(user, pass, email: String): nullable String
+       do
+               # Check if already in user
+               var stmt = select("ROWID FROM users WHERE lower({user.to_sql_string}) = lower(name)")
+               assert stmt != null else print_error "Select 'sign_up' failed with: {error or else "?"}"
+               if not stmt.iterator.to_a.is_empty then return "Username already in use"
+
+               # Check email use
+               stmt = select("ROWID FROM users WHERE lower({email.to_sql_string}) = lower(email)")
+               assert stmt != null else print_error "Select 'sign_up' failed with: {error or else "?"}"
+               if not stmt.iterator.to_a.is_empty then return "There's already an account with that email"
+
+               # Insert intro BD
+               assert insert("INTO users(name, pass, email) VALUES ({user.to_sql_string}, {pass.to_sql_string}, {email.to_sql_string})") else
+                       print_error "Insert 'sign_up' failed with: {error or else "?"}"
+               end
+
+               return null
+       end
+
+       # Post a review
+       fun post_review(author, beer, rating: Int, text: String)
+       do
+               assert insert("OR REPLACE INTO reviews(author, beer, rating, text) VALUES ({author}, {beer}, {rating}, {text.to_sql_string})") else
+                       print_error "Insert 'post_review' failed with: {error or else "?"}"
+               end
+       end
+
+       # Fetch stats on `beer`, if `followed_only` limit to the reviews of followed users
+       fun beer_stats(beer: Int, followed_only: nullable Int): nullable BeerStats
+       do
+               var sql = "avg(rating), count(rating) FROM reviews WHERE beer = {beer}"
+               if followed_only != null then
+                       sql += " AND (author = {followed_only} OR " +
+                         "author IN (SELECT user_to FROM follows WHERE user_from = {followed_only}))"
+               end
+
+               var stmt = select(sql)
+               assert stmt != null else print_error "Select 'beer_stats' failed with: {error or else "?"}"
+
+               var b = beer_from_id(beer)
+               if b == null then return null
+
+               for row in stmt do return new BeerStats(b, row[0].to_f, row[1].to_i)
+               return null
+       end
+
+       # Fetch the most recent rating left by `user_id` about `beer`
+       fun latest_rating(user_id, beer: Int): nullable Int
+       do
+               var stmt = select("rating FROM reviews WHERE author = {user_id} AND beer = {beer} ORDER BY ROWID DESC LIMIT 1")
+               assert stmt != null else print_error "Select 'rating' failed with: {error or else "?"}"
+               for row in stmt do return row[0].to_i
+               return null
+       end
+
+       # Register that `user_from` follows `user_to`
+       fun add_followed(user_from, user_to: Int)
+       do
+               assert insert("OR IGNORE INTO follows(user_from, user_to) VALUES ({user_from}, {user_to})") else
+                       print_error "Insert 'add_followed' failed with: {error or else "?"}"
+               end
+       end
+
+       # Register that `user_from` does not follow `user_to`
+       fun remove_followed(user_from, user_to: Int)
+       do
+               assert execute("DELETE FROM follows WHERE user_from = {user_from} AND user_to = {user_to}") else
+                       print_error "Delete 'remove_followed' failed with: {error or else "?"}"
+               end
+       end
+
+       # Does `user_from` follow `user_to`?
+       fun follows(user_from, user_to: Int): Bool
+       do
+               var stmt = select("ROWID FROM follows WHERE user_from = {user_from} AND user_to = {user_to}")
+               assert stmt != null else
+                       print_error "Select 'follows' failed with: {error or else "?"}"
+               end
+               for row in stmt do return true
+               return false
+       end
+
+       # List of users with a name similar to `pattern` in relation to `user_id`
+       fun search_users(pattern: String, user_id: nullable Int): nullable Array[UserAndFollowing]
+       do
+               # TODO a better search logic
+
+               var like_str = "'%{pattern.replace('\\', "\\\\").replace('\'', "''").replace("%", "\\%")}%'"
+               var stmt = select("ROWID, name FROM users WHERE name LIKE {like_str}")
+               assert stmt != null else print_error "Select 'search_users' failed with: {error or else "?"}"
+
+               var users = new Array[User]
+               for row in stmt do users.add(new User(row[0].to_i, row[1].to_s))
+
+               return user_to_user_and_following(users, user_id)
+       end
+
+       # List the followers of `user_id`
+       fun followers(user_id: Int): nullable Array[UserAndFollowing]
+       do
+               var stmt = select("ROWID, name FROM users WHERE ROWID in (SELECT user_from FROM follows WHERE user_to = {user_id})")
+               assert stmt != null else print_error "Select 'followers' failed with: {error or else "?"}"
+
+               var users = new Array[User]
+               for row in stmt do users.add(new User(row[0].to_i, row[1].to_s))
+
+               return user_to_user_and_following(users, user_id)
+       end
+
+       # List users followed by `user_id`
+       fun followed(user_id: Int): nullable Array[UserAndFollowing]
+       do
+               var stmt = select("ROWID, name FROM users WHERE ROWID in (SELECT user_to FROM follows WHERE user_from = {user_id})")
+               assert stmt != null else print_error "Select 'followed' failed with: {error or else "?"}"
+
+               var users = new Array[User]
+               for row in stmt do users.add(new User(row[0].to_i, row[1].to_s))
+
+               return user_to_user_and_following(users, user_id)
+       end
+
+       # List reciprocal friends of `user_id`
+       fun followed_followers(user_id: Int): nullable Array[User]
+       do
+               var stmt = select("ROWID, name FROM users WHERE " +
+                       "ROWID in (SELECT user_from FROM follows WHERE user_to = {user_id}) AND " +
+                       "ROWID in (SELECT user_to FROM follows WHERE user_from = {user_id})")
+               assert stmt != null else print_error "Select 'followed_followers' failed with: {error or else "?"}"
+
+               var users = new Array[User]
+               for row in stmt do users.add new User(row[0].to_i, row[1].to_s)
+
+               return users
+       end
+
+       # List `n` friends or recommendations (with information)
+       #
+       # If `user_id` is null, list only recommendations.
+       # The default value of `n` is 6 friends.
+       fun friends(user_id, n: nullable Int): nullable Array[UserAndFollowing]
+       do
+               var limit = n or else 6
+               var people = null
+               if user_id != null then
+                       people = followed_followers(user_id)
+                       if people == null then return null
+               else
+                       people = new Array[User]
+               end
+
+               if people.length < limit then
+                       # Add recommendations
+                       # TODO fill with recommendations from a few friends
+                       # TODO starting friends recommendations, popular clients, same visit patterns,
+                       # currently at the bar, official people (proprio, brewers, barmaids & barmen)
+
+                       limit -= people.length
+                       user_id = user_id or else -1
+
+                       # Recommend popular users
+                       var stmt = select("""
+ROWID, name, (SELECT count(*) FROM follows WHERE follows.user_to == users.ROWID) AS n_followers
+FROM users WHERE ROWID != {{{user_id}}}
+ORDER BY n_followers DESC LIMIT {{{limit}}}""")
+                       assert stmt != null else print_error "Select 'friends' failed with: {error or else "?"}"
+                       for row in stmt do people.add new User(row[0].to_i, row[1].to_s)
+
+               else if people.length > limit then
+
+                       # TODO pass the limit `n` to followed_followers
+                       people = people.subarray(0, limit)
+               end
+
+               return user_to_user_and_following(people, user_id)
+       end
+
+       # Convert `users` to an `Array[UserAndFollowing]` in relation to `user_id`
+       private fun user_to_user_and_following(users: Array[User], user_id: nullable Int): Array[UserAndFollowing]
+       do
+               var users_and_f = new Array[UserAndFollowing]
+               for user in users do
+                       var uaf
+                       var favs = favorite_beers(user.id)
+                       if user_id != null then
+                               var following = follows(user_id, user.id)
+                               var followed_by = follows(user.id, user_id)
+                               uaf = new UserAndFollowing(user, favs.join(", "), following, followed_by)
+                       else
+                               uaf = new UserAndFollowing(user, favs.join(", "), false, false)
+                       end
+                       users_and_f.add uaf
+               end
+               return users_and_f
+       end
+
+       # List the name of the favorite beers of `user_id`
+       fun favorite_beers(user_id: Int): Array[String]
+       do
+               var stmt = select("name FROM beers WHERE ROWID in (SELECT beer FROM reviews WHERE author = {user_id} AND rating = 5)")
+               assert stmt != null else print_error "Select 'rating' failed with: {error or else "?"}"
+
+               var beers = new Array[String]
+               for row in stmt do beers.add row[0].to_s
+               return beers
+       end
+
+       # List available beers and changes in relation to `user_from`
+       fun list_beers_and_rating(user_from: nullable Int, since: nullable String): nullable Array[BeerAndRatings]
+       do
+               # TODO replace old services `beer_events_since` and `beer_events_today` by a single* SQL call
+
+               var events
+               if since != null then
+                       events = beer_events_since(since)
+               else events = beer_events_today
+               if events == null then return null
+
+               # New since the last weekday
+               var all = new Array[BeerAndRatings]
+               for beer in events.new_beers do
+                       var global = beer_stats(beer.id)
+                       var friend = null
+                       var rating = null
+                       if user_from != null then
+                               friend = beer_stats(beer.id, user_from)
+                               rating = latest_rating(user_from, beer.id)
+                       end
+                       all.add new BeerAndRatings(beer, global, friend, rating,
+                               false, "TODO batch", true, false, false)
+               end
+
+               # Still here since the last weekday
+               for beer in events.fix_beers do
+                       var global = beer_stats(beer.id)
+                       var friend = null
+                       var rating = null
+                       if user_from != null then
+                               friend = beer_stats(beer.id, user_from)
+                               rating = latest_rating(user_from, beer.id)
+                       end
+                       all.add new BeerAndRatings(beer, global, friend, rating,
+                               false, "TODO batch", false, true, false)
+               end
+
+               # Apply badges
+               for beer in all do
+                       var badges = badges(beer.beer.id, user_from)
+                       if badges.not_empty then beer.badges = badges
+               end
+
+               return all
+       end
+
+       # Badges (or comments, of social description) on `beer` relative to `user_from`
+       fun badges(beer: Int, user_from: nullable Int): Array[BeerBadge]
+       do
+               var badges = new Array[BeerBadge]
+
+               # Overall favorite available today
+               var rows = select("""
+beer
+FROM (
+       SELECT beer, AVG(rating) AS average, COUNT(rating) AS count
+       FROM latest_reviews
+       WHERE beer IN (
+               SELECT beer FROM daily WHERE day IN (
+                       SELECT max(day) FROM daily))
+       GROUP BY beer)
+WHERE count > 1 ORDER BY average DESC LIMIT 1""")
+               if rows == null then
+                       print_error "Select 'overall-favorite' failed with: {error or else "?"}"
+               else
+                       for row in rows do
+                               if row[0].to_i == beer then badges.add new BestBeerBadge
+                       end
+               end
+
+               if user_from != null then
+                       # Favorite of friend
+                       rows = select("""
+name FROM users
+WHERE ROWID in (SELECT user_to FROM follows WHERE user_from = {{{user_from}}})
+       AND ROWID in (SELECT author FROM latest_reviews WHERE beer = {{{beer}}} AND rating = 5)""")
+                       if rows == null then
+                               print_error "Select 'friend-favorite' failed with: {error or else "?"}"
+                       else
+                               var badge = new FavoriteBeerBadge
+                               for row in rows do badge.users.add row[0].to_s
+                               if badge.users.not_empty then badges.add badge
+                       end
+
+                       # Rated similarly to a favorite by most (approximative description)
+                       rows = select("""
+(SELECT name FROM beers WHERE beers.ROWID = beer1)
+FROM global_dist WHERE beer0 = {{{beer}}} AND beer0 != beer1
+       AND count > 1 AND average < 2.0
+       AND beer1 IN (SELECT beer FROM latest_reviews WHERE
+               author = {{{user_from}}} AND rating = 5)
+ORDER BY average LIMIT 1""")
+                       if rows == null then
+                               print_error "Select 'similar' failed with: {error or else "?"}"
+                       else
+                               var badge = new SimilarBeerBadge
+                               for row in rows do badge.beers.add row[0].to_s
+                               if badge.beers.not_empty then badges.add badge
+                       end
+               end
+
+               # TODO more for people with no friends or favorites
+
+               return badges
+       end
+
+       # Register `user_id` as being in or out
+       fun checkin(user_id: Int, checkin: Bool)
+       do
+               var res = insert("INTO checkins(user, is_in) VALUES({user_id}, {if checkin then 1 else 0})")
+               if not res then print_error "Insert 'checkin' failed with: {error or else "?"}"
+       end
+
+       # List currently checked in reciprocal friends of `user_id`
+       fun checkedin_followed_followers(user_id: Int): nullable CheckinReport
+       do
+               var sql = """
+ROWID, name FROM users
+WHERE 1 in (SELECT is_in FROM checkins WHERE user = users.ROWID ORDER BY ROWID DESC LIMIT 1)
+       AND ROWID in (SELECT user_from FROM follows WHERE user_to = {user_id})
+       AND ROWID in (SELECT user_to FROM follows WHERE user_from = {user_id})"""
+
+               var stmt = select(sql)
+               if stmt == null then
+                       print_error "Select 'checkedin_followed_followers' failed with: {error or else "?"}"
+                       return null
+               end
+
+               var report = new CheckinReport
+               for row in stmt do report.users.add new User(row[0].to_i, row[1].to_s)
+               return report
+       end
+end
index 9739241..d5bab54 100644 (file)
@@ -20,16 +20,30 @@ module benitlux_web
 import benitlux_model
 import benitlux_view
 import benitlux_controller
+import benitlux_restful
 
 # Listening interface
 fun iface: String do return "localhost:8080"
 
+# Sqlite3 database
+var db_path = "benitlux_sherbrooke.db"
+var db = new BenitluxDB.open(db_path)
+var db_error = db.error
+if db_error != null then
+       print_error db_error
+       exit 1
+end
+
+# Setup routes
 var vh = new VirtualHost(iface)
-vh.routes.add new Route("/rest/", new BenitluxRESTAction)
-vh.routes.add new Route(null, new BenitluxSubscriptionAction)
+vh.routes.add new Route("/rest/", new BenitluxRESTAction(db))
+vh.routes.add new Route("/push/", new BenitluxPushAction(db))
+vh.routes.add new Route(null, new BenitluxSubscriptionAction(db))
 
 var factory = new HttpFactory.and_libevent
 factory.config.virtual_hosts.add vh
 
 print "Launching server on http://{iface}/"
 factory.run
+
+db.close
index b840e3d..afa3a7f 100644 (file)
@@ -24,7 +24,7 @@ module correct
 
 import benitlux_db
 
-redef class DB
+redef class BenitluxDB
        # Path to file with the corrections
        private var corrections_path = "benitlux_corrections.txt"
 
index e1ba5aa..1f31328 100644 (file)
@@ -58,7 +58,7 @@ var rest = opts.rest
 # Use the local DB
 var db_path = "benitlux_sherbrooke.db"
 if rest.not_empty then db_path = rest.first
-var db = new DB.open(db_path)
+var db = new BenitluxDB.open(db_path)
 
 # All known beers
 var beers = db.beers
index a0acee0..e985891 100644 (file)
@@ -115,9 +115,10 @@ private extern class NativeMediaPlayer in "Java" `{ android.media.MediaPlayer `}
                }
        `}
 
-       fun create(context: NativeActivity, id: Int): NativeMediaPlayer in "Java" `{
+       new create(context: NativeActivity, id: Int): NativeMediaPlayer
+       in "Java" `{
                try {
-                       return self.create(context, (int)id);
+                       return MediaPlayer.create(context, (int)id);
                }catch(Exception e) {
                        return null;
                }
@@ -367,8 +368,7 @@ class MediaPlayer
 
        # Init the mediaplayer with a sound resource id
        init from_id(context: NativeActivity, id: Int) do
-               self.nmedia_player = new NativeMediaPlayer
-               self.nmedia_player = nmedia_player.create(context, id)
+               self.nmedia_player = new NativeMediaPlayer.create(context, id)
                if self.nmedia_player.is_java_null then
                        self.error = new Error("Failed to create the MediaPlayer")
                        self.sound = new Music.priv_init(id, self, self.error)
@@ -388,7 +388,8 @@ class MediaPlayer
                        reset
                        destroy
                end
-               self.nmedia_player = self.nmedia_player.create(context, id)
+
+               self.nmedia_player = new NativeMediaPlayer.create(context, id)
                if self.nmedia_player.is_java_null then
                        self.error = new Error("Failed to load a sound")
                        self.sound = new Music.priv_init(id, self, new Error("Sound loading failed"))
index 859808a..b38eb27 100644 (file)
@@ -495,18 +495,21 @@ abstract class Text
                end
        end
 
-       # Justify a self in a space of `length`
+       # Justify `self` in a space of `length`
        #
        # `left` is the space ratio on the left side.
        # * 0.0 for left-justified (no space at the left)
        # * 1.0 for right-justified (all spaces at the left)
        # * 0.5 for centered (half the spaces at the left)
        #
+       # `char`, or `' '` by default, is repeated to pad the empty space.
+       #
        # Examples
        #
        #     assert "hello".justify(10, 0.0)  == "hello     "
        #     assert "hello".justify(10, 1.0)  == "     hello"
        #     assert "hello".justify(10, 0.5)  == "  hello   "
+       #     assert "hello".justify(10, 0.5, '.') == "..hello..."
        #
        # If `length` is not enough, `self` is returned as is.
        #
@@ -515,13 +518,14 @@ abstract class Text
        # REQUIRE: `left >= 0.0 and left <= 1.0`
        # ENSURE: `self.length <= length implies result.length == length`
        # ENSURE: `self.length >= length implies result == self`
-       fun justify(length: Int, left: Float): String
+       fun justify(length: Int, left: Float, char: nullable Char): String
        do
+               var pad = (char or else ' ').to_s
                var diff = length - self.length
                if diff <= 0 then return to_s
                assert left >= 0.0 and left <= 1.0
                var before = (diff.to_f * left).to_i
-               return " " * before + self + " " * (diff-before)
+               return pad * before + self + pad * (diff-before)
        end
 
        # Mangle a string to be a unique string only made of alphanumeric characters and underscores.
index 9438a0a..4f9df3c 100644 (file)
@@ -609,9 +609,8 @@ redef class Map[K, V]
                                        v.stream.write ", "
                                else first = false
 
-                               if key == null then key = "null"
-
-                               v.stream.write key.to_s.to_json
+                               var k = key or else "null"
+                               v.stream.write k.to_s.to_json
                                v.stream.write ": "
                                if not v.try_to_serialize(val) then
                                        v.warn("element of type {val.class_name} is not serializable.")
index 95cd31d..1233db4 100644 (file)
@@ -208,11 +208,23 @@ pep8_vh.routes.add new Route(null, new FileServer("/var/www/pep8/"))
 #
 # It has 2 actions/Web interfaces. The Web user UI to subscribe and unsubscribe
 # to the mailing list. And the RESTful interface used by the Android app.
-var benitlux_sub = new BenitluxSubscriptionAction
-var benitlux_rest = new BenitluxRESTAction
+
+var db_path = "benitlux_sherbrooke.db"
+var benitlux_db = new BenitluxDB.open(db_path)
+var db_error = benitlux_db.error
+if db_error != null then
+       print_error db_error
+       exit 1
+end
+
+var benitlux_sub = new BenitluxSubscriptionAction(benitlux_db)
+var benitlux_rest = new BenitluxRESTAction(benitlux_db)
+var benitlux_push = new BenitluxPushAction(benitlux_db)
 default_vh.routes.add new Route("/benitlux/rest/", benitlux_rest)
+default_vh.routes.add new Route("/benitlux/push/", benitlux_push)
 default_vh.routes.add new Route("/benitlux", benitlux_sub)
 benitlux_vh.routes.add new Route("/rest/", benitlux_rest)
+benitlux_vh.routes.add new Route("/push/", benitlux_push)
 benitlux_vh.routes.add new Route("/static/", shared_file_server)
 benitlux_vh.routes.add new Route(null, benitlux_sub)
 
index 15fa9cc..4d54aa9 100644 (file)
@@ -26,6 +26,7 @@ import md5
 import server_config
 import http_request
 import http_response
+import token
 
 # A server side session
 class Session
@@ -35,39 +36,20 @@ class Session
 
        init
        do
-               self.id_hash = sys.next_session_hash
-               sys.sessions[self.id_hash] = self
+               loop
+                       var token = generate_token
+                       if sys.sessions.keys.has(token) then continue
+
+                       sys.sessions[token] = self
+                       self.id_hash = token
+                       break
+               end
        end
 end
 
 redef class Sys
        # Active sessions
        var sessions = new HashMap[String, Session]
-
-       # Get the next session hash available, and increment the session id cache
-       fun next_session_hash: String
-       do
-               var id = next_session_id_cache
-               # On firt evocation, seed the pseudo random number generator
-               if id == null then
-                       srand
-                       id = 1000000.rand
-               end
-
-               next_session_id_cache = id + 1
-
-               return id.to_id_hash
-       end
-
-       private var next_session_id_cache: nullable Int = null
-
-       # Salt used to hash the session id
-       protected var session_salt = "Default nitcorn session salt"
-end
-
-redef class Int
-       # Salt and hash and id to use as `Session.id_hash`
-       private fun to_id_hash: String do return (self.to_s+sys.session_salt).md5
 end
 
 redef class HttpResponse
diff --git a/lib/nitcorn/token.nit b/lib/nitcorn/token.nit
new file mode 100644 (file)
index 0000000..31aefd6
--- /dev/null
@@ -0,0 +1,71 @@
+# 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 `generate_token` service, independent of the rest of the nitcorn framework
+module token
+
+import base64
+
+# Generate a random token of `length` chars long
+#
+# The default value of `length` is 64, but it may change in the future.
+#
+# On Linux, `/dev/urandom` is used as source of random data.
+# On other platforms, `rand` is used to generate pseudo-random data.
+# using `rand` can be forced with `force_rand`.
+#
+# Uses `base64` to represent random bytes in the readable ASCII range.
+#
+# ~~~
+# srand_from 0
+# assert generate_token(4,  force_rand=true) == "AMzV"
+# assert generate_token(8,  force_rand=true) == "G8Oibf+s"
+# assert generate_token(16, force_rand=true) == "VUCPPlWLR8lZQvbb"
+# assert generate_token(32, force_rand=true) == "JtJB9vJd+u3c6dyL+Q8U5IKZIueRoZ6h"
+# assert generate_token(    force_rand=true) == "+59Ev7/35sOPeq1y8mI5+npAE1SYbfhVgGeDAq2vo5N3mnWxSJvucd6H3HwxT8v8"
+# ~~~
+fun generate_token(length: nullable Int, force_rand: nullable Bool): String
+do
+       length = length or else 64
+
+       # TODO generate more entropy
+
+       # Generate random bytes
+       var bin_length = ((length.to_f/4.0).ceil * 3.0).to_i
+       var bytes = null
+
+       var linux_random = "/dev/urandom"
+       if linux_random.file_exists then
+               # Use Linux's random number generator
+               var reader = new FileReader.open(linux_random)
+               bytes = reader.read_bytes(bin_length)
+               reader.close
+       end
+
+       # TODO other operating systems random number generators
+
+       # Fallback
+       if bytes == null or bytes.length != bin_length or force_rand == true then
+               bytes = new Bytes.with_capacity(bin_length)
+               for i in bin_length.times do bytes.add 256.rand.to_b
+       end
+
+       # Encode in base64 so it is readable
+       var str = bytes.encode_base64.to_s
+       if str.length > length then
+               # This happens if `length % 4 != 0`
+               str = str.substring(0, length)
+       end
+       return str
+end
index 8403ea7..4763061 100644 (file)
@@ -60,7 +60,45 @@ class PerfMap
                return ts
        end
 
-       redef fun to_s do return "* " + join("\n* ", ": ")
+       redef fun to_s
+       do
+               var prec = 3
+
+               var table = new Map[String, Array[String]]
+               for event, stats in self do
+                       table[event] = [event,
+                               stats.min.to_precision(prec),
+                               stats.max.to_precision(prec),
+                               stats.avg.to_precision(prec),
+                               stats.sum.to_precision(prec),
+                               stats.count.to_s]
+               end
+
+               var widths = [0] * 6
+               for event, row in table do
+                       for i in row.length.times do
+                               widths[i] = widths[i].max(row[i].length)
+                       end
+               end
+
+               var s = "# {"Event".justify(widths[0], 0.0)} {"min".justify(widths[1], 0.5)} {"max".justify(widths[2], 0.5)} {"avg".justify(widths[3], 0.5)} {"sum".justify(widths[4], 0.5)} {"count".justify(widths[5], 0.5)}\n"
+
+               var sorted_events = table.keys.to_a
+               alpha_comparator.sort sorted_events
+               for event in sorted_events do
+                       var row = table[event]
+                       s += "*"
+                       for c in row.length.times do
+                               var cell = row[c]
+                               s += " "
+                               if c == 0 then
+                                       s += cell.justify(widths[c], 0.0, '.')
+                               else s += cell.justify(widths[c], 1.0)
+                       end
+                       s += "\n"
+               end
+               return s
+       end
 end
 
 # Statistics on wall clock execution time of a category of events by `name`
index dd45211..f441545 100644 (file)
@@ -66,14 +66,20 @@ extern class Sqlite3Code `{int`}
        new done `{ return SQLITE_DONE; `} #       101  /* sqlite3_step() has finished executing */
        fun is_done: Bool `{ return self == SQLITE_DONE; `}
 
-       redef fun to_s import NativeString.to_s `{
+       redef fun to_s
+       do
+               var err = native_to_s
+               if err.address_is_null then return "Unknown error"
+               return err.to_s
+       end
+
+       private fun native_to_s: NativeString `{
 #if SQLITE_VERSION_NUMBER >= 3007015
                char *err = (char *)sqlite3_errstr(self);
 #else
                char *err = "sqlite3_errstr supported only by version >= 3.7.15";
 #endif
-               if (err == NULL) err = "";
-               return NativeString_to_s(err);
+               return err;
        `}
 end
 
@@ -133,9 +139,9 @@ end
 extern class NativeSqlite3 `{sqlite3 *`}
 
        # Open a connection to a database in UTF-8
-       new open(filename: String) import String.to_cstring, set_sys_sqlite_open_error `{
+       new open(filename: NativeString) import set_sys_sqlite_open_error `{
                sqlite3 *self = NULL;
-               int err = sqlite3_open(String_to_cstring(filename), &self);
+               int err = sqlite3_open(filename, &self);
                NativeSqlite3_set_sys_sqlite_open_error(self, (void*)(long)err);
                // The previous cast is a hack, using non pointers in extern classes is not
                // yet in the spec of the FFI.
index 40dcdad..1d6287a 100644 (file)
@@ -35,7 +35,7 @@ class Sqlite3DB
        # Open a connection to the database file at `path`
        init open(path: Text)
        do
-               init(new NativeSqlite3.open(path.to_s))
+               init(new NativeSqlite3.open(path.to_cstring))
                if native_connection.is_valid then is_open = true
        end
 
@@ -282,16 +282,18 @@ end
 interface Sqlite3Data end
 
 redef universal Int super Sqlite3Data end
+
 redef universal Float super Sqlite3Data end
+
 redef class String
        super Sqlite3Data
 
-       # Return `self` between `'`s and escaping any extra `'`
+       # Return `self` between `'`s, escaping `\` and `'`
        #
        #     assert "'; DROP TABLE students".to_sql_string == "'''; DROP TABLE students'"
        fun to_sql_string: String
        do
-               return "'{self.replace('\'', "''")}'"
+               return "'{self.replace('\\', "\\\\").replace('\'', "''")}'"
        end
 end
 
index 0e40831..14ae7d4 100644 (file)
@@ -1273,17 +1273,21 @@ abstract class AbstractCompilerVisitor
        do
                mtype = self.anchor(mtype)
                var valmtype = value.mcasttype
+
+               # Do nothing if useless autocast
                if valmtype.is_subtype(self.compiler.mainmodule, null, mtype) then
                        return value
                end
 
+               # Just as_not_null if the target is not nullable.
+               #
+               # eg `nullable PreciseType` adapted to `Object` gives precisetype.
                if valmtype isa MNullableType and valmtype.mtype.is_subtype(self.compiler.mainmodule, null, mtype) then
-                       var res = new RuntimeVariable(value.name, valmtype, valmtype.mtype)
-                       return res
-               else
-                       var res = new RuntimeVariable(value.name, valmtype, mtype)
-                       return res
+                       mtype = valmtype.mtype
                end
+
+               var res = new RuntimeVariable(value.name, value.mtype, mtype)
+               return res
        end
 
        # Generate a super call from a method definition
@@ -1351,13 +1355,18 @@ abstract class AbstractCompilerVisitor
 
        # Checks
 
+       # Can value be null? (according to current knowledge)
+       fun maybenull(value: RuntimeVariable): Bool
+       do
+               return value.mcasttype isa MNullableType or value.mcasttype isa MNullType
+       end
+
        # Add a check and an abort for a null receiver if needed
        fun check_recv_notnull(recv: RuntimeVariable)
        do
                if self.compiler.modelbuilder.toolcontext.opt_no_check_null.value then return
 
-               var maybenull = recv.mcasttype isa MNullableType or recv.mcasttype isa MNullType
-               if maybenull then
+               if maybenull(recv) then
                        self.add("if (unlikely({recv} == NULL)) \{")
                        self.add_abort("Receiver is null")
                        self.add("\}")
@@ -3110,7 +3119,7 @@ redef class AAttrPropdef
                        assert arguments.length == 2
                        var recv = arguments.first
                        var arg = arguments[1]
-                       if is_optional then
+                       if is_optional and v.maybenull(arg) then
                                var value = v.new_var(self.mpropdef.static_mtype.as(not null))
                                v.add("if ({arg} == NULL) \{")
                                v.assign(value, evaluate_expr(v, recv))
@@ -3532,6 +3541,9 @@ redef class AOrElseExpr
        do
                var res = v.new_var(self.mtype.as(not null))
                var i1 = v.expr(self.n_expr, null)
+
+               if not v.maybenull(i1) then return i1
+
                v.add("if ({i1}!=NULL) \{")
                v.assign(res, i1)
                v.add("\} else \{")
@@ -3783,7 +3795,7 @@ redef class AAsNotnullExpr
                var i = v.expr(self.n_expr, null)
                if v.compiler.modelbuilder.toolcontext.opt_no_check_assert.value then return i
 
-               if i.mtype.is_c_primitive then return i
+               if not v.maybenull(i) then return i
 
                v.add("if (unlikely({i} == NULL)) \{")
                v.add_abort("Cast failed")
index b0d5c27..0ac6e66 100644 (file)
@@ -448,6 +448,10 @@ class GlobalCompilerVisitor
                if args.first.mtype.is_c_primitive then
                        var mclasstype = args.first.mtype.as(MClassType)
                        if not self.compiler.runtime_type_analysis.live_types.has(mclasstype) then
+                               self.add("/* skip, dead class {mclasstype} */")
+                               return res
+                       end
+                       if not mclasstype.has_mproperty(self.compiler.mainmodule, m) then
                                self.add("/* skip, no method {m} */")
                                return res
                        end
index 267eae0..362bb52 100644 (file)
@@ -1399,8 +1399,7 @@ 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 or recv.mcasttype isa MNullType) and consider_null
-               if maybenull then
+               if maybenull(recv) and consider_null then
                        self.add("if ({recv} == NULL) \{")
                        if mmethod.name == "==" or mmethod.name == "is_same_instance" then
                                res = self.new_var(bool_type)
index d417d9e..9236d99 100644 (file)
@@ -1012,9 +1012,6 @@ redef class AMethPropdef
                                return v.int32_instance(recvval.to_i32)
                        else if pname == "to_u32" then
                                return v.uint32_instance(recvval.to_u32)
-                       else if pname == "rand" then
-                               var res = recvval.rand
-                               return v.int_instance(res)
                        end
                else if cname == "Byte" then
                        var recvval = args[0].to_b
@@ -1140,8 +1137,6 @@ redef class AMethPropdef
                                return v.float_instance(args[0].to_f.log)
                        else if pname == "pow" then
                                return v.float_instance(args[0].to_f.pow(args[1].to_f))
-                       else if pname == "rand" then
-                               return v.float_instance(args[0].to_f.rand)
                        else if pname == "abs" then
                                return v.float_instance(args[0].to_f.abs)
                        else if pname == "hypot_with" then
@@ -1848,7 +1843,13 @@ redef class AWithExpr
                v.callsite(method_start, [expr])
                v.stmt(self.n_block)
                v.is_escape(self.break_mark) # Clear the break
+
+               # Execute the finally without an escape
+               var old_mark = v.escapemark
+               v.escapemark = null
                v.callsite(method_finish, [expr])
+               # Restore the escape unless another escape was provided
+               if v.escapemark == null then v.escapemark = old_mark
        end
 end
 
index 010a944..3e5865b 100644 (file)
@@ -117,6 +117,7 @@ redef class AExpr
                super
                if other isa AExpr then
                        if other.implicit_cast_to == null then other.implicit_cast_to = implicit_cast_to
+                       other.vararg_decl = vararg_decl
                end
        end
 end
diff --git a/tests/base_covar_adapt.nit b/tests/base_covar_adapt.nit
new file mode 100644 (file)
index 0000000..54f60bd
--- /dev/null
@@ -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.
+
+import core::kernel
+
+class A[E]
+       fun foo(e: E): E
+       do
+               if e == null then e = true
+               return e
+       end
+end
+
+fun foo(a: A[nullable Object], o: nullable Object) do
+       a.foo(o).as(not null).output
+end
+
+foo(new A[nullable Object], 1.0)
+foo(new A[nullable Float], 1.0)
+foo(new A[Float], 1.0)
+
+foo(new A[nullable Object], null)
+#alt1#foo(new A[nullable Float], null)
+#alt2#foo(new A[Float], null)
diff --git a/tests/base_gen_nest.nit b/tests/base_gen_nest.nit
new file mode 100644 (file)
index 0000000..13f8105
--- /dev/null
@@ -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.
+
+import core::kernel
+
+class G[E: Object]
+       var e: E
+       fun foo: Object
+       do
+               var e = self.e
+               if e isa G[Object] then
+                       return e.foo
+               end
+               return e
+       end
+end
+
+var g1 = new G[Float](1.0)
+var g2 = new G[G[Float]](g1)
+g2.foo.output
diff --git a/tests/base_vararg_transform.nit b/tests/base_vararg_transform.nit
new file mode 100644 (file)
index 0000000..732da49
--- /dev/null
@@ -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.
+
+fun foo(i: Object...) do print i
+
+var n: nullable Object = null
+
+foo(1, 1)
+
+foo((false or true) and true, 2)
+foo(n or else 1, 3)
+foo(if true then 1 else -1, 4)
+
+foo([1..3], 5)
+foo([1,2,3], 6)
+foo([for i in [0..2] do i + 1], 7)
diff --git a/tests/base_with2.nit b/tests/base_with2.nit
new file mode 100644 (file)
index 0000000..d357cdd
--- /dev/null
@@ -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.
+
+import core::kernel
+
+class Foo
+       var zz = 0
+       fun work(i: Int) do
+               var a = new Foo
+               i.output
+       end
+       fun start do work 1
+       fun finish do work 4
+end
+
+do
+       with f = new Foo do
+               f.work 2
+               if true then break label
+               f.work 30
+       end
+       50.output
+end label
+6.output
diff --git a/tests/sav/base_covar_adapt.res b/tests/sav/base_covar_adapt.res
new file mode 100644 (file)
index 0000000..5e640b5
--- /dev/null
@@ -0,0 +1,4 @@
+1.000000
+1.000000
+1.000000
+true
diff --git a/tests/sav/base_covar_adapt_alt1.res b/tests/sav/base_covar_adapt_alt1.res
new file mode 100644 (file)
index 0000000..0b0273c
--- /dev/null
@@ -0,0 +1,5 @@
+Runtime error: Cast failed. Expected `E`, got `Bool` (alt/base_covar_adapt_alt1.nit:20)
+1.000000
+1.000000
+1.000000
+true
diff --git a/tests/sav/base_covar_adapt_alt2.res b/tests/sav/base_covar_adapt_alt2.res
new file mode 100644 (file)
index 0000000..bc3ebbd
--- /dev/null
@@ -0,0 +1,5 @@
+Runtime error: Cast failed. Expected `E`, got `null` (alt/base_covar_adapt_alt2.nit:18)
+1.000000
+1.000000
+1.000000
+true
diff --git a/tests/sav/base_gen_nest.res b/tests/sav/base_gen_nest.res
new file mode 100644 (file)
index 0000000..961b372
--- /dev/null
@@ -0,0 +1 @@
+1.000000
diff --git a/tests/sav/base_vararg_transform.res b/tests/sav/base_vararg_transform.res
new file mode 100644 (file)
index 0000000..12a875f
--- /dev/null
@@ -0,0 +1,7 @@
+[1,1]
+[true,2]
+[1,3]
+[1,4]
+[[1,2,3],5]
+[[1,2,3],6]
+[[1,2,3],7]
diff --git a/tests/sav/base_with2.res b/tests/sav/base_with2.res
new file mode 100644 (file)
index 0000000..76fda2f
--- /dev/null
@@ -0,0 +1,4 @@
+1
+2
+4
+6
diff --git a/tests/sav/nitc-common/fixme/base_with2.res b/tests/sav/nitc-common/fixme/base_with2.res
new file mode 100644 (file)
index 0000000..e47fd69
--- /dev/null
@@ -0,0 +1,3 @@
+1
+2
+6
diff --git a/tests/sav/nitce/base_covar_adapt_alt1.res b/tests/sav/nitce/base_covar_adapt_alt1.res
new file mode 100644 (file)
index 0000000..326b920
--- /dev/null
@@ -0,0 +1,5 @@
+1.000000
+1.000000
+1.000000
+true
+true
diff --git a/tests/sav/nitce/base_covar_adapt_alt2.res b/tests/sav/nitce/base_covar_adapt_alt2.res
new file mode 100644 (file)
index 0000000..326b920
--- /dev/null
@@ -0,0 +1,5 @@
+1.000000
+1.000000
+1.000000
+true
+true
diff --git a/tests/sav/nitcg/fixme/base_covar_adapt_alt2.res b/tests/sav/nitcg/fixme/base_covar_adapt_alt2.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
diff --git a/tests/sav/nitcg/test_text_stat.res b/tests/sav/nitcg/test_text_stat.res
deleted file mode 100644 (file)
index e28137f..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-Hello world !
-Usage of Strings:
-
-Allocations, by type:
-               
-       -FlatString = 29
-       -FlatBuffer = 2
-       -Concat = 0
-       -RopeBuffer = 0
-
-Calls to length, by type:
-       FlatString = 18 (cache misses 5, 27.77%)
-Indexed accesses, by type:
-       FlatString = 8
-Calls to bytelen for each type:
-       FlatString = 61
-Calls to position for each type:
-       FlatString = 17
-Calls to bytepos for each type:
-       FlatString = 9
-Calls to first_byte on FlatString 191
-Calls to last_byte on FlatString 19
-FlatStrings allocated with length 78 (86.813%)
-Length of travel for index distribution:
-* null = 11 => occurences 73.333%, cumulative 73.333% 
-* 1 = 8 => occurences 27.586%, cumulative 65.517% 
-Byte length of the FlatStrings created:
-* null = 6 => occurences 4.444%, cumulative 4.444% 
-* 1 = 24 => occurences 16.107%, cumulative 20.134% 
-* 2 = 30 => occurences 18.405%, cumulative 36.81% 
-* 3 = 29 => occurences 16.292%, cumulative 50.0% 
-* 4 = 5 => occurences 2.591%, cumulative 48.705% 
-* 5 = 20 => occurences 9.615%, cumulative 54.808% 
-* 6 = 25 => occurences 11.211%, cumulative 62.332% 
-* 9 = 1 => occurences 0.42%, cumulative 58.824% 
-* 10 = 9 => occurences 3.557%, cumulative 58.893% 
-* 11 = 2 => occurences 0.746%, cumulative 56.343% 
-* 12 = 1 => occurences 0.355%, cumulative 53.901% 
-* 13 = 1 => occurences 0.339%, cumulative 51.864% 
-* 14 = 1 => occurences 0.325%, cumulative 50.0% 
-* 15 = 7 => occurences 2.181%, cumulative 50.156% 
-* 16 = 5 => occurences 1.488%, cumulative 49.405% 
-* 17 = 1 => occurences 0.285%, cumulative 47.578% 
-* 25 = 2 => occurences 0.549%, cumulative 46.429% 
-* 26 = 1 => occurences 0.265%, cumulative 44.974% 
-* 31 = 2 => occurences 0.512%, cumulative 43.99% 
-* 32 = 1 => occurences 0.247%, cumulative 42.716% 
-* 33 = 1 => occurences 0.239%, cumulative 41.627% 
-* 34 = 2 => occurences 0.464%, cumulative 40.835% 
-* 35 = 1 => occurences 0.225%, cumulative 39.775% 
-* 37 = 1 => occurences 0.218%, cumulative 38.865% 
-* 39 = 1 => occurences 0.212%, cumulative 38.004% 
-* 40 = 1 => occurences 0.207%, cumulative 37.19% 
-* 43 = 1 => occurences 0.201%, cumulative 36.419% 
-* 46 = 1 => occurences 0.196%, cumulative 35.686% 
-* 51 = 20 => occurences 3.824%, cumulative 38.623% 
-* 55 = 1 => occurences 0.186%, cumulative 37.732% 
index 614619e..0573a88 100644 (file)
@@ -26,7 +26,7 @@ var insert_req_1 = "INSERT INTO users VALUES('Bob', 'zzz', 1)"
 var insert_req_2 = "INSERT INTO users VALUES('Guillaume', 'xxx', 1)"
 var select_req = "SELECT * FROM users"
 
-var db = new NativeSqlite3.open(filename)
+var db = new NativeSqlite3.open(filename.to_cstring)
 assert sqlite_open: db.error.is_ok
 
 db.exec(create_req)
@@ -53,7 +53,7 @@ end
 
 db.close
 
-db = new NativeSqlite3.open(filename)
+db = new NativeSqlite3.open(filename.to_cstring)
 assert sqlite_reopen: db.error.is_ok
 
 stmt = db.prepare(select_req)