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>
# 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
# 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
return 0
fi
if test -n "$html"; then
- echo >>"$html" "<h2>$1</h2>"
+ echo >>"$html" "<h2 id="$1">$1</h2>"
fi
echo "*"
echo "* $1 *****"
{
echo >&2 "error: $*"
died=1
+ return 1
}
--- /dev/null
+all:
+ ./bench_languages.sh all
+
+check:
+ ./bench_languages.sh --fast bench_meth
+++ /dev/null
-#!/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
# 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 ##
# 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()
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;;
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 ##
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
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
+++ /dev/null
-#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
--- /dev/null
+#!/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
#!/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
redef fun inite(res, se)
do
for c in classes do
- res.add "create \{{c}[ROOT]\} a"
+ res.add "create \{{c}[ROOT]\}"
end
end
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
#!/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
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
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
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
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
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
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
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
#!/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
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
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
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
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
redef fun locale(se)
do
- write "\t\t\tto: {clae(middle)}"
+ write "\t\t\tto: detachable {clae(middle)}"
end
end
-src/benitlux_serial.nit
+src/benitlux_restful.nit
*.db
*.email
benitlux_corrections.txt
-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
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"
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
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
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
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
# 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)
import benitlux_model
# The database of this project
-class DB
+class BenitluxDB
super Sqlite3DB
redef init open(path)
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
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
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`
module benitlux_model
import serialization
+import md5
# A beer, with a name and description
class Beer
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!"
--- /dev/null
+# 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
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
import benitlux_db
-redef class DB
+redef class BenitluxDB
# Path to file with the corrections
private var corrections_path = "benitlux_corrections.txt"
# 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
}
`}
- 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;
}
# 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)
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"))
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.
#
# 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.
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.")
#
# 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)
import server_config
import http_request
import http_response
+import token
# A server side session
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
--- /dev/null
+# 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
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`
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
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.
# 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
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
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
# 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("\}")
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))
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 \{")
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")
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
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)
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
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
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
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
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+# 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)
--- /dev/null
+# 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
--- /dev/null
+1.000000
+1.000000
+1.000000
+true
--- /dev/null
+Runtime error: Cast failed. Expected `E`, got `Bool` (alt/base_covar_adapt_alt1.nit:20)
+1.000000
+1.000000
+1.000000
+true
--- /dev/null
+Runtime error: Cast failed. Expected `E`, got `null` (alt/base_covar_adapt_alt2.nit:18)
+1.000000
+1.000000
+1.000000
+true
--- /dev/null
+[1,1]
+[true,2]
+[1,3]
+[1,4]
+[[1,2,3],5]
+[[1,2,3],6]
+[[1,2,3],7]
--- /dev/null
+1
+2
+4
+6
--- /dev/null
+1.000000
+1.000000
+1.000000
+true
+true
--- /dev/null
+1.000000
+1.000000
+1.000000
+true
+true
+++ /dev/null
-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%
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)
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)