Merge: add attribute annotation `noinit` to skip the attribute in inits
authorJean Privat <jean@pryen.org>
Tue, 22 Jul 2014 03:16:56 +0000 (23:16 -0400)
committerJean Privat <jean@pryen.org>
Tue, 22 Jul 2014 03:16:56 +0000 (23:16 -0400)
~~~.rb
class Toto
   var tata: Int is noinit
   var tutu: String
end
var t = new Toto("hello") # only tutu in the constructor
~~~

Pull-Request: #600
Reviewed-by: Lucas Bajolet <r4pass@hotmail.com>
Reviewed-by: Alexandre Terrasa <alexandre@moz-code.org>

59 files changed:
Makefile
contrib/friendz/Makefile [new file with mode: 0644]
contrib/friendz/README.md [new file with mode: 0644]
contrib/friendz/assets/background.png [new file with mode: 0644]
contrib/friendz/assets/bing.wav [new file with mode: 0644]
contrib/friendz/assets/click.wav [new file with mode: 0644]
contrib/friendz/assets/deltaforce_font.png [new file with mode: 0644]
contrib/friendz/assets/duh.wav [new file with mode: 0644]
contrib/friendz/assets/hitbox.png [new file with mode: 0644]
contrib/friendz/assets/level.wav [new file with mode: 0644]
contrib/friendz/assets/logo.png [new file with mode: 0644]
contrib/friendz/assets/music.ogg [new file with mode: 0644]
contrib/friendz/assets/tiles2.png [new file with mode: 0644]
contrib/friendz/assets/whip.wav [new file with mode: 0644]
contrib/friendz/org.nitlanguage.friendz_android.txt [new file with mode: 0644]
contrib/friendz/src/friendz.nit [new file with mode: 0644]
contrib/friendz/src/friendz_android.nit [new file with mode: 0644]
contrib/friendz/src/friendz_linux.nit [new file with mode: 0644]
contrib/friendz/src/grid.nit [new file with mode: 0644]
contrib/friendz/src/level.nit [new file with mode: 0644]
contrib/friendz/src/solver.nit [new file with mode: 0644]
contrib/jwrapper/Makefile [new file with mode: 0644]
contrib/jwrapper/grammar/javap.sablecc [new file with mode: 0644]
contrib/jwrapper/src/code_generator.nit [new file with mode: 0644]
contrib/jwrapper/src/javap_visitor.nit [new file with mode: 0644]
contrib/jwrapper/src/jtype_converter.nit [new file with mode: 0644]
contrib/jwrapper/src/types.nit [new file with mode: 0644]
doc/Makefile [deleted file]
doc/git-gen-version.sh [deleted file]
lib/neo4j/neo4j.nit
lib/sqlite3/native_sqlite3.nit [moved from lib/sqlite3.nit with 79% similarity]
lib/sqlite3/sqlite3.nit [new file with mode: 0644]
src/abstract_compiler.nit
src/android_annotations.nit
src/android_platform.nit
src/compiler_ffi.nit
src/global_compiler.nit
src/model/README.md [new file with mode: 0644]
src/model/mmodule.nit
src/model/model.nit
src/model/model_base.nit
src/model/mproject.nit
src/parser/README [deleted file]
src/parser/README.md [new file with mode: 0644]
src/separate_compiler.nit
src/separate_erasure_compiler.nit
src/vm.nit
tests/exec.skip
tests/sav/fixme/nitvm_args3.res [new file with mode: 0644]
tests/sav/friendz.res [new file with mode: 0644]
tests/sav/friendz_android.res [new file with mode: 0644]
tests/sav/javap_visitor.res [new file with mode: 0644]
tests/sav/test_sqlite3_native.res [moved from tests/sav/test_sqlite3.res with 100% similarity]
tests/sav/test_sqlite3_nity.res [new file with mode: 0644]
tests/sav/test_sqlite3_nity_alt1.res [new file with mode: 0644]
tests/sav/test_sqlite3_nity_alt2.res [new file with mode: 0644]
tests/test_ffi_c_lots_of_refs.nit [new file with mode: 0644]
tests/test_sqlite3_native.nit [moved from tests/test_sqlite3.nit with 89% similarity]
tests/test_sqlite3_nity.nit [new file with mode: 0644]

index d51f431..ef24ecb 100644 (file)
--- a/Makefile
+++ b/Makefile
 
 NITCOPT=
 
-all: tools doc/stdlib/index.html
+all: tools
 
 docs: doc/stdlib/index.html doc/nitc/index.html
-       #cd doc; make
 
 tools:
        cd src; make
diff --git a/contrib/friendz/Makefile b/contrib/friendz/Makefile
new file mode 100644 (file)
index 0000000..eb9ce29
--- /dev/null
@@ -0,0 +1,16 @@
+default: linux
+
+linux:
+       mkdir -p bin
+       ../../bin/nitg -o bin/friendz src/friendz_linux.nit
+
+android:
+       mkdir -p bin
+       ../../bin/nitg -o bin/friendz.apk src/friendz_android.nit
+
+doc:
+       mkdir -p doc
+       ../../bin/nitdoc -d doc/ src/friendz.nit src/friendz_linux.nit
+
+clean:
+       rm -rf bin/ doc/
diff --git a/contrib/friendz/README.md b/contrib/friendz/README.md
new file mode 100644 (file)
index 0000000..bd5f5e5
--- /dev/null
@@ -0,0 +1,35 @@
+Chainz of Friendz
+
+A puzzle game
+
+# Objectives
+
+Place monsters to create big chains of friends. You cannot add or
+remove monsters on a metal block. Monsters cannot have more that two
+direct friends. You must build one chain for each type of monster.
+
+# Controls
+
+Select a monster in the right pane and click on the board to add or
+remove the monster. Keep the mouse button down on the map to place
+multiple monsters. Click on a monster on a metal block to automatically
+select the monster.
+
+Keyboard shortcuts:
+
+* 1 to 9 : select a monster
+* 0 : eraser
+* q : metal block (editor only)
+
+# Editor
+
+Once the editor is unlocked you can create your own levels.
+
+# Challenge levels
+
+In challenge level you have to place as much as monster as possible.
+
+# Misc
+
+This game was originally developed for the [Casual Gameplay Design Competition
+\#9](http://jayisgames.com/cgdc9).
diff --git a/contrib/friendz/assets/background.png b/contrib/friendz/assets/background.png
new file mode 100644 (file)
index 0000000..a21aab4
Binary files /dev/null and b/contrib/friendz/assets/background.png differ
diff --git a/contrib/friendz/assets/bing.wav b/contrib/friendz/assets/bing.wav
new file mode 100644 (file)
index 0000000..c7cae5b
Binary files /dev/null and b/contrib/friendz/assets/bing.wav differ
diff --git a/contrib/friendz/assets/click.wav b/contrib/friendz/assets/click.wav
new file mode 100644 (file)
index 0000000..3d07b83
Binary files /dev/null and b/contrib/friendz/assets/click.wav differ
diff --git a/contrib/friendz/assets/deltaforce_font.png b/contrib/friendz/assets/deltaforce_font.png
new file mode 100644 (file)
index 0000000..fb6cc4b
Binary files /dev/null and b/contrib/friendz/assets/deltaforce_font.png differ
diff --git a/contrib/friendz/assets/duh.wav b/contrib/friendz/assets/duh.wav
new file mode 100644 (file)
index 0000000..054e1fe
Binary files /dev/null and b/contrib/friendz/assets/duh.wav differ
diff --git a/contrib/friendz/assets/hitbox.png b/contrib/friendz/assets/hitbox.png
new file mode 100644 (file)
index 0000000..bb48006
Binary files /dev/null and b/contrib/friendz/assets/hitbox.png differ
diff --git a/contrib/friendz/assets/level.wav b/contrib/friendz/assets/level.wav
new file mode 100644 (file)
index 0000000..2d30b6e
Binary files /dev/null and b/contrib/friendz/assets/level.wav differ
diff --git a/contrib/friendz/assets/logo.png b/contrib/friendz/assets/logo.png
new file mode 100644 (file)
index 0000000..6370e2a
Binary files /dev/null and b/contrib/friendz/assets/logo.png differ
diff --git a/contrib/friendz/assets/music.ogg b/contrib/friendz/assets/music.ogg
new file mode 100644 (file)
index 0000000..8dcfa3b
Binary files /dev/null and b/contrib/friendz/assets/music.ogg differ
diff --git a/contrib/friendz/assets/tiles2.png b/contrib/friendz/assets/tiles2.png
new file mode 100644 (file)
index 0000000..24038a9
Binary files /dev/null and b/contrib/friendz/assets/tiles2.png differ
diff --git a/contrib/friendz/assets/whip.wav b/contrib/friendz/assets/whip.wav
new file mode 100644 (file)
index 0000000..b907832
Binary files /dev/null and b/contrib/friendz/assets/whip.wav differ
diff --git a/contrib/friendz/org.nitlanguage.friendz_android.txt b/contrib/friendz/org.nitlanguage.friendz_android.txt
new file mode 100644 (file)
index 0000000..fc0e9dc
--- /dev/null
@@ -0,0 +1,10 @@
+Categories:Nit,Games
+License:WTFPL
+Web Site:http://nitlanguage.org
+Source Code:http://nitlanguage.org/nit.git/tree/HEAD:/contrib/friendz
+Issue Tracker:https://github.com/privat/nit/issues
+
+Summary:Puzzle game
+Description:
+Connect colored monsters to from bit continuous chains of friends.
+.
diff --git a/contrib/friendz/src/friendz.nit b/contrib/friendz/src/friendz.nit
new file mode 100644 (file)
index 0000000..e745040
--- /dev/null
@@ -0,0 +1,1642 @@
+# Monsterz - Chains of Friends
+#
+# 2010-2014 (c) Jean Privat <jean@pryen.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the Do What The Fuck You Want To
+# Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+
+# Full UI for the game
+module friendz is
+       app_name("ChainZ of FriendZ")
+       app_version(0, 1, git_revision)
+end
+
+import mnit
+import realtime
+import solver
+import mnit::tileset
+
+intrude import grid
+intrude import level
+
+redef class Grid
+       # Zoom level in %
+       # higher means more dense grid
+       var ratio = 100
+
+       # Various grid sizes from large to small (16x16, 12x12, 8x8, 6x6)
+       var ratios: Array[Int] = [200, 150, 100, 75]
+
+       redef fun resize(w,h)
+       do
+               super
+               for r in ratios do
+                       if w*100/r <= 8 and h*100/r <= 8 then self.ratio = r
+               end
+       end
+end
+
+#   * ENTITIES ****************************************************************
+
+# A game entity is something that is displayed and may interact with the player.
+abstract class Entity
+       # The associated game
+       var game: Game
+
+       # X left coordinate (in pixel).
+       var x: Int
+
+       # Y top coordinate (in pixel).
+       var y: Int
+
+       # X right coordinate (in pixel).
+       fun x2: Int do return x + width
+
+       # Y bottom coordinate (in pixel).
+       fun y2: Int do return y + height
+
+       # Width
+       var width: Int
+
+       # Height
+       var height: Int
+
+       # Tool tip text (if any)
+       var over: nullable String = null
+
+       # can the entity intercepts drag ang drop events?
+       var draggable = false
+
+       # Draw function. To implement
+       fun draw(ctx: Display) do end
+
+       # Update function. Called each loop. To implement
+       fun update do end
+
+       # Enter function. Called when the cursor enter in the element. To implement
+       fun enter(ev: Event) do end
+
+       # Click function. Called when the player click in the element.
+       # (or activate it with a shortcut).
+       fun click(ev: Event) do end
+
+       # keyboard shortcut do activate the entity, if any
+       var shortcut: nullable String = null
+
+       # Are events received?
+       var enabled = true
+
+       fun bw: Int do return game.bw
+       fun bh: Int do return game.bh
+
+       # Should the entity be redrawn
+       var dirty = true
+end
+
+# TEXT BUTTONS ***********************************************************/
+
+# Button entity displayed as a simple text.
+# if `over1` is null, then the button is a simple pasive label
+# if `over1` is set but `over2` is null, then the button is a normal button
+# if both `over1` and `over2` arew set, then the button is a toggleable button with two states
+class TextButton
+       super Entity
+       var str: String
+       init(game: Game, str: String, x,y: Int, color: nullable String, over, over2: nullable String)
+       do
+               var w = 10 # TODO
+               super(game, x,y,w,24)
+               self.str = str
+               self.color = color or else "purple"
+               self.over = over
+               self.over1 = over
+               self.over2 = over2
+               self.textx = x
+               if self.toggleable then
+                       self.x -= bw/2 + 4
+               end
+       end
+
+       var color: String
+
+       # The description of the button action
+       var over1: nullable String
+       # The description of the state2 button action
+       var over2: nullable String
+
+       # is the button a two-state button
+       fun toggleable: Bool do return over2 != null
+
+       # is the toggleable button in its second state?
+       var toggled = false
+
+       # ttl for highlighting
+       var ttl = 0
+
+       # position of the start of the text
+       # in a toggleable button, there is space for the mark between `x` and `textx`
+       var textx: Int
+
+       redef fun draw(ctx) do
+               var x = self.x
+               if self.toggleable then
+                       var w
+                       if self.toggled or not self.enabled then w = 6 else w = 7
+                       ctx.blit(game.img2[w,0], self.x, self.y)
+               end
+               var c
+               if self.enabled then c = self.color else c = "gray"
+               var c2= null
+               if self.ttl > 0 then c2 = "rgba(255,255,255,{self.ttl/10})"
+               ctx.textx(self.str, self.textx, self.y, self.height, c, c2)
+               self.width = ctx.measureText(self.str, self.height)
+               if self.toggleable then self.width += bw/2 + 4
+       end
+
+       redef fun update
+       do
+               if game.statusbar.over_what != self and self.ttl > 0 then
+                       self.ttl-=1
+                       self.dirty = true
+               end
+       end
+
+       redef fun enter(ev)
+       do
+               if over1 == null then return
+               if not self.enabled then return
+               game.snd_click.replay
+               self.ttl = 10
+               self.dirty = true
+               self.enter2
+       end
+
+       # Called by `enter` do perform additionnal work if the button is active
+       # Specific button should implement this instead of `enter`
+       fun enter2 do end
+
+       redef fun click(ev)
+       do
+               if not self.enabled then
+                       game.snd_bing.replay
+               else
+                       if self.toggleable then
+                               self.toggled = not self.toggled
+                               if self.toggled then self.over = self.over2 else self.over = self.over1
+                               game.statusbar.over_txt = self.over
+                       end
+                       game.snd_whip.replay
+               end
+               self.click2(ev)
+       end
+
+       # Called by `click` do perform additionnal work if the button is active
+       # Specific button should implement this instead of `click`
+       fun click2(ev: Event) do end
+
+end
+
+# LEVEL BUTTONS ***********************************************************/
+
+# button to play a level in the menu screen
+class LevelButton
+       super Entity
+
+       # The associated level to play
+       var level: Level
+
+       init(l: Level)
+       do
+               self.level = l
+               var i = l.number
+               super(l.game, (i%5)*56 + 54, (i/5)*56 + 55, l.game.bw, l.game.bh)
+
+               self.over = self.level.fullname
+               if self.level.get_state >= l.l_won then
+                       if game.levels[9].get_state >= l.l_won then self.over += " --- {self.level.score}/{self.level.par}"
+               else if self.level.get_state >= l.l_open then
+                       if game.levels[9].get_state >= l.l_open then self.over +=  " --- ?/{self.level.par}"
+               end
+               #self.enabled = l.get_state >= l.l_open
+       end
+
+       redef fun draw(ctx)
+       do
+               var l = level
+               var s = self.level.get_state
+               var ix = 5 + l.number % 2
+               var iy = 0
+               if s == l.l_disabled then
+                       ix = 3
+                       iy = 3
+               else if s == l.l_open then
+                       ix = 1
+                       iy = 1
+                       ctx.blit(game.img[ix,iy], self.x, self.y)
+                       ix = 0
+                       iy = 0
+               end
+               ctx.blit(game.img[ix,iy], self.x, self.y)
+
+               if s == l.l_par then
+                       ctx.blit(game.img2[7,0], self.x + bw*5/8, self.y-bh*1/8)
+               end
+               ctx.textx(self.level.name, self.x+5, self.y+5, 24, null, null)
+       end
+
+       redef fun click(ev)
+       do
+               if self.enabled then
+                       game.snd_whip.replay
+                       game.play(self.level)
+               else
+                       game.snd_bing.replay
+                       game.statusbar.set_tmp("Locked level", "red")
+               end
+       end
+
+end
+
+# ACHIEVEMENTS ************************************************************/
+
+# Achievement (monster-like) button in the menu screen
+class Achievement
+       super Button
+
+       # The number of the achievement (0 is first)
+       var number: Int
+
+       # The name of the achievement
+       var name: String
+
+       init(game: Game, i: Int, name: String)
+       do
+               super(game, 5*56 + 54, i*56 + 55, game.bw, game.bh)
+               self.over = name
+               self.number = i
+               self.name = name
+               var l =  game.levels[number*5+4]
+               enabled = l.get_state >= l.l_won
+               if self.enabled then self.over = name + " (unlocked)" else self.over = name + " (locked)"
+       end
+
+       redef fun draw(ctx)
+       do
+               var w
+               if self.enabled then w = 5 else w = 3
+               ctx.blit(game.img[w,self.number+5], self.x, self.y)
+       end
+
+       redef fun click(ev)
+       do
+               if not self.enabled then
+                       game.snd_bing.replay
+                       game.statusbar.set_tmp("Locked achievement!", "red")
+               else
+                       game.snd_whip.replay
+                       self.click2(ev)
+               end
+       end
+
+       fun click2(ev: Event) do
+               # TODO
+       end
+end
+
+
+# BOARD (THE GRID) *******************************************************/
+
+# The board game element.
+class Board
+       super Entity
+       init(game: Game)
+       do
+               super(game, game.xpad, game.ypad, 8*game.bw, 8*game.bh)
+               draggable = true
+       end
+
+       redef fun draw(ctx)
+       do
+               var grid = game.grid
+               var bwr = bw*100/grid.ratio
+               var bhr = bh*100/grid.ratio
+               var w = grid.width
+               var h = grid.height
+               if game.selected_button == game.button_size then
+                       bwr = bw/2
+                       bhr = bh/2
+                       w = game.gw
+                       h = game.gh
+               end
+               self.x = game.xpad+(48*8/2)-w*bwr/2
+               self.y = game.ypad+(48*8/2)-h*bhr/2
+               self.width = w*bwr
+               self.height = h*bhr
+               for i in [0..w[ do
+                       for j in [0..h[ do
+                               var t = grid.grid[i][j]
+                               var dx = i * bwr + self.x
+                               var dy = j * bhr + self.y
+                               if (i+j)%2 == 0 then
+                                       ctx.blit_scaled(game.img[5,0], dx, dy, bwr, bhr)
+                               else
+                                       ctx.blit_scaled(game.img[6,0], dx, dy, bwr, bhr)
+                               end
+                               if t.fixed then
+                                       if t.shape != null and not game.editing then
+                                               #ctx.drawImage(game.img, t.shape.x*bw, (2+t.shape.y)*bh, bw, bh, i * bwr + self.x, j * bhr + self.y, bwr, bhr)
+                                               ctx.blit_scaled(game.img[3,3], dx, dy, bwr, bhr)
+                                       else
+                                               ctx.blit_scaled(game.img[3,3], dx, dy, bwr, bhr)
+                                       end
+                               end
+                               if t.kind>0 then
+                                       var m = grid.monsters[t.kind]
+                                       var s = 0
+                                       if t.blink > 0 then s = 1
+                                       if t.nexts > 2 then s = 3
+                                       if t.nexts == 0 then s = 6
+                                       if m.chain then s = 5
+                                       if t.shocked>0 then s = 2
+                                       ctx.blit_scaled(game.img[s,(4+t.kind)], dx, dy, bwr, bhr)
+                               end
+                               #ctx.textx(t.chain_mark.to_s, dx, dy, 20, "", null)
+                       end
+               end
+               if game.selected_button == game.button_size then
+                       var x0 = self.x
+                       var x1 = (grid.width) * bwr - bwr/2 + self.x
+                       var y0 = self.y
+                       var y1 = (grid.height) * bhr - bhr/2 + self.y
+                       ctx.blit_scaled(game.img2[0,0], x0, y0, bwr/2, bhr/2)
+                       ctx.blit_scaled(game.img2[1,0], x1, y0, bwr/2, bhr/2)
+                       ctx.blit_scaled(game.img2[1,1], x1, y1, bwr/2, bhr/2)
+                       ctx.blit_scaled(game.img2[0,1], x0, y1, bwr/2, bhr/2)
+                       ctx.textx("{grid.width}x{grid.height}",self.x + grid.width*bwr/2,self.y+grid.height*bhr/2,20,"orange",null)
+               end
+       end
+
+       redef fun update
+       do
+               var grid = game.grid
+               for i in [0..grid.width[ do
+                       for j in [0..grid.height[ do
+                               var t = grid.grid[i][j]
+                               if t.kind == 0 then continue
+                               if t.blink > 0 then
+                                       t.blink-=1
+                                       self.dirty=true
+                               end
+                               if t.shocked > 0 then
+                                       t.shocked-=1
+                                       self.dirty=true
+                               else if 100.rand == 0 then
+                                       t.blink = 5
+                                       self.dirty=true
+                               end
+                       end
+               end
+       end
+
+       # Last clicked tile
+       # Uded to filter drag events
+       private var last: nullable Tile = null
+
+       redef fun click(ev)
+       do
+               var grid = game.grid
+               var r = grid.ratio
+               if game.selected_button == game.button_size then r = 200
+               var x = ev.game_x * r / bw / 100
+               var y = ev.game_y * r / bh / 100
+               var t = grid.grid[x][y]
+
+               if ev.drag and last == t then return
+               last = t
+
+               if game.selected_button != game.button_size and (x>=grid.width or y>=grid.height) then return
+
+               # print "{ev.game_x},{ev.game_y},{ev.drag} -> {x},{y}:{t}"
+
+               if game.selected_button != null then
+                       game.selected_button.click_board(ev, t)
+               end
+       end
+end
+
+# BUTTONS *****************************************************************/
+
+# A in-game selectable button for monsters or tools
+class Button
+       super Entity
+
+       # The x tile
+       var imgx: Int = 0
+
+       # The y tile
+       var imgy: Int = 0
+
+       # The associated monster tile
+       # >0 for monsters, <=0 for tools
+       var kind = 0
+
+       redef fun draw(ctx)
+       do
+               ctx.blit(game.img[self.imgx, self.imgy], self.x, self.y)
+               if game.selected_button == self then ctx.blit(game.img[0, 0], self.x, self.y)
+       end
+
+       redef fun click(ev)
+       do
+               var sel = game.selected_button
+               if game.selected_button == game.button_size then game.board.dirty=true
+               if sel != null then sel.dirty=true
+               game.selected_button = self
+               game.snd_click.replay
+       end
+
+       # Current inputed chain
+       # Used for drag
+       private var chain = new Array[Tile]
+
+       # Board click. Called when the player click on the board with the button selected.
+       fun click_board(ev: Event, t: Tile)
+       do
+               game.score.dirty = true
+               if ev.drag and self.kind>0 and not chain.is_empty then
+                       if self.chain.length >= 2 and self.chain[1] == t then
+                               var t2 = self.chain.shift
+                               game.snd_click.replay
+                               if t2.fixed and not game.editing then return
+                               t2.update(0)
+                               return
+                       end
+                       if t.fixed and t.kind == self.kind then
+                               self.chain.unshift(t)
+                               game.snd_click.replay
+                               return
+                       end
+                       if (self.chain[0].x - t.x).abs + (self.chain[0].y - t.y).abs != 1 then return
+                       if t.fixed and not game.editing then
+                               game.snd_bing.replay
+                               return
+                       end
+                       if t.kind != 0 and t.kind != self.kind then
+                               t.shocked = 5
+                               game.snd_duh.replay
+                               return
+                       end
+                       self.chain.unshift(t)
+                       if t.kind == self.kind then return
+                       game.snd_click.replay
+                       t.update(self.kind)
+                       return
+               end
+
+               if t.fixed and not game.editing then
+                       if t.kind == 0 then
+                               game.snd_bing.replay
+                               return
+                       end
+                       if t.kind != self.kind and not ev.drag then
+                               game.buttons[t.kind].click(ev)
+                               game.buttons[t.kind].chain = [t]
+                       else
+                               self.chain = [t]
+                               game.snd_bing.replay
+                       end
+                       return
+               end
+               if t.fixed and game.editing and self == game.button_erase and t.kind == 0 then
+                       t.fixed = false
+                       game.snd_click.replay
+                       return
+               end
+
+               var nkind = 0 # the new kind
+               if ev.drag then
+                       # Here we clean
+                       if t.kind == 0 then return
+                       if self.kind != 0 and t.kind != self.kind then
+                               t.shocked = 5
+                               game.snd_duh.replay
+                               return
+                       end
+                       nkind = 0
+               else if t.kind != self.kind then
+                       nkind = self.kind
+                       self.chain = [t]
+               else if t.kind != 0 then
+                       nkind = 0
+                       self.chain.clear
+               end
+               if nkind == t.kind then return
+               game.snd_click.replay
+               t.update(nkind)
+       end
+end
+
+# A monster button
+class MonsterButton
+       super Button
+
+       # TTL for the monster being angry
+       var angries = 0
+       # TTL for the monster being happy
+       var happy = 0
+       # TTL for the monster being shocked
+       var shocked = 0
+       # TTL for the monster blinking
+       var blink = 0
+
+       init(game: Game, i: Int)
+       do
+               self.game = game
+               var x = 440 + 58 * ((i-1).abs%3)
+               var y = 150 + (bh+5) * ((i-1)/3)
+               super(game, x, y, game.bw, game.bh)
+               if i == 0 then return
+               kind = i
+               imgx = 0
+               imgy = (4+i)
+               over = game.colors[i] + " monster ({i})"
+               shortcut = i.to_s # code for 1 trough 9
+       end
+
+       redef fun click(ev)
+       do
+               super
+               self.shocked = 5
+       end
+
+       redef fun update
+       do
+               if self.happy > 0 then
+                       self.happy-=1
+                       self.dirty=true
+               end
+               if self.shocked > 0 then
+                       self.shocked-=1
+                       self.dirty=true
+               end
+               if self.blink > 0 then
+                       self.blink-=1
+                       self.dirty=true
+               else if 100.rand == 0 then
+                       self.blink = 5
+                       self.dirty=true
+               end
+       end
+
+       redef fun draw(ctx)
+       do
+               var s = self.imgx
+               if self.angries>0 then
+                       s += 3
+               else if self.happy > 5 then
+                       s += 5
+               else if self.shocked > 0 then
+                       s += 5
+               else if self.blink > 0 then
+                       s += 1
+               end
+               ctx.blit(game.img[s, self.imgy], self.x, self.y)
+               if game.selected_button == self then ctx.blit(game.img[0, 0], self.x, self.y)
+       end
+end
+
+# Erase button.
+class EraseButton
+       super Button
+       init(game: Game) do
+               super(game, 440, 92, game.bh, 22+game.bh)
+               imgx = 4
+               imgy = 13
+               kind = 0
+               over = "Eraser (0)"
+               shortcut = "0"
+       end
+end
+
+# Metal (fixed) button.
+class MetalButton
+       super Button
+       init(game: Game)
+       do
+               super(game, 498, 92, game.bh, 20+game.bh)
+               imgx = 3
+               imgy = 3
+               kind = -1
+               over = "Metal block (q)"
+               shortcut = "q"
+       end
+
+       private var fixed = false
+
+       redef fun click_board(ev,t)
+       do
+               if not ev.drag then self.fixed = not t.fixed
+               if t.fixed == self.fixed then return
+               t.fixed = self.fixed
+               game.snd_click.replay
+       end
+end
+
+# Resize button.
+class ResizeButton
+       super Button
+
+       init(game: Game)
+       do
+               super(game,556, 92, game.bh, 20+game.bh)
+               kind = -2
+               over = "Resize the grid"
+       end
+
+       redef fun draw(ctx)
+       do
+               for i in [0..3[ do
+                       for j in [0..3[ do
+                               var x = self.x + i*bw/3
+                               var y = self.y + j*bh/3
+                               ctx.blit_scaled(game.img[5+(i+j)%2,0], x, y, bw/3, bh/3)
+                       end
+               end
+               if game.selected_button == self then ctx.blit(game.img[0, 0], self.x, self.y)
+       end
+
+       redef fun click(ev)
+       do
+               if game.selected_button != game.button_size then
+                       super
+               else
+                       game.selected_button = null
+                       game.board.dirty=true
+               end
+       end
+
+       redef fun click_board(evt, t)
+       do
+               var grid = t.grid
+               var w = t.x+1
+               var h = t.y+1
+               if w < 3 or h < 3 then
+                       game.snd_bing.replay
+                       game.statusbar.set_tmp("Too small!", "red")
+                       return
+               end
+               var aborts = false
+               for i in [0..grid.width[ do
+                       for j in [0..grid.height[ do
+                               if i>=w or j>=h then
+                                       var t2 = grid.grid[i][j]
+                                       if t2.kind > 0 then
+                                               aborts = true
+                                               t2.shocked = 5
+                                       end
+                               end
+                       end
+               end
+               if aborts then
+                       game.snd_duh.replay
+                       game.statusbar.set_tmp("Monsters on the way!", "red")
+                       return
+               end
+               game.snd_click.replay
+               grid.resize(w,h)
+       end
+end
+
+# Inactive area used to display the score
+class Score
+       super Entity
+       init(game: Game)
+       do
+               super(game,440,310,199,62)
+       end
+       redef fun draw(ctx)
+       do
+               ctx.textx("MONSTERS: {game.grid.number}",self.x,self.y+1,21,"cyan",null)
+               var level = game.level
+               if level == null then return
+               if level.get_state >= level.l_won then
+                       ctx.textx("BEST: {level.score}",self.x,self.y+22,21,"pink", null)
+               else
+                       ctx.textx("BEST: -",self.x,self.y+22,21,"pink", null)
+               end
+               if game.levels[9].get_state >= level.l_won then
+                       if level.is_challenge then
+                               ctx.textx("GOAL: {level.par}",self.x,self.y+44,21,"yellow",null)
+                       else
+                               ctx.textx("PAR: {level.par}",self.x,self.y+44,21,"yellow",null)
+                       end
+               end
+       end
+end
+
+# Status bar element.
+class StatusBar
+       super Entity
+       init(game: Game)
+       do
+               super(game,24, 440, 418-24, 30)
+       end
+
+       # Permanant text, if any
+       var main_txt: nullable String = null
+
+       # Text to display when the cursor if over an entity (`over_what`), if any
+       var over_txt: nullable String = null
+
+       # What is the entity for `over_txt`
+       var over_what: nullable Entity
+
+       # Text to temporally display, for some game event, if any
+       var tmp_txt: nullable String = null
+
+       # time-to-live for the `tmp_txt`
+       var tmp_txt_ttl = 0
+
+       # Color used to display `tmp_txt`
+       var tmp_txt_color: nullable String = null
+
+       # reset the status
+       fun clear do
+               self.main_txt = null
+               self.over_txt = null
+               self.tmp_txt = null
+               self.over = null
+       end
+
+       # set a temporary text
+       fun set_tmp(txt, color: String)
+       do
+               print "***STATUS** {txt}"
+               self.tmp_txt = txt
+               self.tmp_txt_ttl = 20
+               self.tmp_txt_color = color
+       end
+
+       redef fun draw(ctx)
+       do
+               var tmp_txt = self.tmp_txt
+               var over_txt = self.over_txt
+               var main_txt = self.main_txt
+               if tmp_txt != null and self.tmp_txt_ttl>0 then
+                       ctx.textx(tmp_txt,24,442,24,self.tmp_txt_color,null)
+               else if over_txt != null then
+                       ctx.textx(over_txt,24,442,24,"yellow",null)
+               else if main_txt != null then
+                       ctx.textx(main_txt,24,442,24,"white",null)
+               end
+       end
+
+       redef fun update
+       do
+               if self.tmp_txt_ttl>0 then
+                       self.tmp_txt_ttl-=1
+                       self.dirty=true
+               end
+       end
+end
+
+# ************************************************************************/
+
+# Simple audio asset
+class Audio
+       var path: String
+
+       # placebo
+       fun play do end
+
+       # placebo
+       fun pause do end
+
+       # Play a sound.
+       fun replay
+       do
+               sys.system("aplay assets/{path} &")
+       end
+end
+
+redef class Display
+       # Display a text
+       fun textx(str: String, x, y, height: Int, color, color2: nullable String)
+       do
+               #var w = measureText(str, height)
+               #rect(x,y,w,height)
+               text(str.to_upper, app.game.font, x, y)
+       end
+
+       # give the width for a giver text
+       fun measureText(str: String, height: Int): Int
+       do
+               var font = app.game.font
+               return str.length * (app.game.font.width + app.game.font.hspace)
+       end
+
+       # displays a debug rectangle
+       fun rect(x,y,w,h:Int)
+       do
+               var image = once app.load_image("hitbox.png")
+               blit_scaled(image, x, y, w, h)
+       end
+end
+
+# Simple basic class for event
+class Event
+       # Is a drag event?
+       var drag = false
+       # screen x
+       var offset_x: Int
+       # screen y
+       var offset_y: Int
+       # entity x
+       var game_x = 0
+       # entity y
+       var game_y = 0
+       # key pressed
+       var char_code: String
+end
+
+redef class Game
+       # width of a tile, used for most width reference in the game
+       var bw = 48
+       # height a tile, used for most width reference in the game
+       var bh = 48
+       # x-coordinate of the board (padding)
+       var xpad = 24
+       # y-coordinate of the board (padding)
+       var ypad = 24
+
+       # Load tiles
+
+       # Basic tileset
+       var img = new TileSet(app.load_image("tiles2.png"),48,48)
+
+       # Sub tileset (for marks or other)
+       var img2 = new TileSet(app.load_image("tiles2.png"),24,24)
+
+       # background image
+       var back: Image = app.load_image("background.png")
+
+       # Logo image
+       var logo: Image = app.load_image("logo.png")
+
+       # Font
+       var font = new TileSetFont(app.load_image("deltaforce_font.png"), 16, 17, "ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789.:;!?\"'() -,/")
+
+       var xxx = """
+       fun save_cookie(name, val:String) do
+       var days = 365
+       var date = new Date()
+       date.setTime(date.getTime()+(days*24*60*60*1000))
+       document.cookie = name+"="+val+"; expires="+date.toGMTString()+"; path=/"
+       end
+
+       fun read_cookie(name:String):String do
+       var key = name + "="
+       var ca = document.cookie.split(';')
+       for(var i=0; i<ca.length; i++) do
+       var c = ca[i]
+       while (c[0]==' ') c = c.substring(1, c.length)
+       if (c.indexOf(key) == 0) return c.substring(key.length)
+       end
+       return null
+       end
+       """
+
+       # DISPLAY *****************************************************************
+
+       # Is the game in editing mode
+       var editing = false
+
+       # The selected button, if any
+       var selected_button: nullable Button = null
+
+       # SOUND
+
+       # Is the music muted?
+       var music_muted: Bool = true #read_cookie("music_muted")
+
+       # Is the sound effects muted?
+       var sfx_muted: Bool = true #read_cookie("sfx_muted")
+
+       # The background music resource. */
+       var music = new Audio("music.ogg")
+
+       # Click sound
+       var snd_click = new Audio("click.wav")
+
+       # Wining soulf
+       var snd_win = new Audio("level.wav")
+
+       # Shocked sound
+       var snd_duh = new Audio("duh.wav")
+
+       # metal sound
+       var snd_bing = new Audio("bing.wav")
+
+       # transition sound
+       var snd_whip = new Audio("whip.wav")
+
+       # INPUT ******************************************************************
+
+       # Current grid edited (if any).
+       var grid_edit: nullable Grid = null
+
+       # Sequence of current entities
+       var entities = new Array[Entity]
+
+       # The current statusbar
+       var statusbar = new StatusBar(self)
+
+       # The grid board
+       var board = new Board(self)
+
+       # The current score board
+       var score = new Score(self)
+
+       # Monster button game elements.
+       var buttons = new Array[MonsterButton]
+
+       # MetalButton
+       var button_wall = new MetalButton(self)
+
+       # EraseButton
+       var button_erase = new EraseButton(self)
+
+       # ResizeButton
+       var button_size = new ResizeButton(self)
+
+       # fill `buttons`
+       fun init_buttons
+       do
+               for i in [0..9] do
+                       buttons[i] = new MonsterButton(self, i)
+               end
+       end
+
+       # Play a level in player mode.
+       fun play(l: Level)
+       do
+               level = l
+               grid.load(level.str)
+               init_play_menu(false)
+               if level.status != "" then
+                       statusbar.main_txt = level.status
+               else
+                       statusbar.main_txt = level.fullname
+               end
+               var t = new NextLevelButton(self)
+               entities.push(t)
+               run
+       end
+
+       # Play the next level.
+       fun play_next
+       do
+               play(levels[level.number+1])
+       end
+
+
+       # Helper function to initialize all states.
+       # Set up buttons for music and SFX.
+       fun init_game
+       do
+               editing = false
+               solver = null
+               entities.clear
+               entities.push(new MusicButton(self))
+               entities.push(new SFXButton(self))
+               entities.push(new MenuButton(self))
+               statusbar.clear
+               entities.push(statusbar)
+       end
+
+       # Helper function to initialize monster menu entries.
+       fun init_play_menu(full: Bool)
+       do
+               init_game
+               entities.push(board)
+               entities.push(new ResetButton(self))
+               entities.push(button_erase)
+               # Push monster buttons and determine the selected one
+               var sel: nullable Button = null
+               for i in [1..monsters] do
+                       if grid.monsters[i].number > 0 or full then
+                               if selected_button == buttons[i] or sel == null then
+                                       sel = buttons[i]
+                               end
+                               entities.push(buttons[i])
+                       end
+               end
+               selected_button = sel
+               entities.push(score)
+       end
+
+       # Play a arbitrary grid in try mode.
+       fun play_grid(g: Grid)
+       do
+               grid = g
+               init_play_menu(false)
+               statusbar.main_txt = "User level"
+               if grid_edit != null then
+                       entities.push(new EditButton(self))
+               end
+               entities.push(new WonButton(self))
+               run
+       end
+
+       # Launch the editor starting with a grid.
+       fun edit_grid(g: Grid)
+       do
+               grid = g
+               init_play_menu(true)
+               editing = true
+               statusbar.main_txt = "Level editor"
+               if level != null then statusbar.main_txt += ": level "+level.name
+               entities.push(button_wall)
+               entities.push(button_size)
+               entities.push(new TestButton(self))
+               entities.push(new SaveButton(self))
+               entities.push(new LoadButton(self))
+               run
+       end
+
+       # Launch the title screen
+       fun title
+       do
+               init_menu
+               entities.push(new Splash(self))
+               run
+       end
+
+       # Helper function to initialize the menu (and tile) screen
+       fun init_menu
+       do
+               init_game
+               level = null
+               var i = levels.first
+               for l in levels do
+                       if l.get_state == l.l_open then break
+                       i = l
+               end
+               entities.push(new StartButton(self, i))
+       end
+
+       # Launch the menu.
+       fun menu
+       do
+               init_menu
+               var t
+               t = new TextButton(self,"LEVEL SELECT", 120, ypad, "white", null, null)
+               entities.push(t)
+               for i in [0..levels.length[ do
+                       var b = new LevelButton(levels[i])
+                       entities.push(b)
+               end
+               t = new Achievement(self, 0, "Training")
+               entities.push(t)
+               t = new Achievement(self, 1, "Par")
+               entities.push(t)
+               t = new Achievement(self, 2, "Editor")
+               entities.push(t)
+               t = new Achievement(self, 3, "Challenge")
+               entities.push(t)
+               t = new Achievement(self, 4, "Congraturation")
+               entities.push(t)
+               t = new Achievement(self, 5, "Awesome")
+               entities.push(t)
+               run
+       end
+
+       # Last function called when the lauch state is ready
+       fun run do
+               dirty_all = true
+       end
+
+       init
+       do
+               load_levels
+               init_buttons
+               entities.clear
+               title
+       end
+
+       # Should all entity redrawn?
+       var dirty_all = true
+
+       # Draw all game entities.
+       fun draw(display: Display) do
+               dirty_all = true
+               if dirty_all then display.blit(back, 0, 0)
+               for g in entities do
+                       if g.dirty or dirty_all then
+                               g.dirty = false
+                               #if g.x2-g.x>0 and g.y2-g.y>0 then ctx.drawImage(back, g.x, g.y, g.x2-g.x, g.y2-g.y, g.x, g.y, g.x2-g.x, g.y2-g.y)
+                               g.draw(display)
+                               #ctx.rect(g.x, g.y, g.width, g.height)
+                       end
+               end
+               var ev = lastev
+               if ev isa Event then
+                       display.blit(img[4,0],ev.offset_x-42,ev.offset_y-6)
+               end
+               dirty_all = false
+       end
+
+       # Update all game entities.
+       fun step do
+               if solver != null and not solver_pause then
+                       for i in [0..solver_steps[ do
+                               if solver.step then
+                                       solver_pause = true
+                                       break
+                               end
+                       end
+                       solver.dump
+                       if solver.is_over then solver = null
+               end
+               for g in entities do
+                       g.update
+               end
+       end
+
+       # Return the game entity located at a mouse event.
+       fun get_game_element(ev: Event): nullable Entity
+       do
+               var x = ev.offset_x
+               var y = ev.offset_y
+               for g in entities do
+                       if x>=g.x and x<g.x2 and y>g.y and y<g.y2 then
+                               ev.game_x = x-g.x
+                               ev.game_y = y-g.y
+                               #print "get {g}"
+                               return g
+                       end
+               end
+               return null
+       end
+
+       # The game entlty the mouse went down on
+       var drag: nullable Entity = null
+
+       # Last mouse event. Used to dray the cursor
+       var lastev: nullable Event = null
+
+       # Callback when the mouse is pressed
+       fun onMouseDown(ev: Event) do
+               lastev = ev
+               var g = get_game_element(ev)
+               if g != null then
+                       g.click(ev)
+                       g.dirty = true
+               end
+               drag = g
+       end
+
+       # Callback when the mouse is releassed
+       fun onMouseUp(ev: Event) do
+               drag = null
+       end
+
+       # Callback when the mouse if moved while pressed
+       fun onMouseMove(ev: Event) do
+               lastev = ev
+               var g = get_game_element(ev)
+               if g == null then
+                       statusbar.dirty = true
+                       statusbar.over_txt = null
+                       statusbar.over_what = null
+                       return
+               end
+               if statusbar.over_what != g then
+                       statusbar.dirty = true
+                       var go = g.over
+                       statusbar.over_txt = go
+                       statusbar.over_what = g
+                       g.enter(ev)
+                       if go != null then print "***OVER*** {go}"
+               end
+               # We moved abode a element that accepts drag event
+               if drag == g and g.draggable then
+                       # print "DRAG {g}"
+                       ev.drag = true
+                       g.click(ev)
+                       g.dirty = true
+               end
+       end
+
+       # Current solver, if any
+       var solver: nullable Solver = null
+
+       # Is the solver paused?
+       var solver_pause = false
+
+       # Number of solver steps played in a single game `update`
+       var solver_steps = 20000
+
+       # Callback when a keyboard event is recieved
+       fun onKeyDown(ev: Event) do
+               var kc = ev.char_code
+               if kc == "e" then
+                       grid_edit = grid.copy(true)
+                       edit_grid(grid)
+               else if kc == "s" then
+                       if solver == null then
+                               solver = new Solver(grid)
+                               solver_pause = false
+                       else
+                               solver_pause = not solver_pause
+                       end
+                       #solver.step
+               else if kc == "d" then
+                       if solver == null then
+                               solver = new Solver(grid)
+                               solver_pause = true
+                       else
+                               solver.step
+                       end
+               else if kc == "+" then
+                       solver_steps += 100
+                       print solver_steps
+               else if kc == "-" then
+                       solver_steps -= 100
+                       print solver_steps
+               else for g in entities do
+                       if kc == g.shortcut then
+                               g.click(ev)
+                               g.dirty = true
+                       end
+               end
+       end
+end
+
+# The spash title image
+class Splash
+       super Entity
+       init(game: Game)
+       do
+               super(game,game.xpad,game.ypad,380,350)
+       end
+       redef fun draw(ctx)
+       do
+               ctx.blit(game.logo, game.xpad, game.ypad)
+       end
+       redef fun click(ev)
+       do
+               game.snd_whip.replay
+               game.menu
+       end
+end
+
+class NextLevelButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game, "NEXT LEVEL", 440, 24, "cyan", "Play next level", null)
+               enabled = false
+       end
+
+       redef fun update
+       do
+               var w = game.level.check_won(game.grid)
+               if self.enabled != w then
+                       self.dirty = true
+                       self.enabled = w
+                       if w then
+                               game.snd_win.replay
+                               game.statusbar.set_tmp("Level solved!", "cyan")
+                       end
+               end
+       end
+
+       redef fun click(ev)
+       do
+               if not self.enabled then
+                       game.snd_duh.replay
+                       var grid = game.grid
+                       var monsters = grid.monsters
+                       var angry = new Array[Tile]
+                       var lonely = new Array[Tile]
+                       var edges = new Array[Tile]
+                       for i in [0..grid.width[ do
+                               for j in [0..grid.height[ do
+                                       var t = grid.grid[i][j]
+                                       if t.kind == 0 then continue
+                                       if t.nexts == 0 then lonely.push(t)
+                                       if t.nexts == 1 and not monsters[t.kind].chain then edges.push(t)
+                                       if t.nexts > 2 then angry.push(t)
+                               end
+                       end
+
+                       var l
+                       if angry.length>0 then
+                               l = angry
+                       else if lonely.length>0 then
+                               l = lonely
+                       else
+                               l = edges
+                       end
+                       for i in l do i.shocked=5
+
+                       if angry.length>0 then
+                               game.statusbar.set_tmp("Angry monsters!", "red")
+                       else if lonely.length>0 then
+                               game.statusbar.set_tmp("Lonely monsters!", "red")
+                       else if not grid.won then
+                               game.statusbar.set_tmp("Unconnected monsters!", "red")
+                       else
+                               game.statusbar.set_tmp("Not enough monsters!", "red")
+                       end
+                       return
+               end
+
+               game.snd_whip.replay
+               game.play_next
+       end
+end
+
+class MusicButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game, "MUSIC", 470, 412, "purple", "Mute the music", "Unmute the music")
+       end
+       redef fun click2(ev)
+       do
+               game.music_muted = self.toggled
+               if game.music_muted then game.music.pause else game.music.play
+               #game.save_cookie("music_muted",music_muted?"true":"")
+       end
+end
+
+class SFXButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game, "SOUND FX", 470, 382, "purple", "Mute the sound effects", "Unmute the sound effects")
+       end
+
+       redef fun click2(ev)
+       do
+               game.sfx_muted = self.toggled
+               if not game.sfx_muted then game.snd_whip.replay # Because the automatic one was muted
+               #save_cookie("sfx_muted",sfx_muted?"true":"")
+       end
+end
+
+class MenuButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game, "MENU", 470, 442, "purple", "Exit to the main menu", null)
+               shortcut = "back"
+       end
+
+       redef fun click2(ev)
+       do
+               game.menu
+       end
+end
+
+class ResetButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game,"RESET", 440, 61, "purple", "Clear the grid",null)
+       end
+
+       var count = 0
+
+       redef fun click2(ev)
+       do
+               self.count += 1
+               if self.count==1 then
+                       game.statusbar.set_tmp("Click again to reset","white")
+               else if self.count==2 then
+                       game.grid.reset(false)
+                       if game.editing then
+                               game.statusbar.set_tmp("Click again to clear all","white")
+                       end
+               else if game.editing then
+                       game.grid.reset(true)
+               end
+               game.dirty_all = true
+       end
+
+       redef fun enter2
+       do
+               self.count = 0
+       end
+end
+
+class EditButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game,"EDIT", 540, 24, "purple", "Return to the editor",null)
+       end
+
+       redef fun click2(ev)
+       do
+               var ge = game.grid_edit
+               assert ge != null
+               game.edit_grid(ge)
+       end
+end
+
+class WonButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game,"WON", 440, 24, "cyan", "", null)
+               enabled = false
+       end
+       redef fun click2(ev)
+       do
+               var ge = game.grid_edit
+               if not self.enabled then
+                       game.statusbar.set_tmp("Solve the level first!", "red")
+               else if ge != null then
+                       game.snd_whip.replay
+                       game.edit_grid(ge)
+               else
+                       game.snd_whip.replay
+                       game.menu
+               end
+       end
+
+       redef fun update
+       do
+               var w = game.grid.won
+               if self.enabled != w then
+                       self.dirty = true
+                       self.enabled = w
+                       if w then
+                               game.snd_win.replay
+                               game.statusbar.set_tmp("Level solved!", "cyan")
+                       end
+               end
+       end
+end
+
+class TestButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game,"TEST", 440, 24, "cyan", "Try to play the level", null)
+       end
+
+       redef fun click2(ev)
+       do
+               game.grid_edit = game.grid
+               game.play_grid(game.grid.copy(false))
+       end
+end
+
+class SaveButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game, "SAVE", 540, 24, "purple", "Save the level", null)
+       end
+
+       redef fun click2(ev)
+       do
+               var res = game.grid.save
+               print "SAVE: {res}"
+       end
+end
+
+class LoadButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game,"LOAD", 540, 61, "purple", "Load an user level", null)
+       end
+
+       redef fun click2(ev)
+       do
+               var grid2 = new Grid(game.gw,game.gh,game.monsters)
+               if grid2.load("") then
+                       game.grid = grid2
+               end
+               game.dirty_all = true
+       end
+end
+
+class ContinueButton
+       super TextButton
+       init(game: Game)
+       do
+               super(game,"CONTINUE", 440, 24, "purple", "Continue playing", null)
+       end
+
+       redef fun click2(ev)
+       do
+               game.play_next
+       end
+end
+
+class StartButton
+       super TextButton
+       var level: Level
+       init(game: Game, level: Level)
+       do
+               self.level = level
+               if level.number == 0 then
+                       super(game,"START", 440, 24, "purple", "Play the first level", null)
+               else
+                       super(game,"CONTINUE", 440, 24, "purple", "Continue from level "+level.name, null)
+               end
+       end
+
+       redef fun click2(ev)
+       do
+               game.play(level)
+       end
+end
+
+#
+
+redef class App
+
+       # The game
+       var game: Game
+
+       # Wanted screen width
+       var screen_width = 640
+
+       # Wanted screen height
+       var screen_height = 480
+
+       redef fun window_created
+       do
+               super
+               game = new Game
+               game.font.hspace = -2
+               if args.length > 0 then
+                       game.play(game.levels[args.first.to_i])
+               end
+               # img loading?
+       end
+
+       # Maximum wanted frame per second
+       var max_fps = 30
+
+       # clock used to track FPS
+       private var clock = new Clock
+
+       redef fun frame_core(display)
+       do
+               game.step
+               game.draw(display)
+               var dt = clock.lapse
+               var target_dt = 1000000000 / max_fps
+               if dt.sec == 0 and dt.nanosec < target_dt then
+                       var sleep_t = target_dt - dt.nanosec
+                       sys.nanosleep(0, sleep_t)
+               end
+       end
+
+       redef fun input(input_event)
+       do
+               #print input_event
+               if input_event isa QuitEvent then # close window button
+                       quit = true # orders system to quit
+               else if input_event isa PointerEvent then
+                       var ev = new Event(input_event.x.to_i, input_event.y.to_i, "")
+                       if input_event.is_motion then
+                               game.onMouseMove(ev)
+                       else if input_event.pressed then
+                               game.onMouseDown(ev)
+                       else
+                               game.onMouseUp(ev)
+                       end
+                       return true
+               else if input_event isa KeyEvent and input_event.is_down then
+                       var ev = new Event(0, 0, input_event.key_name)
+                       game.onKeyDown(ev)
+                       return true
+               end
+
+               return false
+       end
+end
+
+redef class PointerEvent
+       fun is_motion: Bool do return false
+end
+
+redef class KeyEvent
+       fun key_name: String
+       do
+               var c = to_c
+               if c != null then return c.to_s
+               return "unknown"
+       end
+end
diff --git a/contrib/friendz/src/friendz_android.nit b/contrib/friendz/src/friendz_android.nit
new file mode 100644 (file)
index 0000000..94b9d2b
--- /dev/null
@@ -0,0 +1,27 @@
+# Monsterz - Chains of Friends
+#
+# 2010-2014 (c) Jean Privat <jean@pryen.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the Do What The Fuck You Want To
+# Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+
+# android version of the game
+module friendz_android
+
+import friendz
+import mnit_android
+
+redef class App
+       redef fun window_created
+       do
+               super
+               var w = screen_width
+               display.set_viewport(0,0,w,w*display.height/display.width)
+       end
+end
+
+redef class AndroidPointerEvent
+       redef fun is_motion do return not motion_event.just_went_down
+end
diff --git a/contrib/friendz/src/friendz_linux.nit b/contrib/friendz/src/friendz_linux.nit
new file mode 100644 (file)
index 0000000..7940843
--- /dev/null
@@ -0,0 +1,27 @@
+# Monsterz - Chains of Friends
+#
+# 2010-2014 (c) Jean Privat <jean@pryen.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the Do What The Fuck You Want To
+# Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+
+# Linux (SDL) version of the game
+module friendz_linux
+
+import friendz
+import mnit_linux
+
+redef class Display
+       redef fun wanted_width do return app.screen_width
+       redef fun wanted_height do return app.screen_height
+end
+
+redef class SDLDisplay
+       redef fun enable_mouse_motion_events do return true
+end
+
+redef class SDLMouseMotionEvent
+       redef fun is_motion do return true
+end
diff --git a/contrib/friendz/src/grid.nit b/contrib/friendz/src/grid.nit
new file mode 100644 (file)
index 0000000..3913154
--- /dev/null
@@ -0,0 +1,571 @@
+# Monsterz - Chains of Friends
+#
+# 2010-2014 (c) Jean Privat <jean@pryen.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the Do What The Fuck You Want To
+# Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+
+# Game login on the grid of monsters
+module grid
+
+# Grid of monsters.
+class Grid
+       # width of the current grid
+       var width: Int
+
+       # maximum width of the grid
+       var max_width: Int
+
+       # height of the current grid
+       var height: Int
+
+       # maximum height of the grid
+       var max_height: Int
+
+       # nm is the number of monster + 1 for the empty tile
+       var nb_monsters: Int
+
+       # the data grid
+       private var grid = new Array[Array[Tile]]
+
+       init(mw,mh,nm: Int)
+       do
+               self.max_width = mw
+               self.max_height = mh
+               self.nb_monsters = mh
+               clear
+       end
+
+       # Reinitialize the grid with new empty tiles and monsters info
+       fun clear
+       do
+               self.number = 0
+               self.error = 0
+               self.won = false
+               for i in [0..max_width[ do
+                       self.grid[i] = new Array[Tile]
+                       for j in [0..max_height[ do
+                               var t = new Tile(self, i, j)
+                               self.grid[i][j] = t
+                       end
+               end
+               self.monsters = new Array[MonsterInfo]
+               for i in [0..nb_monsters] do
+                       self.monsters[i] = new MonsterInfo
+               end
+               self.resize(max_width,max_height)
+       end
+
+       # Clear all monsters
+       # if `fixed` is false, fixed monsters remains
+       fun reset(fixed: Bool): Bool
+       do
+               var r = false
+               for i in [0..width[ do
+                       for j in [0..height[ do
+                               var t = self.grid[i][j]
+                               if fixed then t.fixed = false
+                               if not t.fixed and t.kind>0 then
+                                       t.update(0)
+                                       r = true
+                               end
+                       end
+               end
+               return r
+       end
+
+       # Total number of monsters in the grid
+       var number = 0
+
+       # Number of monsters alone or with >=3 next
+       var error = 0
+
+       # information about each kind of monsters
+       var monsters = new Array[MonsterInfo]
+
+       # Last result of win.
+       var won = false
+
+       # Check that the monster of `kind` form a complete chain.
+       # if not null, `t` is used as a starting tile to check the chain
+       fun check_chain(kind: Int, t: nullable Tile): Bool
+       do
+               var m = monsters[kind]
+               if m.angry > 0 or m.lonely > 0 or m.single > 2 then
+                       # easy case for no chain
+                       # print "easy {kind} chain: false"
+                       m.chain = false
+                       return false
+               else
+                       if t == null then
+                               # Search for a starting
+                               for x in [0..width[ do for y in [0..height[ do
+                                       var t2 = get(x,y)
+                                       if t2 == null or t2.kind != kind then continue
+                                       t = t2
+                                       break label found
+                               end label found
+                               if t == null then
+                                       assert m.number == 0
+                                       m.chain = true
+                                       return m.chain
+                               end
+                               # print "get neighbor {t}"
+                       end
+                       assert t.kind == kind
+                       var c = count_chain(t, 1000.rand)
+                       # print "old {kind} chain? {c} / {m.number}"
+                       m.chain = c == m.number
+                       return m.chain
+               end
+       end
+
+       # The total number of monsters connected to the chain of `t`
+       # `mark` is a uniq number used to mark tiles
+       fun count_chain(t: Tile, mark: Int): Int
+       do
+               t.chain_mark = mark
+               var res = 1
+               for i in [-1..1] do
+                       for j in [-1..1] do
+                               if (i==0 and j==0) or (i!=0 and j!=0) then continue
+                               var t2 = get(t.x+i,t.y+j)
+                               if t2 == null then continue
+                               if t2.chain_mark == mark or t2.kind != t.kind then continue
+                               res += count_chain(t2, mark)
+                       end
+               end
+               return res
+       end
+
+       # Resize the grid. Do not touch the content.
+       fun resize(w,h: Int)
+       do
+               self.width = w
+               self.height = h
+       end
+
+       # Try to get the tila at `x`,`y`.
+       # Returns null if the position is out of bound.
+       fun get(x,y: Int): nullable Tile
+       do
+               if x<0 or x>=self.width or y<0 or y>=self.height then return null
+               return self.grid[x][y]
+       end
+
+
+       var fixed_shaped = """[
+       [{x:1,y:0},{x:2,y:0},{x:1,y:1},{x:2,y:1}],
+       [{x:0,y:0},{x:0,y:1},{x:0,y:2}],
+       [{x:1,y:2},{x:2,y:2},{x:3,y:2}],
+       [{x:4,y:1},{x:4,y:2}],
+       [{x:3,y:0},{x:4,y:0}],
+       [{x:3,y:1}]
+       ]"""
+
+       # Set shapes for the fixed blocks.
+       fun metalize
+       do
+               for i in [0..width[ do
+                       for j in [0..height[ do
+                               var t = self.grid[i][j]
+                               if t.fixed then t.shape = null
+                       end
+               end
+               for shape in fixed_shaped.split(",") do
+                       for i in [0..width[ do
+                               for j in [0..height[ do
+                                       var ts = new Array[Tile]
+                                       for l in [0..shape.length[ do
+                                               #var t = self.get(i+shape[l].x-shape[0].x,j+shape[l].y-shape[0].y)
+                                               var t = self.get(i,j)
+                                               if t != null and t.fixed and t.shape == null then ts.push(t)
+                                       end
+                                       if ts.length == shape.length then
+                                               for l in [0..shape.length[ do
+                                                       ts[l].shape = shape[l]
+                                               end
+                                       end
+                               end
+                       end
+               end
+       end
+
+       # Return the serialization of the fixed tiles. */
+       fun save: String
+       do
+               var res = ""
+               var str = ".#ABCDEFGHI"
+               for y in [0..height[ do
+                       var rle = 0
+                       var last: nullable Int = null
+                       for x in [0..width[ do
+                               var t = self.grid[x][y]
+                               var tk = 0
+                               if t.fixed then tk = t.kind + 1
+                               if tk == last and rle<9 then
+                                       rle += 1
+                               else
+                                       if last != null then
+                                               if rle>1 then res += rle.to_s
+                                               res += str.chars[last].to_s
+                                       end
+                                       rle = 1
+                                       last = tk
+                               end
+                       end
+                       if last != null then
+                               if rle>1 then res += rle.to_s
+                               res += str.chars[last].to_s
+                       end
+                       res += "|"
+               end
+               return res
+       end
+
+       # Load a new grid from a seialization.
+       fun load(str: String): Bool
+       do
+               self.clear
+               var l = str.length
+               var x = 0
+               var y = 0
+               var mx = 1
+               var my = 1
+               var rle = 1
+               for i in [0..l[ do
+                       var z = rle
+                       while z > 0 do
+                               z -= 1
+                               rle = 1
+                               var c = str.chars[i]
+                               if c == '|' then
+                                       if x > mx then mx = x
+                                       x = 0
+                                       y += 1
+                               else if c == '.' then
+                                       x += 1
+                               else if c == '#' then
+                                       var t = self.get(x,y)
+                                       t.fixed = true
+                                       x += 1
+                               else if c >= 'A' and c <= 'I' then
+                                       var t = self.get(x,y)
+                                       assert t != null
+                                       t.update(c.ascii-'A'.ascii+1)
+                                       t.fixed = true
+                                       x += 1
+                               else if c >= '1' and c <= '9' then
+                                       rle = c.to_i
+                               else
+                                       abort
+                               end
+                       end
+               end
+               if x>0 then y += 1
+               if x > mx then mx = x
+               if y > my then my = y
+               if mx<3 or my<3 or mx>=max_width or my>=max_height then
+                       return false
+               end
+               self.resize(mx,my)
+               self.metalize
+               return true
+       end
+
+       # A ASCII version of the grid.
+       redef fun to_s: String
+       do
+               var ansicols = once ["37;1","31","36;1","32;1","35;1","33;1","33","34;1","31;1","37"]
+               var b = new FlatBuffer
+               b.append("{width}x{height}\n")
+               for j in [0..height[ do
+                       for i in [0..width[ do
+                               var t = grid[i][j]
+                               var k = t.kind
+                               var c = ' '
+                               if k == 0 then
+                                       if t.fixed then c = '#'
+                               else
+                                       b.add(0x1b.ascii)
+                                       b.add('[')
+                                       b.append ansicols[k]
+                                       c = (k + 'a'.ascii - 1).ascii
+                                       if t.fixed then c = c.to_upper
+                                       b.append("m")
+                               end
+                               b.add(c)
+                               if k != 0 then
+                                       b.add(0x1b.ascii)
+                                       b.append("[0m")
+
+                               end
+                       end
+                       b.append "|\n"
+               end
+               return b.to_s
+       end
+
+       # Return a copy of the current grid.
+       # if (!no_fixed) copy only the fixed tiles.
+       fun copy(no_fixed: Bool): Grid
+       do
+               var g = new Grid(self.max_width, self.max_height, self.nb_monsters)
+               g.resize(width, height)
+               for y in [0..height[ do
+                       for x in [0..width[ do
+                               var t = self.grid[x][y]
+                               if no_fixed or t.fixed then
+                                       var t2 = g.grid[x][y]
+                                       t2.update(t.kind)
+                                       t2.fixed = t.fixed
+                               end
+                       end
+               end
+               g.metalize
+               return g
+       end
+
+       # Internal check of the validity of tile and monster informations
+       fun check_grid
+       do
+               var m2 = new Array[MonsterInfo]
+               for m in [0..nb_monsters] do
+                       m2[m] = new MonsterInfo
+               end
+               for x in [0..width[ do
+                       for y in [0..height[ do
+                               var n = 0
+                               var f = 0
+                               var t = get(x,y)
+                               assert t != null
+                               assert t.x == x
+                               assert t.y == y
+                               var k = t.kind
+                               if k == 0 then continue
+
+                               for i in [-1..1] do
+                                       for j in [-1..1] do
+                                               if i == j or (i != 0 and j != 0) then continue
+                                               var t2 = get(x+i, y+j)
+                                               if t2 == null then continue
+                                               if t2.kind == k then
+                                                       n += 1
+                                               else if t2.kind == 0 and not t2.fixed then
+                                                       f += 1
+                                               end
+                                       end
+                               end
+                               assert n == t.nexts else
+                                       print self
+                                       print "{t} says {t.nexts} nexts, found {n}"
+                               end
+                               #assert f == t.frees else
+
+                               var m = m2[k]
+                               m.number += 1
+                               if n == 0 then
+                                       m.lonely += 1
+                               else if n == 1 then
+                                       m.single += 1
+                               else if n > 2 then
+                                       m.angry += 1
+                               end
+                       end
+               end
+               for m in [1..nb_monsters] do
+                       assert m2[m].number == monsters[m].number
+                       assert m2[m].lonely == monsters[m].lonely
+                       assert m2[m].single == monsters[m].single
+                       assert m2[m].angry == monsters[m].angry
+               end
+       end
+end
+
+# Information about each kind of monsters
+class MonsterInfo
+       # number of monsters of this kind on board
+       var number = 0
+       # number of monsters of this kind to place, -1 if no limit
+       var remains: Int = -1
+       # number of monsters that have exactly 1 next
+       var single = 0
+       # number of monsters that have exactly 0 next
+       var lonely = 0
+       # number of monsters that have 3 or more next
+       var angry = 0
+       # Are all monsters form a wining chain?
+       var chain = false
+end
+
+# A localized tile of a grid, can contain a monster and be fixed.
+class Tile
+       # The grid of the tile.
+       var grid: Grid
+
+       # The x coordinate in the grid (starting from 0).
+       var x: Int
+
+       # The y coordinate in the grid (starting from 0).
+       var y: Int
+
+       # The kind of monster in the grid. 0 means empty.
+       var kind = 0
+
+       # blink time to live (0 means no blinking).
+       var blink = 0
+
+       # shocked time to live (0 means not shocked)
+       var shocked = 0
+
+       # number of neighbors of the same kind.
+       var nexts = 0
+
+       # number of free non fixed next tiles
+       var frees = 0
+
+       # is the tile editable (metal block)
+       var fixed = false
+
+       redef fun to_s
+       do
+               var s
+               if fixed then
+                       s = "#ABCDEFGHI"
+               else
+                       s = ".abcdefghi"
+               end
+               return "\{{x},{y}:{s.chars[kind]}\}"
+       end
+
+       # Shape for metal block
+       var shape: nullable Object = null
+
+       # Flag for `count_chain` computation.
+       private var chain_mark = 0
+
+       # Set a new kind of monster on tile
+       # Return true is the move made the grid unsolvable (bad move)
+       fun update(nkind: Int): Bool
+       do
+               var t = self
+               var g = self.grid
+               var res = false
+               var okind = t.kind
+               if okind == nkind then return false
+
+
+               # First, remove it and update info.
+               if okind > 0 then
+                       var m = g.monsters[okind]
+                       var n = t.nexts
+                       if n > 2 then
+                               g.error -= 1
+                               m.angry -= 1
+                       else if n == 1 then
+                               m.single -= 1
+                       else if n == 0 then
+                               g.error -= 1
+                               m.lonely -= 1
+                       end
+                       m.number -= 1
+                       g.number -= 1
+               end
+               t.nexts = 0
+               t.blink = 5
+               t.frees = 0
+
+               var a_neigbor: nullable Tile = null
+               # update neighbors
+               for i in [-1..1] do
+                       for j in [-1..1] do
+                               if (i==0 and j==0) or (i!=0 and j!=0) then continue
+                               var t2 = g.get(t.x+i,t.y+j)
+                               if t2 == null then continue
+                               if t2.kind == 0 then
+                                       if not t2.fixed then t.frees += 1
+                                       continue
+                               end
+                               var m = g.monsters[t2.kind]
+
+                               if t2.kind == okind then
+                                       if a_neigbor == null then a_neigbor = t2
+                                       # same than old, thus dec neighbors
+                                       t2.nexts -=1
+                                       var n = t2.nexts
+                                       if n == 2 then
+                                               g.error -= 1
+                                               m.angry -= 1
+                                       else if n == 1 then
+                                               m.single += 1
+                                               g.error += 1
+                                       else if n == 0 then
+                                               m.single -= 1
+                                               m.lonely += 1
+                                       end
+                                       # print "+ {t} one less next: {t2} ; +({i}x{j})"
+                               end
+
+                               if t2.kind == nkind then
+                                       # Same than new, thus inc neighbors
+                                       t2.nexts += 1
+                                       t.nexts += 1
+                                       var n = t2.nexts
+                                       if n > 3 then
+                                               res = true
+                                       else if n == 3 then
+                                               g.error += 1
+                                               m.angry += 1
+                                               res = true
+                                       else if n == 2 then
+                                               m.single -= 1
+                                               g.error -= 1
+                                       else if n == 1 then
+                                               m.single += 1
+                                               m.lonely -= 1
+                                       end
+                                       # print "+ {t} one more next: {t2}"
+                               end
+                       end
+               end
+
+               # Add and update neighbors info
+               t.kind = nkind
+               if nkind > 0 then
+                       var m = g.monsters[nkind]
+                       var n = t.nexts
+                       if n > 2 then
+                               g.error += 1
+                               m.angry += 1
+                       else if n == 1 then
+                               m.single += 1
+                               g.error += 1
+                       else if n == 0 then
+                               g.error += 1
+                               m.lonely += 1
+                       end
+                       m.number+=1
+                       g.number+=1
+
+                       g.check_chain(nkind, t)
+               end
+
+               # check if the old kind broke, or create a chain
+               if okind > 0 then
+                       g.check_chain(okind, a_neigbor)
+               end
+
+               # update win status
+               g.won = true
+               for m in g.monsters do
+                       if m.number > 0 and not m.chain then g.won = false
+               end
+
+               #grid.check_grid
+
+               return res
+       end
+end
+
diff --git a/contrib/friendz/src/level.nit b/contrib/friendz/src/level.nit
new file mode 100644 (file)
index 0000000..d459875
--- /dev/null
@@ -0,0 +1,190 @@
+# Monsterz - Chains of Friends
+#
+# 2010-2014 (c) Jean Privat <jean@pryen.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the Do What The Fuck You Want To
+# Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+
+# Level managment
+module level
+
+import grid
+
+# A level in the game
+class Level
+       # The associated game
+       var game: Game
+
+       init(game: Game, i: Int, code: String)
+       do
+               self.game = game
+               var ls = code.split(";")
+               self.number = i
+               self.str = ls[0]
+               self.par = ls[1].to_i
+               if ls.length >= 3 then
+                       self.status = ls[2]
+               end
+               self.is_tutorial = i<=4
+               self.is_challenge = i>=25
+               if self.is_tutorial then
+                       self.name = "T{i+1}"
+                       self.fullname = "Tutorial level {i+1}"
+               else if not self.is_challenge then
+                       self.name = (i-4).to_s
+                       self.fullname = "Level {i-4}"
+               else
+                       self.name = "C{i-24}"
+                       self.fullname = "Challenge level {i-24}"
+               end
+       end
+
+       # number the level (0 for first)
+       var number: Int
+
+       # initial grid position
+       var str: String
+
+       # top score
+       var par: Int
+
+       # Help message if any
+       var status: String = ""
+
+       # Is the level a tutorial level?
+       var is_tutorial: Bool
+
+       # Is the level a challenge level?
+       var is_challenge: Bool
+
+       # The short name of the level (eg. "T1")
+       var name: String
+
+       # The long name of the level (eg. "Tutorial level 1")
+       var fullname: String
+
+       # The player best wining score.
+       # 0 it not yet won
+       var score = 0
+
+       var l_disabled = 1
+       var l_open = 2
+       var l_won = 3
+       var l_par = 4
+
+       fun get_state: Int
+       do
+               if self.score == 0 then
+                       if self.number == 0 or game.levels[self.number-1].score > 0 then return l_open
+                       if self.number == 25 and game.levels[19].score > 0 then return l_open else return l_disabled
+               else if self.score < self.par or not game.levels[9].score > 0 then
+                       return l_won
+               else return l_par
+       end
+
+       # Returns true if g is a wining condition for the level.
+       fun check_won(g: Grid): Bool
+       do
+               var w = g.won and (not self.is_challenge or g.number >= self.par)
+               if not w then return false
+               if g.number > self.score then
+                       self.score = g.number
+                       self.save
+               end
+               return true
+       end
+
+       fun save
+       do
+               #save_cookie("s"+self.hash, self.score>0?self.score:"")
+       end
+end
+
+# main game object
+class Game
+       # Game version
+       var version = "1.99"
+
+       # Names of kind of monsters @constant
+       var colors: Array[nullable String] = [null, "Red", "Cyan", "Green", "Purple", "Yellow", "Orange", "Blue", "Pink", "White"]
+
+       # max grid width
+       var gw = 16
+
+       # max grid height
+       var gh = 16
+
+       # Number of monster kinds (+1 for empty) @constant
+       var monsters = 9
+
+       # The grid to play on
+       var grid = new Grid(gw, gh, monsters)
+
+       # LEVELS ******************************************************************
+
+       # Raw level description
+       var levels_code: Array[String] = [
+       ".3#|A2.A|3#.|;6;Connect two monsters",
+       ".#.|A.#|#.A|;5;Diagonals do not count",
+       "2.A|A#.|2.A|;8;Connect all monsters",
+       "3A.|3.A|2.A.|;10;Only 1 or 2 neighbors",
+       "2.A.|.B.B|2.A.|;12;Build two chains",
+
+       "A.A#|4.|#.#.|A3.|;11",
+       "A2.#A|5.|2.A2.|4.#|4.A|;17",
+       "A2.B2.|.B2.A.|6.|.AB2.B|6.|6.|;36",
+       "A5.|B5.|#.A3.|6.|2.A.B.|.A4.|;26",
+       "8.|BA2.B.A.|2.C.C.B.|6.#.|;29",
+
+       ".B3.B|6.|.#3.#|.C3.B|.#4.|6.|.C3.C|;30",
+       ".A.#4.|.C3.A.E|.A6.|6.C.|8.|2.6#|6.E#|5.C2#|;54",
+       ".A5.A|.A.#3.A|2.#A.#.#|.#.#4.|2.A3.#.|8.|4.A2.A|2.A2.A.#|;37",
+       "AB3.C|2.AC2.|6.|.B.CB.|6.|;30",
+       "AC5#CA|2.#3.#2.|4.#4.|7.#.|5.#.#.|9.|2.G6.|6.2#.|9.|H#F#G#F#H|;72",
+
+       "AG3.C|2.AC2.|6.|.G2.G.|2.C3.|6.|;32",
+       "7.#2.|3.#.#4.|.#8.|7.D2.|2.#.3#3.|2.#3.2#FC|9..|2.2#3.FCD|9..|;71",
+       "2.#A4.|6.B.|.C6.|.A.A.AC.|#7.|5.B2.|8.|#A5.C|;56",
+       "9.3.|8.#3.|2.B.#3.BA2.|2.AC4.DC2.|2.EA8.|8.#3.|.2#3.#5.|7.#4.|2#3.#B5.|5.CD5.|.2#9.|3.E8.|;119",
+       "9.7.|.2#B8#E2#.|.E8.#3.#.|.#3.4#.#.#.#.|.#.#.#4.#.#.#.|.C.#3.2#.#.#.#.|.#.#.4#3.#.#.|.D8.3#.#.|.#.#.4#5.A.|.A5.#2.#.3#.|.3#.#.2#.#2.2#.|.D3.#4.2#2.#.|.#.3#.#.4#.#.|.#5.#6.#.|.3#C6#B3#.|9.7.|;130",
+
+       "8.|.#C2.C#.|.#E2.E#.|8.|8.|.B#2.#A.|.#C2.C#.|8.|8.|.BF2.FA.|.2#2.2#.|8.|8.|.2#2.2#.|.A#2.B#.|8.|;92",
+       ".A9..|D9.F.|7.CA3.|3.E8.|9.2.H|9.2.#|7.G4.|4.BG2.F3.|2.B6.E2.|9.G2.|.H8.C.|8.D3.|;140",
+       "2.F9..|2.C9..|9..C2.|8.AG3.|9.4.|4.2#5.F.|7.E4.#|3.A9.|.G.CF4.C.#.|9.2.E.|;125",
+       "9.HCB|.DH9.|.GC9.|9.3.|4.G7.|3.#8.|9.3.|3.B4.D3.|.G9..|8.#3.|2.C2.2#5.|9.3.|;135",
+       "9.6.A|6.C9.|2.A.B4.E3.#I.|9.I.#4.|9.7.|2.E3.C9.|7.B8.|5.G6.G3.|6.C6.G2.|9..H3.B.|.CH.#5.#2.C2.|4.H3.#3.H3.|.B3.F5.F4.|.D.D4.D5.D.|9.7.|6.C9.|;244",
+
+       "E6.|7.|.F.E.F.|.F.#.E.|.F.E.E.|7.|6.F|;48",
+       "3.2#3.|2.A2.A2.|.C4.C.|8.|3.2#3.|2.A2.A2.|.C4.C.|8.|;56",
+       "3.#3.A4.|5.#6.|3.#5.#2.|.#4.#4.#|4.#7.|#6.#4.|8.#3.|.#9..|.#3.#4.#.|3.2#3.#3.|;74",
+       "2.A7.|2.D7.|2.A4.A2.|2.G4.G2.|2.A4.D2.|2.D7.|2.A7.|2.G7.|2.D7.|2.G7.|;95",
+       "E9.6.|9.H4.#.|9.E2.#3.|9.F2.#2.#|9.3.H2.#|HEF9.3.#|9.7.|6.#9.|6.#2.H3.HEF|.6#2.E6.|9.F6.|5#9.2.|4.#9.#.|.F#3.7#.#.|9.5.#.|9.7.|;225"
+       ]
+
+       # The loaded levels
+       var levels = new Array[Level]
+
+       # Load levels
+       # used duting `init`
+       fun load_levels
+       do
+               # Transform level strings into level objects. */
+               for i in [0..levels_code.length[ do
+                       var l = new Level(self,i, levels_code[i])
+                       levels[i] = l
+                       #var v = read_cookie("s"+l.hash)
+                       #l = v
+               end
+       end
+
+       # The current played level (if any)
+       var level: nullable Level = null
+
+       init
+       do
+               load_levels
+       end
+end
+
diff --git a/contrib/friendz/src/solver.nit b/contrib/friendz/src/solver.nit
new file mode 100644 (file)
index 0000000..5b80fd4
--- /dev/null
@@ -0,0 +1,230 @@
+# Monsterz - Chains of Friends
+#
+# 2010-2014 (c) Jean Privat <jean@pryen.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the Do What The Fuck You Want To
+# Public License, Version 2, as published by Sam Hocevar. See
+# http://sam.zoy.org/projects/COPYING.WTFPL for more details.
+
+# Basic game solver
+module solver
+intrude import grid
+
+# A solver with a deep-first search algorithm
+# Instead of storig grids in memory, it plays by placing monsters and backtrack by removing placed monsters.
+class Solver
+       # Return true if grid accepts kind at tile t.
+       # Accepts means that
+       # * the tile is playble (free and not fixed)
+       # * the new monster will not be angry
+       # * the new monster will not make neighbor angry
+       fun accepts(t: Tile, kind: Int): Bool
+       do
+               var grid = t.grid
+               if t.kind != 0 or t.fixed then return false
+               #if t.x<0 or t.x>=grid.width or t.y<0 or t.y>=grid.height then return false
+               var cpt = 0
+               for i in [-1..1] do
+                       for j in [-1..1] do
+                               if i==0 and j==0 or i!=0 and j!=0 then continue
+                               var x2 = t.x+i
+                               var y2 = t.y+j
+                               if x2<0 or x2>=grid.width or y2<0 or y2>=grid.height then continue
+                               var t2 = grid.grid[x2][y2]
+                               if t2.kind == kind then
+                                       if t2.nexts >= 2 then return false
+                                       cpt += 1
+                                       if cpt>=3 then return false
+                               end
+                       end
+               end
+               return true
+       end
+
+       # Return the list of accepting neighbors of t
+       fun frees(t: Tile): Array[Tile]
+       do
+               var grid = t.grid
+               var res = new Array[Tile]
+               for i in [-1..1] do
+                       for j in [-1..1] do
+                               if i==0 and j==0 or i!=0 and j!=0 then continue
+                               var x2 = t.x+i
+                               var y2 = t.y+j
+                               if x2<0 or x2>=grid.width or y2<0 or y2>=grid.height then continue
+                               var t2 = grid.grid[x2][y2]
+                               if accepts(t2, t.kind) then res.push(t2)
+                       end
+               end
+               return res
+       end
+
+       # The grid played on
+       var grid: Grid
+
+       # Open moves to play
+       # If empty, it means the next move will be backtraking
+       var tries = new Array[Tile]
+
+       # The color played for `tries`
+       var kind = 1
+
+       # Search free tiles next to lonely monsters
+       # Used to initialize `tries`
+       fun look_start: Array[Tile]
+       do
+               var start = new Array[Tile]
+               var min = 1
+               kind = 0
+
+               var tile: nullable Tile = null
+
+               for x in [0..grid.width[ do
+                       for y in [0..grid.height[ do
+                               var t = grid.grid[x][y]
+                               var k = t.kind
+                               if k == 0 or grid.monsters[k].chain then continue
+                               var n = t.nexts
+
+                               if n > min then continue
+
+                               var fs = frees(t)
+                               var l = fs.length
+                               if l == 0 then continue
+
+                               tile = t
+                               if n == 0 then
+                                       if min == 1 or start.length > l then
+                                               start = fs
+                                               kind = k
+                                       end
+                               else if kind == 0 or kind == k then
+                                       start.add_all fs
+                                       kind = k
+                               end
+
+                               min = n
+                       end
+               end
+               #print "-------------"
+               #print grid
+               #dump
+               #print "START: {tile} -> {start.join(",")}"
+               #print "-------------"
+               return start
+       end
+
+       # compute and print some metrics about the problem
+       fun size_problem
+       do
+               var free = 0
+               for x in [0..grid.width[ do
+                       for y in [0..grid.height[ do
+                               var t = grid.grid[x][y]
+                               if t.kind == 0 and not t.fixed then free += 1
+                       end
+               end
+               print "FREE: {free}"
+               var ms = 0
+               for m in grid.monsters do
+                       if m.number > 0 then ms += 1
+               end
+               print "KINDS: {ms}"
+               print "SIZE: {(ms+1).to_f.pow(free.to_f)}"
+       end
+
+       init(grid: Grid)
+       do
+               self.grid = grid
+               size_problem
+               tries = look_start
+       end
+
+       # Zipper in the search tree as a FIFO
+       var history = new Array[Move]
+
+       # Perform a backtrack step
+       # Remove the placed monsters and go for the other tries
+       fun backtrack: Bool
+       do
+               if history.is_empty then
+                       is_over = true
+                       return true
+               end
+               var h = history.pop
+               tries = h.tries
+               opens -= tries.length
+               h.tile.update(0)
+               kind = h.kind
+               return false
+       end
+
+       # Number of player steps
+       var steps = 0
+
+       # Number of open in the
+       # real opens is `opens+tries.length`
+       var opens = 0
+
+       fun dump
+       do
+               print "STEPS: {steps}"
+               print "OPENS: {opens+tries.length}"
+               print "DEPTH: {history.length}"
+               print "NEXTS: {tries.join(", ")}"
+       end
+
+       # Is the last step exhausted all the possibilities?
+       var is_over = false
+
+       # Compute the next step.
+       # Return tru on a wining position (`grid.won`) or when the solver `is_over`
+       fun step: Bool
+       do
+               #print "=========="
+               #print grid
+               #dump
+               #print "=========="
+               steps += 1
+               if tries.is_empty then
+                       return backtrack
+               end
+               var t = tries.pop
+               var eval = 0
+               var fail = t.update(kind)
+               if not fail then
+                       #eval = evaluate_new_tile(t)
+                       #fail = (eval == -1)
+               end
+               if not fail then
+                       opens += tries.length
+                       var h = new Move(t, tries, kind)
+                       history.push(h)
+                       if grid.won then
+                               tries = new Array[Tile]
+                               return true
+                       end
+                       if t.nexts == 2 then
+                               tries = look_start
+                       else
+                               tries = frees(t)
+                       end
+               else
+                       t.update(0)
+               end
+               return false
+       end
+end
+
+# A stored move in the `Solver::history`
+class Move
+       # The tile played
+       var tile: Tile
+
+       # The remainig alternatives to try
+       var tries: Array[Tile]
+
+       # to color for the moves
+       var kind: Int
+end
diff --git a/contrib/jwrapper/Makefile b/contrib/jwrapper/Makefile
new file mode 100644 (file)
index 0000000..b9d9b00
--- /dev/null
@@ -0,0 +1,14 @@
+default:
+       mkdir -p bin
+       make -C ../nitcc
+       cp ../nitcc/src/nitcc bin/
+       ./bin/nitcc ./grammar/javap.sablecc
+       ../../bin/nitg ./src/javap_visitor.nit -o ./bin/jwrapper
+       mv *.nit ./src/
+       mkdir -p gen
+       mv javap* ./gen/
+
+clean:
+       rm -f bin/javap_test_parser bin/jwrapper
+       rm -f gen/*
+       rm -rf .nit_compile/
diff --git a/contrib/jwrapper/grammar/javap.sablecc b/contrib/jwrapper/grammar/javap.sablecc
new file mode 100644 (file)
index 0000000..4e9165a
--- /dev/null
@@ -0,0 +1,61 @@
+Grammar javap;
+
+Lexer
+
+identifier = ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'$'|'0'..'9')*;
+blank = (' '|'\n'|'\t'|'\r')+;
+separator = ('.'|'/');
+
+Parser
+Ignored blank;
+
+multi_files = class_or_interface*;
+
+class_or_interface = class_declaration | interface_declaration;
+
+class_declaration = class_header '{' field_declaration* '}';
+
+class_header = modifier* 'class' full_class_name extends_declaration?
+                          implements_declaration? throws_declaration?;
+interface_declaration = modifier* 'interface' full_class_name extends_interface_declaration?
+                                               '{' field_declaration* '}';
+
+modifier = 'public'|'private'|'protected'|'static'|'final'|'native'|'synchronized'|'abstract'|'threadsafe'|'transient'|'volatile';
+type = type_specifier '[]'*;
+type_specifier = 'boolean'|'byte'|'char'|'short'|'int'|'float'|'long'|'double' | type_ref;
+
+type_ref = full_class_name | generic_identifier 'extends' full_class_name | '?';
+type_refs = {tail:} type_refs ',' type_ref | {head:} type_ref;
+
+generic_param = '<' generic_parameter_list '>';
+generic_parameter_list = {tail:} generic_parameter_list ',' parameter | {head:} parameter;
+generic_identifier = full_class_name | '?';
+
+full_class_name = full_class_name separator class_name | class_name;
+class_name = identifier generic_param?;
+
+interface_name = full_class_name;
+interface_list = {tail:} interface_list ',' interface_name | {head:} interface_name;
+
+parameter = type '...'?;
+parameter_list_comp = {tail:} parameter_list_comp ',' parameter | {head:} parameter;
+parameter_list = parameter_list_comp;
+
+exception = type;
+exception_list = exception_list ',' exception | exception;
+
+statement = variable_declaration | statement_block | ';';
+statement_block = '{' statement* '}';
+
+variable_id = identifier '[]'*;
+method_id = identifier;
+
+field_declaration = method_declaration | constructor_declaration | variable_declaration | static_declaration | ';';
+variable_declaration = modifier* type variable_id throws_declaration? ';';
+method_declaration = modifier* generic_param? type method_id '(' parameter_list? ')' throws_declaration? ';';
+constructor_declaration = modifier* full_class_name '(' parameter_list? ')' throws_declaration? ';';
+implements_declaration = 'implements' interface_list*;
+extends_interface_declaration = 'extends' interface_list*;
+extends_declaration = 'extends' type;
+static_declaration = modifier* '{' '}' ';';
+throws_declaration = 'throws' exception_list?;
diff --git a/contrib/jwrapper/src/code_generator.nit b/contrib/jwrapper/src/code_generator.nit
new file mode 100644 (file)
index 0000000..e7c471a
--- /dev/null
@@ -0,0 +1,333 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to generate extern class `in "Java"`
+module code_generator
+
+intrude import types
+
+class CodeGenerator
+
+       var file_out: OFStream
+       var java_class: JavaClass
+       fun code_warehouse: CodeWarehouse do return once new CodeWarehouse
+
+       init (file_name: String, jclass: JavaClass) 
+       do
+               file_out = new OFStream.open(file_name)
+               self.java_class = jclass
+       end
+
+       fun generate
+       do
+               var jclass = self.java_class
+
+               file_out.write("import mnit_android\n")
+               gen_class_header(jclass.name)
+
+               # Attributes generation
+               for id, jtype in jclass.attributes do gen_attribute(id, jtype)
+
+               for id, methods_info in jclass.methods do
+                       for method_info in methods_info do
+                               var nid = id
+                               if methods_info.length > 1 then nid += "{methods_info.index_of(method_info)}"
+                               gen_method(id, nid, method_info.return_type, method_info.params)
+                       end
+               end
+
+               file_out.write("\nend")
+       end
+
+       fun gen_class_header(full_class_name: Array[String])
+       do
+               file_out.write("extern class Native{full_class_name.last} in \"Java\" `\{ {full_class_name.join(".")} `\}\n")
+               file_out.write("\tsuper JavaObject\n\tredef type SELF: Native{full_class_name.last}\n\n")
+       end
+
+       fun gen_attribute(jid: String, jtype: JavaType)
+       do
+               file_out.write("\tvar {jid.to_snake_case}: {jtype.to_nit_type}\n")
+       end
+       
+       fun gen_method(jmethod_id: String, nmethod_id: String, jreturn_type: JavaType, jparam_list: Array[JavaType])
+       do
+               var java_params = ""
+               var nit_params  = ""
+               var nit_id = "arg"
+               var nit_id_no = 0
+               var nit_types = new Array[NitType]
+               var comment = "" 
+
+               # Parameters
+               for i in [0..jparam_list.length[ do
+                       var jparam = jparam_list[i]
+                       var nit_type = jparam.to_nit_type
+                       var cast = ""
+
+                       if not jparam.is_collection then cast = jparam.param_cast
+
+                       nit_types.add(nit_type)
+                       nit_type.arg_id = "{nit_id}{nit_id_no}"
+
+                       if i == jparam_list.length - 1 then
+                               java_params += "{cast}{nit_id}{nit_id_no}"
+                               nit_params  += "{nit_id}{nit_id_no}: {nit_type}"
+                       else
+                               java_params += "{cast}{nit_id}{nit_id_no}" + ", "
+                               nit_params  += "{nit_id}{nit_id_no}: {nit_type}, "
+                       end
+
+                       nit_id_no += 1
+                       # Comment if one type is unknown
+                       if not nit_type.is_complete then comment = "#"
+               end
+
+               # Method identifier
+               var method_id = nmethod_id.to_snake_case
+               var nit_signature = new Array[String]
+
+               nit_signature.add "\tfun {method_id}"
+
+               if not jparam_list.is_empty then
+                       nit_signature.add "({nit_params})"
+               end
+
+               var return_type = null
+
+               if not jreturn_type.is_void then
+                       return_type = jreturn_type.to_nit_type
+                       if not return_type.is_complete then comment = "#"
+                       nit_signature.add ": {return_type} "
+               end
+
+               file_out.write(comment + nit_signature.join(""))
+
+               var param_to_copy = param_to_copy(jparam_list, nit_types)
+
+               # Copy one parameter, the return value, one parameter and the return value or nothing
+               if return_type != null then
+                       if return_type.is_complete and jreturn_type.is_collection then
+                               if param_to_copy != null then
+                                       var rtype_couple = new Couple[JavaType, NitType](jreturn_type, return_type)
+                                       file_out.write(code_warehouse.param_return_copy(rtype_couple, param_to_copy, jmethod_id, java_params))
+                               else
+                                       file_out.write(code_warehouse.return_type_copy(jreturn_type, return_type, jmethod_id, java_params))
+                               end
+                       else if param_to_copy != null then
+                               file_out.write(code_warehouse.param_type_copy(param_to_copy.first, param_to_copy.second, jmethod_id, java_params, true))
+                       else
+                               file_out.write(" in \"Java\" `\{\n\t\t{comment}return {jreturn_type.return_cast} recv.{jmethod_id}({java_params}); \n\t{comment}`\}\n")
+                       end
+               else if jreturn_type.is_void then
+                       if param_to_copy != null then
+                               file_out.write(code_warehouse.param_type_copy(param_to_copy.first, param_to_copy.second, jmethod_id, java_params, false))
+                       else
+                               file_out.write(" in \"Java\" `\{\n\t\t{comment}recv.{jmethod_id}({java_params}); \n\t{comment}`\}\n")
+                       end
+               else
+                       file_out.write(" in \"Java\" `\{\n\t\t{comment}recv.{jmethod_id}({java_params}); \n\t{comment}`\}\n")
+               end
+       end
+
+       # Only one collection type parameter can be copied
+       # If there's none or more than one then `null` is returned
+       fun param_to_copy(jtypes: Array[JavaType], ntypes: Array[NitType]): nullable Couple[JavaType, NitType]
+       do
+               var counter = 0
+               var couple = null
+               for i in [0..jtypes.length[ do
+                       if jtypes[i].is_collection and ntypes[i].is_complete then
+                               counter += 1
+                               if counter > 1 then return null
+                               couple = new Couple[JavaType, NitType](jtypes[i], ntypes[i])
+                       end
+               end
+
+               return couple
+       end
+end
+
+# Contains raw code mostly used to copy collections
+class CodeWarehouse
+
+       # Collection as return value
+       fun return_type_copy(java_type: JavaType, nit_type: NitType, jmethod_id, params_id: String): String
+       do
+               var narray_id = "nit_array"
+               var loop_ = create_loop(java_type, nit_type, false, "java_array", narray_id)
+               var imports = create_imports(nit_type, false)
+
+               return """{{{imports}}} in "Java" `{ 
+               {{{java_type.to_s}}} java_array = recv.{{{jmethod_id}}}({{{params_id}}});
+               int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}();
+
+               {{{loop_}}}
+
+               return {{{narray_id}}};
+       `}
+       """
+       end
+
+       # Collection as parameter
+       fun param_type_copy(java_type: JavaType, nit_type: NitType, jmethod_id, params_id: String, has_return: Bool): String
+       do
+               var narray_id = "nit_array"
+               var jarray_id = "java_array"
+               var loop_ = create_loop(java_type, nit_type, true, jarray_id, narray_id)
+               var imports = create_imports(nit_type, true)
+               var jtype = java_type.to_s
+               var jinstanciation = create_array_instance(java_type, nit_type, jarray_id)
+               var return_str = ""
+               
+               if has_return then
+                       return_str = "return "
+               end
+
+               params_id = params_id.replace(nit_type.arg_id, jarray_id)
+
+               return """{{{imports}}} in "Java" `{ 
+               {{{jinstanciation}}}
+               int {{{narray_id}}} = new_{{{nit_type.id}}}_of_{{{nit_type.generic_params.join("_")}}}();
+
+               {{{loop_}}}
+
+               {{{return_str}}}recv.{{{jmethod_id}}}({{{params_id}}});
+       `}
+       """
+       end
+
+       # One collection parameter and the return type will be copied
+       fun param_return_copy(return_types, param_types: Couple[JavaType, NitType], jmethod_id, params_id: String): String
+       do
+               var narray_id = "nit_array"
+               var narray_id2 = "nit_array2"
+
+               var r_jtype = return_types.first
+               var r_ntype = return_types.second
+
+               var p_jtype = param_types.first
+               var p_ntype = param_types.second
+
+               var r_loop = create_loop(r_jtype, r_ntype, false, "java_array", narray_id)
+               var p_loop = create_loop(p_jtype, p_ntype, true, "java_array2", narray_id2)
+
+               var imports = new Array[String]
+               
+               # Avoid import duplication
+               if p_ntype.to_s != r_ntype.to_s then
+                       imports.add create_imports(p_ntype, true)
+               end
+
+               imports.add create_imports(r_ntype, false)
+
+               params_id = params_id.replace(p_ntype.arg_id, narray_id)
+
+               var jinstanciation = create_array_instance(p_jtype, p_ntype, "java_array")
+
+               return """{{{imports.join(", ")}}} in "Java" `{
+               {{{jinstanciation}}}
+
+               {{{p_loop}}}
+
+               {{{r_jtype.to_s}}} java_array2 = recv.{{{jmethod_id}}}({{{params_id}}});
+               int {{{narray_id2}}} = new_{{{r_ntype.id}}}_of_{{{r_ntype.generic_params.join("_")}}}();
+
+               {{{r_loop}}}
+
+               return {{{narray_id2}}};
+       `}
+       """
+       end
+
+       private fun create_array_instance(java_type: JavaType, nit_type: NitType, jarray_id: String): String
+       do
+               var jtype = java_type.to_s
+               var instanciation = ""
+
+               if java_type.is_primitive_array then
+                       instanciation = "{jtype} {jarray_id} = new {java_type.full_id}[Array_of_{nit_type.generic_params[0]}_length({nit_type.arg_id})];"
+               else
+                       instanciation = "{jtype} {jarray_id} = new {jtype}();"
+               end
+
+               return instanciation
+       end
+
+       private fun create_imports(nit_type: NitType, is_param: Bool): String
+       do
+               var imports = ""
+               var ntype = nit_type.to_s
+               var gen_type = nit_type.generic_params.join(", ")
+
+               if not is_param then
+                       if nit_type.is_map then
+                               imports = """import {{{ntype}}}, {{{ntype}}}.[]="""
+                       else
+                               imports = """import {{{ntype}}}, {{{ntype}}}.add"""
+                       end
+               else if nit_type.id == "Array" then
+                       imports = """import {{{ntype}}}.length, {{{ntype}}}.[]"""
+               else if nit_type.is_map then
+                       imports = """import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item, Iterator[{{{gen_type}}}].key"""
+               else
+                       imports = """import {{{ntype}}}.iterator, Iterator[{{{gen_type}}}].is_ok, Iterator[{{{gen_type}}}].next, Iterator[{{{gen_type}}}].item"""
+               end
+               
+               return imports
+       end
+
+       private fun create_loop(java_type: JavaType, nit_type: NitType, is_param: Bool, jarray_id, narray_id: String): String
+       do
+               var loop_header = ""
+               var loop_body = ""
+               var gen_type = nit_type.generic_params.join("_")
+
+               if is_param then
+                       if java_type.is_primitive_array then
+                               loop_header = "for(int i=0; i < {jarray_id}.length; ++i)"
+                               loop_body   = """\t\t\t{{{jarray_id}}}[i] = {{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{nit_type.arg_id}}}, i);"""
+                       else if nit_type.id == "Array" then
+                               loop_header = """int length = Array_of_{{{gen_type}}}_length({{{nit_type.arg_id}}});\n\t\tfor(int i=0; i < length; ++i)"""
+                               loop_body   = """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}Array_of_{{{gen_type}}}__index({{{narray_id}}}, i));"""
+                       else
+                               loop_header = """int itr = {{{nit_type.id}}}_of_{{{gen_type}}}_iterator({{{nit_type.arg_id}}});\n\t\twhile(Iterator_of_{{{gen_type}}}_is_ok(itr)) {"""
+                               if nit_type.is_map then
+                                       var key_cast = java_type.to_cast(java_type.generic_params[0].id, true)
+                                       var value_cast = java_type.to_cast(java_type.generic_params[1].id, true)
+                                       loop_body   = """\t\t\t{{{jarray_id}}}[{{{key_cast}}}iterator_of_{{{nit_type.id}}}_key(itr)] = {{{value_cast}}}iterator_of_{{{nit_type.id}}}_item(itr);\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}"""
+                               else
+                                       loop_body   = """\t\t\t{{{jarray_id}}}.add({{{java_type.param_cast}}}iterator_of_{{{nit_type.id}}}_item(itr));\n\t\t\titerator_of_{{{gen_type}}}_next(itr);\n\t\t}"""
+                               end
+                       end
+               else
+                       if nit_type.is_map then
+                               var key_cast = java_type.to_cast(java_type.generic_params[0].id, false)
+                               var value_cast = java_type.to_cast(java_type.generic_params[1].id, false)
+                               loop_header = """for (java.util.Map.Entry<{{{java_type.generic_params[0]}}}, {{{java_type.generic_params[1]}}}> e: {{{jarray_id}}})"""
+                               loop_body   = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_{{{nit_type.generic_params[1]}}}__index_assign({{{narray_id}}}, {{{key_cast}}}e.getKey(), {{{value_cast}}}e.getValue()); """
+                       else if java_type.is_iterable then
+                               loop_header = """for ({{{java_type.generic_params[0]}}} e: {{{jarray_id}}})"""
+                               loop_body   = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}e);"""
+                       else
+                               loop_header = "for(int i=0; i < {jarray_id}.length; ++i)"
+                               loop_body   = """\t\t\t{{{nit_type.id}}}_of_{{{gen_type}}}_add({{{narray_id}}}, {{{java_type.return_cast}}}{{{jarray_id}}}[i]);"""
+                       end
+               end
+
+               return loop_header + "\n" + loop_body
+       end
+end
diff --git a/contrib/jwrapper/src/javap_visitor.nit b/contrib/jwrapper/src/javap_visitor.nit
new file mode 100644 (file)
index 0000000..5e9896b
--- /dev/null
@@ -0,0 +1,493 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# 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.
+
+# Uses a visitor to extract data from the javap output AST
+# It sends the data to `code_generator` module
+module javap_visitor
+
+import javap_test_parser
+import code_generator
+intrude import types
+
+class JavaVisitor
+       super Visitor
+
+       var java_class = new JavaClass
+       var declaration_type: nullable String =  null
+       var declaration_element: nullable String = null
+       var full_class_name = new Array[String]
+
+       var variable_id = ""
+       var variable_type = new JavaType
+
+       var is_generic_param = false
+       var gen_params_index = 0
+
+       var is_primitive_array = false
+
+       var method_id = ""
+       var method_return_type = new JavaType
+       var method_params = new Array[JavaType]
+       var param_index = 0
+
+       redef fun visit(n) do n.accept_visitor(self)
+end
+
+redef class Node
+       fun accept_visitor(v: JavaVisitor) do visit_children(v)
+end
+
+redef class Nidentifier
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "class_header" then
+
+                       if v.declaration_element == "id" then
+                               v.full_class_name.add(self.text)
+                       end
+
+               else if v.declaration_type == "variable" then
+
+                       if v.declaration_element == "id" then
+                               v.variable_id += self.text
+                       else if v.declaration_element == "type" then
+                               if v.is_generic_param then
+                                       v.variable_type.generic_params[v.gen_params_index].identifier.add(self.text)
+                               else
+                                       v.variable_type.identifier.add(self.text)
+                               end
+                       end
+
+               else if v.declaration_type == "method" then
+
+                       if v.declaration_element == "id" then
+                               v.method_id = self.text
+                       else if v.declaration_element == "return_type" then
+                               if self.text == "void" then 
+                                       v.method_return_type.is_void = true
+                               else if v.is_generic_param then
+                                       v.method_return_type.generic_params[v.gen_params_index].identifier.add(self.text)
+                               else
+                                       v.method_return_type.identifier.add(self.text)
+                               end
+                       else if v.declaration_element == "parameter_list" then
+                               if v.is_generic_param then
+                                       v.method_params[v.param_index].generic_params[v.gen_params_index].identifier.add(self.text)
+                               else
+                                       v.method_params[v.param_index].identifier.add(self.text)
+                               end
+                       end
+
+               end
+
+               super
+       end
+end
+
+# Primitive array node
+redef class N_39d_91d_93d_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               if v.is_generic_param then
+                                       v.variable_type.generic_params[v.gen_params_index].array_dimension += 1
+                               else
+                                       v.variable_type.array_dimension += 1
+                               end
+                       end
+
+               else if v.declaration_type == "method" then
+
+                       if v.declaration_element == "return_type" then
+                               if v.is_generic_param then
+                                       v.method_return_type.generic_params[v.gen_params_index].array_dimension += 1
+                               else
+                                       v.method_return_type.array_dimension += 1
+                               end
+                       else if v.declaration_element == "parameter_list" then
+                               if v.is_generic_param then
+                                       v.method_params[v.param_index].generic_params[v.gen_params_index].array_dimension += 1
+                               else
+                                       v.method_params[v.param_index].array_dimension += 1
+                               end
+                       end
+
+               end
+
+               super
+       end
+end
+
+redef class N_39dchar_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39dboolean_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39dfloat_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39ddouble_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39dbyte_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39dshort_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39dint_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+redef class N_39dlong_39d
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.variable_type.identifier.add(self.text)
+                       end
+               else if v.declaration_type == "method" then
+                       if v.declaration_element == "return_type" then
+                               v.method_return_type.identifier.add(self.text)
+                       else if v.declaration_element == "parameter_list" then
+                               v.method_params[v.param_index].identifier.add(self.text)
+                       end
+               end
+       end
+end
+
+#                                  #
+#    C L A S S     H E A D E R     #
+#                                  #
+redef class Nclass_header
+       redef fun accept_visitor(v)
+       do
+               v.declaration_type = "class_header"
+               v.declaration_element = "id"
+               super
+
+               # Exit class declaration
+               v.declaration_type = null
+               v.declaration_element = null
+
+               v.java_class.name = v.full_class_name
+       end
+end
+
+# Extends declaration in the class header
+redef class Nextends_declaration
+       redef fun accept_visitor(v)
+       do
+               v.declaration_element = "extends"
+               super
+               v.declaration_element = null
+       end
+end
+
+# Implements declaration in the class header
+redef class Nimplements_declaration
+       redef fun accept_visitor(v)
+       do
+               v.declaration_element = "implements"
+               super
+               v.declaration_element = null
+       end
+end
+
+#                                          #
+#   F I E L D    D E C L A R A T I O N S   #
+#                                          #
+
+# Method declaration in the field declarations
+redef class Nmethod_declaration
+       redef fun accept_visitor(v)
+       do
+               v.declaration_type = "method"
+               super
+               v.declaration_type = null
+
+               v.java_class.add_method(v.method_id, v.method_return_type, v.method_params)
+
+               v.method_params.clear
+               v.method_id = ""
+               v.method_return_type = new JavaType
+       end
+end
+
+# Constructor declaration in the field declarations
+redef class Nconstructor_declaration
+       redef fun accept_visitor(v)
+       do
+               v.declaration_type = "constructor"
+               super
+               v.declaration_type = null
+       end
+end
+
+# Variable declaration in the field declarations
+redef class Nvariable_declaration
+       redef fun accept_visitor(v)
+       do
+               v.declaration_type = "variable"
+               super
+               v.declaration_type = null
+
+               v.java_class.attributes[v.variable_id] = v.variable_type
+
+               v.variable_id = ""
+               v.variable_type = new JavaType
+       end
+end
+
+# Static declaration in the field declarations
+redef class Nstatic_declaration
+       redef fun accept_visitor(v)
+       do
+               v.declaration_type = "static"
+               super
+               v.declaration_type = null
+       end
+end
+
+# Identifier of the field
+redef class Nvariable_id
+       redef fun accept_visitor(v)
+       do
+               v.declaration_element = "id"
+               super
+               v.declaration_element = null
+       end
+end
+
+# Identifier of the method
+redef class Nmethod_id
+       redef fun accept_visitor(v)
+       do
+               v.declaration_element = "id"
+               super
+               v.declaration_element = null
+       end
+end
+
+redef class Ntype
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "variable" and v.declaration_element != "id" then
+                       v.declaration_element = "type"
+               end
+
+               if v.declaration_type == "method" and v.declaration_element == null then
+                       v.declaration_element = "return_type"
+               end
+
+               super
+
+               if v.declaration_element == "variable" then
+                       v.declaration_element = null
+               end
+       end
+end
+
+redef class Ngeneric_param
+       redef fun accept_visitor(v)
+       do
+               # Ignore the weird generic return type declaration
+               if v.declaration_type == "method" then
+                       if v.declaration_element == null then
+                               v.declaration_element = "ignore"
+                       else
+                               v.is_generic_param = true
+                               v.gen_params_index = 0
+
+                               if v.declaration_element == "return_type" then
+                                       v.method_return_type.generic_params = new Array[JavaType]
+                               else if v.declaration_element == "parameter_list" then
+                                       v.method_params[v.param_index].generic_params = new Array[JavaType]
+                               end
+                       end
+               else if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" then
+                               v.is_generic_param = true
+                               v.gen_params_index = 0
+                               v.variable_type.generic_params = new Array[JavaType]
+                       end
+               end
+
+               super
+
+               v.declaration_element = null
+               v.is_generic_param = false
+       end
+end
+
+redef class Nparameter_list
+       redef fun accept_visitor(v)
+       do
+               v.declaration_element = "parameter_list"
+               v.param_index = 0
+               super
+               v.declaration_element = null
+               v.param_index = 0
+       end
+end
+
+redef class Nparameter
+       redef fun accept_visitor(v)
+       do
+               if v.declaration_type == "method" then
+                       if v.declaration_element == "parameter_list" then
+                               if v.is_generic_param then
+                                       v.method_params[v.param_index].generic_params.add(new JavaType)
+
+                                       super
+
+                                       v.gen_params_index += 1
+                               else
+                                       v.method_params.add(new JavaType)
+
+                                       super
+
+                                       v.param_index += 1
+                               end
+                       else if v.declaration_element == "return_type" and v.is_generic_param then
+
+                               v.method_return_type.generic_params.add(new JavaType)
+
+                               super
+
+                               v.gen_params_index += 1
+                       end
+               else if v.declaration_type == "variable" then
+                       if v.declaration_element == "type" and v.is_generic_param then
+                               v.variable_type.generic_params.add(new JavaType)
+
+                               super
+
+                               v.gen_params_index += 1
+                       end
+               else
+                       super
+               end
+       end
+end
+
+var p = new TestParser_javap
+var tree = p.main
+
+var visitor = new JavaVisitor
+visitor.enter_visit(tree)
+
+var generator = new CodeGenerator("bundle.nit", visitor.java_class)
+generator.generate
diff --git a/contrib/jwrapper/src/jtype_converter.nit b/contrib/jwrapper/src/jtype_converter.nit
new file mode 100644 (file)
index 0000000..95adbb4
--- /dev/null
@@ -0,0 +1,99 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to convert java type to nit type and get casts if needed
+module jtype_converter
+
+class JavaTypeConverter
+
+       var type_map = new HashMap[String, String]
+       var param_cast_map = new HashMap[String, String]
+       var return_cast_map = new HashMap[String, String]
+
+       init
+       do
+               # Java type to nit type
+               type_map["byte"] = "Int"
+               type_map["Byte"] = "Int"
+               type_map["short"] = "Int"
+               type_map["Short"] = "Int"
+               type_map["int"] = "Int"
+               type_map["Integer"] = "Int"
+               type_map["long"] = "Int"
+               type_map["Long"] = "Int"
+               type_map["char"] = "Char"
+               type_map["Character"] = "Char"
+               type_map["float"] = "Float"
+               type_map["Float"] = "Float"
+               type_map["double"] = "Float"
+               type_map["Double"] = "Float"
+               type_map["boolean"] = "Bool"
+               type_map["Boolean"] = "Bool"
+               type_map["Object"] = "JavaObject"
+               type_map["Bundle"] = "NativeBundle"
+               type_map["String"] = "JavaString"
+               type_map["CharSequence"] = "JavaString"
+
+               # Collections
+               type_map["List"] = "Array"
+               type_map["ArrayList"] = "Array"
+               type_map["LinkedList"] = "List"
+               type_map["Vector"] = "Array"
+       
+               type_map["Set"] = "HashSet"
+               type_map["SortedSet"] = "Still have to make my mind on this one"
+               type_map["HashSet"] = "HashSet"
+               type_map["TreeSet"] = "HashSet"
+               type_map["LinkedHashSet"] = "HashSet"
+               type_map["Map"] = "HashMap"
+               type_map["SortedMap"] = "RBTreeMap"
+               type_map["HashMap"] = "HashMap"
+               type_map["TreeMap"] = "RBTreeMap"
+               type_map["Hashtable"] = "HashMap"
+               type_map["LinkedHashMap"] = "HashMap"
+
+               # Cast if the type is given as a parameter
+               param_cast_map["byte"] = "(byte)"
+               param_cast_map["Byte"] = "(Byte)"
+               param_cast_map["short"] = "(short)"
+               param_cast_map["Short"] = "(short)"
+               param_cast_map["float"] = "(float)"
+               param_cast_map["Float"] = "(float)"
+               # FIXME: Uncomment as soon as Nit `Int` will be equivalent to Java `long`
+               # param_cast_map["int"] = "int"
+               # param_cast_map["Integer"] = "int"
+
+               # Cast if the type is given as a return value
+               return_cast_map["CharSequence"] = "(String)"
+               # FIXME: Erase as soon as the Nit `Int` type will become a Java `long`
+               return_cast_map["long"] = "(int)"
+       end
+
+       fun to_nit_type(java_type: String): nullable String
+       do
+               return self.type_map.get_or_null(java_type)
+       end
+
+       fun cast_as_param(java_type: String): String
+       do
+               return self.param_cast_map.get_or_default(java_type, "")
+       end
+       
+       fun cast_as_return(java_type: String): String
+       do
+               return self.return_cast_map.get_or_default(java_type, "")
+       end
+end
diff --git a/contrib/jwrapper/src/types.nit b/contrib/jwrapper/src/types.nit
new file mode 100644 (file)
index 0000000..f953d25
--- /dev/null
@@ -0,0 +1,216 @@
+# This file is part of NIT (http://www.nitlanguage.org).
+#
+# Copyright 2014 Frédéric Vachon <fredvac@gmail.com>
+#
+# 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.
+
+# Contains the java and nit type representation used to convert java to nit code
+module types
+
+import jtype_converter
+
+class JavaType
+       private var converter = new JavaTypeConverter
+       var identifier: Array[String] = new Array[String]
+       var generic_params: nullable Array[JavaType] = null
+       var is_void = false
+       var array_dimension = 0
+
+       fun collections_list: Array[String] is cached do return ["List", "ArrayList", "LinkedList", "Vector", "Set", "SortedSet", "HashSet", "TreeSet", "LinkedHashSet", "Map", "SortedMap", "HashMap", "TreeMap", "Hashtable", "LinkedHashMap"]
+       fun iterable: Array[String] is cached do return ["ArrayList", "Set", "HashSet", "LinkedHashSet", "LinkedList", "Stack", "TreeSet", "Vector"]
+       fun maps: Array[String] is cached do return ["Map", "SortedMap", "HashMap", "TreeMap", "Hashtable", "LinkedHashMap"]
+       fun has_generic_params: Bool do return not generic_params == null
+       fun is_primitive_array: Bool do return array_dimension > 0
+       fun full_id: String do return identifier.join(".")
+       fun id: String do return identifier.last
+
+       fun return_cast: String
+       do
+               if self.has_generic_params then
+                       return converter.cast_as_return(self.generic_params[0].id)
+               end
+
+               return converter.cast_as_return(self.id)
+       end
+
+       fun param_cast: String
+       do
+               if self.has_generic_params then
+                       return converter.cast_as_param(self.generic_params[0].id)
+               end
+
+               return converter.cast_as_param(self.id)
+       end
+
+       fun to_nit_type: NitType
+       do
+               var nit_type: NitType
+
+               if self.is_primitive_array then
+                       return self.convert_primitive_array
+               end
+
+               var type_id = converter.to_nit_type(self.id)
+
+               if type_id == null then
+                       nit_type = new NitType(self.full_id)
+                       nit_type.is_complete = false
+               else
+                       nit_type = new NitType(type_id)
+               end
+
+               if not self.has_generic_params then return nit_type
+               
+               nit_type.generic_params = new Array[NitType]
+
+               for param in generic_params do
+                       var nit_param = param.to_nit_type
+
+                       nit_type.generic_params.add(nit_param)
+
+                       if not nit_param.is_complete then nit_type.is_complete = false
+               end
+
+               return nit_type
+       end
+
+       fun convert_primitive_array: NitType
+       do
+               var nit_type = new NitType("Array")
+
+               var last_nit_type = nit_type
+
+               for i in [1..array_dimension] do
+                       var temp: NitType
+                       last_nit_type.generic_params = new Array[NitType]
+
+                       if i == array_dimension then
+                               var temp_type = converter.to_nit_type(self.id)
+
+                               if temp_type == null then 
+                                       temp_type = self.full_id
+                                       nit_type.is_complete = false
+                               end
+
+                               temp = new NitType(temp_type)
+                       else
+                               temp = new NitType("Array")
+                       end
+
+                       last_nit_type.generic_params.add(temp)
+
+                       last_nit_type = temp
+               end
+               
+               return nit_type
+       end
+
+       fun is_iterable: Bool do return iterable.has(self.id)
+
+       fun is_collection: Bool do return is_primitive_array or collections_list.has(self.id)
+
+       fun is_map: Bool do return maps.has(self.id)
+
+       redef fun to_s: String
+       do
+               var id = self.full_id
+
+               if self.is_primitive_array then
+                       for i in [0..array_dimension[ do
+                               id += "[]"
+                       end
+               else if self.has_generic_params then 
+                       var gen_list = new Array[String]
+
+                       for param in generic_params do
+                               gen_list.add(param.to_s)
+                       end
+
+                       id += "<{gen_list.join(", ")}>"
+               end
+
+               return id
+       end
+
+       fun to_cast(jtype: String, is_param: Bool): String
+       do
+               if is_param then
+                       return converter.cast_as_param(jtype)
+               end
+
+               return converter.cast_as_return(jtype)
+       end
+end
+
+class NitType
+       var identifier: String
+       var arg_id: String
+       var generic_params: nullable Array[NitType] = null
+
+       # Returns `true` if all types have been successfully converted to Nit type
+       var is_complete: Bool = true
+
+       fun has_generic_params: Bool do return not generic_params == null
+       fun maps: Array[String] is cached do return ["HashMap", "RBTreeMap"]
+
+       fun id: String do return identifier
+
+       init (id: String)
+       do
+               self.identifier = id
+       end
+
+       fun is_map: Bool do return maps.has(self.identifier)
+
+       redef fun to_s: String
+       do
+               var id = self.identifier
+
+               if self.has_generic_params then 
+                       var gen_list = new Array[String]
+
+                       for param in generic_params do
+                               gen_list.add(param.to_s)
+                       end
+
+                       id += "[{gen_list.join(", ")}]"
+               end
+
+               return id
+       end
+end
+
+class JavaClass
+       var name = new Array[String]
+       var attributes = new HashMap[String, JavaType]
+       var methods = new HashMap[String, Array[JReturnAndParams]]
+
+       fun add_method(id: String, return_type: JavaType, params: Array[JavaType])
+       do
+               var ret_and_params = methods.get_or_default(id, new Array[JReturnAndParams])
+               
+               ret_and_params.add(new JReturnAndParams(return_type, new Array[JavaType].from(params)))
+               methods[id] = ret_and_params
+       end
+end
+
+class JReturnAndParams
+       var return_type: JavaType
+       var params: Array[JavaType]
+
+       init(return_type: JavaType, params: Array[JavaType])
+       do
+               self.return_type = return_type
+               self.params = params
+       end
+end
diff --git a/doc/Makefile b/doc/Makefile
deleted file mode 100644 (file)
index c37eaa8..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-all:
-       ./git-gen-version.sh
-       rubber --pdf --inplace nitreference/nitreference.tex
-       rubber --pdf --inplace developpez/nit.tex
-
-clean:
-       rm nit_version.sty 2> /dev/null || true
-       rubber --clean --inplace nitreference/nitreference.tex
-       rubber --clean --inplace developpez/nit.tex
-
-distclean: clean
-       rm nitreference/nitreference.pdf developpez/nit.pdf 2> /dev/null || true
diff --git a/doc/git-gen-version.sh b/doc/git-gen-version.sh
deleted file mode 100755 (executable)
index 145f2f7..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-#!/bin/sh
-
-# This file is part of NIT ( http://www.nitlanguage.org ).
-#
-# Copyright 2008 Jean Privat <jean@pryen.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.
-
-# This program is used to generate version number from git refs.
-# The version number is stored in a dedicated Nit module.
-
-gen_version() {
-       if ! grep "$*" nit_version.sty >/dev/null 2>&1; then
-               cat > nit_version.sty<<END
-% This file was generated by git-gen-version.sh
-\\newcommand\\nitversion{$*}
-END
-               echo "Version $*"
-       fi
-}
-
-VN=$(git describe --always HEAD 2>/dev/null)
-if [ "$?" != "0" ]; then
-       if [ -r ../VERSION ]; then
-               VN="$(cat ../VERSION)"
-       else
-               echo >&2 "Error: no VERSION file and not a .git repository."
-               exit 1
-       fi
-fi
-if [ -z "$VN" ]; then
-       VN="undefined"
-fi
-if [ -n "$1" ]; then
-       VN="${VN}-$1"
-fi
-if [ -n "$(git diff HEAD 2>/dev/null)" ]; then
-       VN="${VN}-dirty"
-fi
-
-gen_version "$VN"
index 9b7ad1c..6b3f0f3 100644 (file)
@@ -249,13 +249,12 @@ class Neo4jClient
        fun nodes_with_label(lbl: String): Array[NeoNode] do
                var res = get("{base_url}/db/data/label/{lbl}/nodes")
                var nodes = new Array[NeoNode]
-               var batch = new NeoBatch(self)
-               for obj in res.as(JsonArray) do
-                       var node = new NeoNode.from_json(self, obj.as(JsonObject))
-                       batch.load_node(node)
+               for json in res.as(JsonArray) do
+                       var obj = json.as(JsonObject)
+                       var node = load_node(obj["self"].to_s)
+                       node.internal_properties = obj["data"].as(JsonObject)
                        nodes.add node
                end
-               batch.execute
                return nodes
        end
 
@@ -690,7 +689,7 @@ class NeoEdge
        fun from: NeoNode do return internal_from or else load_from
 
        private fun load_from: NeoNode do
-               var node = new NeoNode.from_neo(neo, internal_from_url.to_s)
+               var node = neo.load_node(internal_from_url.to_s)
                internal_from = node
                return node
        end
@@ -699,7 +698,7 @@ class NeoEdge
        fun to: NeoNode do return internal_to or else load_to
 
        private fun load_to: NeoNode do
-               var node = new NeoNode.from_neo(neo, internal_to_url.to_s)
+               var node = neo.load_node(internal_to_url.to_s)
                internal_to = node
                return node
        end
@@ -768,13 +767,6 @@ class NeoBatch
 
        # Load a node in batch mode also load labels, data and edges
        fun load_node(node: NeoNode) do
-               load_node_data(node)
-               load_node_labels(node)
-               load_node_out_edges(node)
-       end
-
-       # Load data into node
-       private fun load_node_data(node: NeoNode) do
                var job = new_job(node)
                job.action = load_node_data_action
                job.method = "GET"
@@ -783,11 +775,7 @@ class NeoBatch
                else
                        job.to = "\{{node.batch_id.to_s}\}"
                end
-       end
-
-       # Load labels into node
-       private fun load_node_labels(node: NeoNode) do
-               var job = new_job(node)
+               job = new_job(node)
                job.action = load_node_labels_action
                job.method = "GET"
                if node.id != null then
@@ -797,9 +785,17 @@ class NeoBatch
                end
        end
 
-       # Load out edges into node
-       private fun load_node_out_edges(node: NeoNode) do
+       # Load in and out edges into node
+       fun load_node_edges(node: NeoNode) do
                var job = new_job(node)
+               job.action = load_node_in_edges_action
+               job.method = "GET"
+               if node.id != null then
+                       job.to = "/node/{node.id.to_s}/relationships/in"
+               else
+                       job.to = "\{{node.batch_id.to_s}\}/relationships/in"
+               end
+               job = new_job(node)
                job.action = load_node_out_edges_action
                job.method = "GET"
                if node.id != null then
@@ -825,7 +821,7 @@ class NeoBatch
                job.to = "\{{node.batch_id.to_s}\}/labels"
                job.body = new JsonArray.from(node.labels)
                # add edges
-               save_edges(node.out_edges)
+               #save_edges(node.out_edges)
        end
 
        # Create multiple nodes
@@ -898,12 +894,19 @@ class NeoBatch
                                var labels = new Array[String]
                                for l in res["body"].as(JsonArray) do labels.add l.to_s
                                node.internal_labels = labels
+                       else if job.action == load_node_in_edges_action then
+                               var node = job.entity.as(NeoNode)
+                               var edges = res["body"].as(JsonArray)
+                               node.internal_in_edges = new List[NeoEdge]
+                               for edge in edges do
+                                       node.internal_in_edges.add client.load_edge(edge.as(JsonObject)["self"].to_s)
+                               end
                        else if job.action == load_node_out_edges_action then
                                var node = job.entity.as(NeoNode)
                                var edges = res["body"].as(JsonArray)
                                node.internal_out_edges = new List[NeoEdge]
                                for edge in edges do
-                                       node.internal_out_edges.add new NeoEdge.from_json(client, edge.as(JsonObject))
+                                       node.internal_out_edges.add client.load_edge(edge.as(JsonObject)["self"].to_s)
                                end
                        end
                end
@@ -917,7 +920,8 @@ class NeoBatch
        private fun create_edge_action: Int do return 2
        private fun load_node_data_action: Int do return 3
        private fun load_node_labels_action: Int do return 4
-       private fun load_node_out_edges_action: Int do return 5
+       private fun load_node_in_edges_action: Int do return 5
+       private fun load_node_out_edges_action: Int do return 6
 end
 
 # A job that can be executed in a `NeoBatch`
similarity index 79%
rename from lib/sqlite3.nit
rename to lib/sqlite3/native_sqlite3.nit
index 6fe0828..da93820 100644 (file)
 # limitations under the License.
 
 # Low-level Sqlite3 features
-module sqlite3 is pkgconfig("sqlite3")
+module native_sqlite3 is pkgconfig("sqlite3")
 
 in "C header" `{
        #include <sqlite3.h>
 `}
 
+redef class Sys
+       # Last error raised when calling `Sqlite3::open`
+       var sqlite_open_error: nullable Sqlite3Code = null
+end
+
 extern class Sqlite3Code `{int`}
        new ok `{ return SQLITE_OK; `} #         0   /* Successful result */
        fun is_ok: Bool `{ return recv == SQLITE_OK; `}
@@ -73,7 +78,7 @@ extern class Sqlite3Code `{int`}
 end
 
 # A prepared statement
-extern class Statement `{sqlite3_stmt*`}
+extern class NativeStatement `{sqlite3_stmt*`}
 
        # Evaluate the statement
        fun step: Sqlite3Code `{
@@ -89,6 +94,7 @@ extern class Statement `{sqlite3_stmt*`}
                return NativeString_to_s(ret);
        `}
 
+       # Number of bytes in the blob or string at row `i`
        fun column_bytes(i: Int) : Int `{
                return sqlite3_column_bytes(recv, i);
        `}
@@ -101,15 +107,12 @@ extern class Statement `{sqlite3_stmt*`}
                return sqlite3_column_int(recv, i);
        `}
 
-       fun column_text(i: Int) : String import NativeString.to_s `{
-               char * ret = (char *) sqlite3_column_text(recv, i);
-               if( ret == NULL ){
-                       ret = "";
-               }
-               return NativeString_to_s(ret);
+       fun column_text(i: Int): NativeString `{
+               return (char *)sqlite3_column_text(recv, i);
        `}
 
-       fun column_type(i: Int) : Int `{
+       # Type of the entry at row `i`
+       fun column_type(i: Int): DataType `{
                return sqlite3_column_type(recv, i);
        `}
 
@@ -127,15 +130,21 @@ extern class Statement `{sqlite3_stmt*`}
 end
 
 # A database connection
-extern class Sqlite3 `{sqlite3 *`}
+extern class NativeSqlite3 `{sqlite3 *`}
 
        # Open a connection to a database in UTF-8
-       new open(filename: String) import String.to_cstring `{
+       new open(filename: String) import String.to_cstring, set_sys_sqlite_open_error `{
                sqlite3 *self = NULL;
-               sqlite3_open(String_to_cstring(filename), &self);
+               int err = sqlite3_open(String_to_cstring(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.
                return self;
        `}
 
+       # Utility method to set `Sys.sqlite_open_error`
+       private fun set_sys_sqlite_open_error(err: Sqlite3Code) do sys.sqlite_open_error = err
+
        # Has this DB been correctly opened?
        #
        # To know if it has been closed or interrupted, you must check for errors with `error`.
@@ -152,13 +161,13 @@ extern class Sqlite3 `{sqlite3 *`}
        `}
 
        # Prepare a SQL statement
-       fun prepare(sql: String): nullable Statement import String.to_cstring, Statement.as nullable `{
+       fun prepare(sql: String): nullable NativeStatement import String.to_cstring, NativeStatement.as nullable `{
                sqlite3_stmt *stmt;
                int res = sqlite3_prepare_v2(recv, String_to_cstring(sql), -1, &stmt, 0);
                if (res == SQLITE_OK)
-                       return Statement_as_nullable(stmt);
+                       return NativeStatement_as_nullable(stmt);
                else
-                       return null_Statement();
+                       return null_NativeStatement();
        `}
 
        fun last_insert_rowid: Int `{
@@ -169,3 +178,14 @@ extern class Sqlite3 `{sqlite3 *`}
                return sqlite3_errcode(recv);
        `}
 end
+
+# Sqlite data types
+extern class DataType `{ int `}
+       fun is_integer: Bool `{ return recv == SQLITE_INTEGER; `}
+       fun is_float: Bool `{ return recv == SQLITE_FLOAT; `}
+       fun is_blob: Bool `{ return recv == SQLITE_BLOB; `}
+       fun is_null: Bool `{ return recv == SQLITE_NULL; `}
+       fun is_text: Bool `{ return recv == SQLITE_TEXT; `}
+
+       fun to_i: Int `{ return recv; `}
+end
diff --git a/lib/sqlite3/sqlite3.nit b/lib/sqlite3/sqlite3.nit
new file mode 100644 (file)
index 0000000..d194362
--- /dev/null
@@ -0,0 +1,307 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 201 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Services to manipulate a Sqlite3 database
+#
+# For more information, refer to the documentation of http://www.sqlite.org/docs.html
+module sqlite3
+
+private import native_sqlite3
+import standard
+
+# A connection to a Sqlite3 database
+class Sqlite3DB
+       private var native_connection: NativeSqlite3
+
+       # Is this connection to the DB open?
+       var is_open = false
+
+       # All `Statement` opened from this connection that must be closed with this connection
+       private var open_statements = new Array[Statement]
+
+       # Open a connection to the database file at `path`
+       init open(path: Text)
+       do
+               native_connection = new NativeSqlite3.open(path.to_s)
+               if native_connection.is_valid then is_open = true
+       end
+
+       # Close this connection to the DB and all open statements
+       fun close
+       do
+               is_open = false
+
+               # close open statements
+               for stmt in open_statements do if stmt.is_open then
+                       stmt.close
+               end
+
+               native_connection.close
+       end
+
+       # Prepare and return a `Statement`, return `null` on error
+       fun prepare(sql: Text): nullable Statement
+       do
+               var native_stmt = native_connection.prepare(sql.to_s)
+               if native_stmt == null then return null
+
+               var stmt = new Statement(native_stmt)
+               open_statements.add stmt
+               return stmt
+       end
+
+       # Execute the `sql` statement and return `true` on success
+       fun execute(sql: Text): Bool
+       do
+               var err = native_connection.exec(sql.to_s)
+               return err.is_ok
+       end
+
+       # Create a table on the DB with a statement beginning with "CREATE TABLE ", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun create_table(rest: Text): Bool do return execute("CREATE TABLE " + rest)
+
+       # Insert in the DB with a statement beginning with "INSERT ", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun insert(rest: Text): Bool do return execute("INSERT " + rest)
+
+       # Replace in the DB with a statement beginning with "REPLACE", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun replace(rest: Text): Bool do return execute("REPLACE " + rest)
+
+       # Select from the DB with a statement beginning with "SELECT ", followed by `rest`
+       #
+       # This method does not escape special characters.
+       fun select(rest: Text): nullable Statement do return prepare("SELECT " + rest)
+
+       # TODO add more prefix here as needed
+
+       # The latest error message, or `null` if there is none
+       fun error: nullable String
+       do
+               if not native_connection.is_valid then
+                       var err = sys.sqlite_open_error
+                       if err == null then return null
+                       return err.to_s
+               end
+
+               var err = native_connection.error
+               if err.is_ok then return null
+               return err.to_s
+       end
+end
+
+# A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select`
+class Statement
+       private var native_statement: NativeStatement
+
+       private init(ns: NativeStatement) do self.native_statement = ns
+
+       # Is this statement usable?
+       var is_open = true
+
+       # Close and finalize this statement
+       fun close
+       do
+               is_open = false
+               native_statement.finalize
+       end
+
+       # Reset this statement and return a `StatementIterator` to iterate over the result
+       fun iterator: StatementIterator
+       do
+               native_statement.reset
+               native_statement.step
+               return new StatementIterator(self)
+       end
+end
+
+class StatementRow
+       # Statement linked to `self`
+       var statement: Statement
+
+       private init(s: Statement) do self.statement = s
+
+       # Number of entries in this row
+       #
+       # require: `self.statement.is_open`
+       fun length: Int
+       do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_count
+       end
+
+       # Returns the `i`th entry on this row
+       fun [](i: Int): StatementEntry do return new StatementEntry(statement, i)
+end
+
+# An entry on a `StatementRow`
+class StatementEntry
+       # Statement linked to `self`
+       var statement: Statement
+
+       private var index: Int
+
+       private init(s: Statement, i: Int)
+       do
+               self.statement = s
+               self.index = i
+       end
+
+       # Name of the column
+       #
+       # require: `self.statement.is_open`
+       fun name: String is cached do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_name(index)
+       end
+
+       # Get the value of this entry according to its Sqlite type
+       #
+       # require: `self.statement.is_open`
+       fun value: nullable Sqlite3Data
+       do
+               assert statement_closed: statement.is_open
+
+               var data_type = statement.native_statement.column_type(index)
+               if data_type.is_integer then return to_i
+               if data_type.is_float then return to_f
+               if data_type.is_blob then return to_blob
+               if data_type.is_null then return null
+               if data_type.is_text then return to_s
+               abort
+       end
+
+       # Get this entry as `Int`
+       #
+       # If the Sqlite type of this entry is not an integer, it will be `CAST` to
+       # integer. If `null`, returns 0.
+       #
+       # require: `self.statement.is_open`
+       fun to_i: Int
+       do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_int(index)
+       end
+
+       # Get this entry as `Float`
+       #
+       # If the Sqlite type of this entry is not a floating point, it will be `CAST`
+       # to float. If `null`, returns 0.0.
+       #
+       # require: `self.statement.is_open`
+       fun to_f: Float
+       do
+               assert statement_closed: statement.is_open
+
+               return statement.native_statement.column_double(index)
+       end
+
+       # Get this entry as `String`
+       #
+       # If the Sqlite type of this entry is not text, it will be `CAST` to text.
+       # If null, returns an empty string.
+       #
+       # require: `self.statement.is_open`
+       redef fun to_s
+       do
+               assert statement_closed: statement.is_open
+
+               var native_string = statement.native_statement.column_text(index)
+               if native_string.address_is_null then return ""
+               return native_string.to_s
+       end
+
+       # Get this entry as `Blob`
+       #
+       # If the Sqlite type of this entry is not a blob, it will be `CAST` to text.
+       # If null, returns a NULL pointer.
+       #
+       # require: `self.statement.is_open`
+       fun to_blob: Blob
+       do
+               assert statement_closed: statement.is_open
+
+               # By spec, we must get the pointer before the byte count
+               var pointer = statement.native_statement.column_blob(index)
+               var length = statement.native_statement.column_bytes(index)
+
+               return new Blob(pointer, length)
+       end
+end
+
+# Iterator over the rows of a statement result
+class StatementIterator
+       super Iterator[StatementRow]
+
+       # Statement linked to `self`
+       var statement: Statement
+
+       private init(s: Statement)
+       do
+               self.statement = s
+               self.item = new StatementRow(s)
+       end
+
+       redef var item: StatementRow
+
+       redef var is_ok = true
+
+       # require: `self.statement.is_open`
+       redef fun next
+       do
+               assert statement_closed: statement.is_open
+
+               var err = statement.native_statement.step
+               if err.is_row then
+                       is_ok = true
+               else if err.is_done then
+                       # Clean complete
+                       is_ok = false
+               else
+                       # error
+                       # FIXME do something with the error?
+                       is_ok = false
+               end
+       end
+end
+
+# A data type supported by Sqlite3
+interface Sqlite3Data end
+
+redef universal Int super Sqlite3Data end
+redef universal Float super Sqlite3Data end
+redef class String super Sqlite3Data end
+
+# A Sqlite3 blob
+class Blob
+       super Sqlite3Data
+
+       private init(pointer: Pointer, length: Int)
+       do
+               self.pointer = pointer
+               self.length = length
+       end
+
+       var pointer: Pointer
+       var length: Int
+end
index c4ddcb0..8015a5a 100644 (file)
@@ -578,7 +578,40 @@ abstract class AbstractCompiler
        protected fun compile_header_structs is abstract
 
        # Declaration of structures for nitni undelying the FFI
-       protected fun compile_nitni_structs is abstract
+       protected fun compile_nitni_structs
+       do
+               self.header.add_decl """
+/* Native reference to Nit objects */
+/* This structure is used to represent every Nit type in extern methods and custom C code. */
+struct nitni_ref {
+       struct nitni_ref *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+};
+
+/* List of global references from C code to Nit objects */
+/* Instanciated empty at init of Nit system and filled explicitly by user in C code */
+struct nitni_global_ref_list_t {
+       struct nitni_ref *head, *tail;
+};
+extern struct nitni_global_ref_list_t *nitni_global_ref_list;
+
+/* Initializer of global reference list */
+extern void nitni_global_ref_list_init();
+
+/* Intern function to add a global reference to the list */
+extern void nitni_global_ref_add( struct nitni_ref *ref );
+
+/* Intern function to remove a global reference from the list */
+extern void nitni_global_ref_remove( struct nitni_ref *ref );
+
+/* Increase count on an existing global reference */
+extern void nitni_global_ref_incr( struct nitni_ref *ref );
+
+/* Decrease count on an existing global reference */
+extern void nitni_global_ref_decr( struct nitni_ref *ref );
+"""
+       end
 
        # Generate the main C function.
        # This function:
@@ -688,6 +721,9 @@ abstract class AbstractCompiler
 
                v.add("glob_argc = argc; glob_argv = argv;")
                v.add("initialize_gc_option();")
+
+               v.add "initialize_nitni_global_refs();"
+
                var main_type = mainmodule.sys_type
                if main_type != null then
                        var mainmodule = v.compiler.mainmodule
@@ -747,6 +783,67 @@ abstract class AbstractCompiler
                v.add("\}")
        end
 
+       # Copile all C functions related to the [incr|decr]_ref features of the FFI
+       fun compile_nitni_global_ref_functions
+       do
+               var v = self.new_visitor
+               v.add """
+struct nitni_global_ref_list_t *nitni_global_ref_list;
+void initialize_nitni_global_refs() {
+       nitni_global_ref_list = (struct nitni_global_ref_list_t*)nit_alloc(sizeof(struct nitni_global_ref_list_t));
+       nitni_global_ref_list->head = NULL;
+       nitni_global_ref_list->tail = NULL;
+}
+
+void nitni_global_ref_add( struct nitni_ref *ref ) {
+       if ( nitni_global_ref_list->head == NULL ) {
+               nitni_global_ref_list->head = ref;
+               ref->prev = NULL;
+       } else {
+               nitni_global_ref_list->tail->next = ref;
+               ref->prev = nitni_global_ref_list->tail;
+       }
+       nitni_global_ref_list->tail = ref;
+
+       ref->next = NULL;
+}
+
+void nitni_global_ref_remove( struct nitni_ref *ref ) {
+       if ( ref->prev == NULL ) {
+               nitni_global_ref_list->head = ref->next;
+       } else {
+               ref->prev->next = ref->next;
+       }
+
+       if ( ref->next == NULL ) {
+               nitni_global_ref_list->tail = ref->prev;
+       } else {
+               ref->next->prev = ref->prev;
+       }
+}
+
+extern void nitni_global_ref_incr( struct nitni_ref *ref ) {
+       if ( ref->count == 0 ) /* not registered */
+       {
+               /* add to list */
+               nitni_global_ref_add( ref );
+       }
+
+       ref->count ++;
+}
+
+extern void nitni_global_ref_decr( struct nitni_ref *ref ) {
+       if ( ref->count == 1 ) /* was last reference */
+       {
+               /* remove from list */
+               nitni_global_ref_remove( ref );
+       }
+
+       ref->count --;
+}
+"""
+       end
+
        # List of additional files required to compile (FFI)
        var extern_bodies = new Array[ExternFile]
 
index 2183269..cad98bd 100644 (file)
@@ -46,13 +46,13 @@ class AndroidProject
        var manifest_application_lines = new Array[String]
 
        # Minimum API level required for the application to run
-       var min_sdk: nullable Int = null
+       var min_api: nullable Int = null
 
        # Build target API level
-       var target_sdk: nullable Int = null
+       var target_api: nullable Int = null
 
        # Maximum API level on which the application will be allowed to run
-       var max_sdk: nullable Int = null
+       var max_api: nullable Int = null
 
        redef fun to_s do return """
 name: {{{name or else "null"}}}
@@ -75,14 +75,14 @@ redef class ModelBuilder
                annot = priority_annotation_on_modules("java_package", mmodule)
                if annot != null then project.java_package = annot.arg_as_string(self)
 
-               var annots = collect_annotations_on_modules("min_sdk_version", mmodule)
-               for an in annots do project.min_sdk = an.arg_as_int(self)
+               var annots = collect_annotations_on_modules("min_api_version", mmodule)
+               for an in annots do project.min_api = an.arg_as_int(self)
 
-               annots = collect_annotations_on_modules("max_sdk_version", mmodule)
-               for an in annots do project.max_sdk = an.arg_as_int(self)
+               annots = collect_annotations_on_modules("max_api_version", mmodule)
+               for an in annots do project.max_api = an.arg_as_int(self)
 
-               annots = collect_annotations_on_modules("target_sdk_version", mmodule)
-               for an in annots do project.target_sdk = an.arg_as_int(self)
+               annots = collect_annotations_on_modules("target_api_version", mmodule)
+               for an in annots do project.target_api = an.arg_as_int(self)
 
                annots = collect_annotations_on_modules("android_manifest", mmodule)
                for an in annots do project.manifest_lines.add an.arg_as_string(self) or else ""
index 79be326..7de42f4 100644 (file)
@@ -67,14 +67,14 @@ class AndroidToolchain
                var app_version = project.version
                if app_version == null then app_version = "1.0"
 
-               var app_min_sdk = project.min_sdk
-               if app_min_sdk == null then app_min_sdk = 10
+               var app_min_api = project.min_api
+               if app_min_api == null then app_min_api = 10
 
-               var app_target_sdk = project.target_sdk
-               if app_target_sdk == null then app_target_sdk = app_min_sdk
+               var app_target_api = project.target_api
+               if app_target_api == null then app_target_api = app_min_api
 
-               var app_max_sdk = ""
-               if project.max_sdk != null then app_max_sdk = "android:maxSdkVersion=\"{app_max_sdk}\""
+               var app_max_api = ""
+               if project.max_api != null then app_max_api = "android:maxSdkVersion=\"{project.max_api.as(not null)}\""
 
                # Clear the previous android project, so there is no "existing project warning"
                # or conflict between Java files of different projects
@@ -83,7 +83,7 @@ class AndroidToolchain
                var args = ["android", "-s",
                        "create", "project",
                        "--name", short_project_name,
-                       "--target", "android-{app_target_sdk}",
+                       "--target", "android-{app_target_api}",
                        "--path", android_project_root,
                        "--package", app_package,
                        "--activity", short_project_name]
@@ -139,9 +139,9 @@ $(call import-module,android/native_app_glue)
 
     <!-- This is the platform API where NativeActivity was introduced. -->
     <uses-sdk 
-        android:minSdkVersion="{{{app_min_sdk}}}" 
-        android:targetSdkVersion="{{{app_target_sdk}}}" 
-        {{{app_max_sdk}}} /> 
+        android:minSdkVersion="{{{app_min_api}}}" 
+        android:targetSdkVersion="{{{app_target_api}}}" 
+        {{{app_max_api}}} /> 
 
     <application
                android:label="@string/app_name"
index 14ae43f..9a83ba4 100644 (file)
@@ -45,6 +45,10 @@ redef class MModule
                ensure_compile_nitni_base(v)
 
                nitni_ccu.header_c_types.add("#include \"{name}._ffi.h\"\n")
+               nitni_ccu.header_c_types.add """
+extern void nitni_global_ref_incr(void*);
+extern void nitni_global_ref_decr(void*);
+"""
 
                nitni_ccu.write_as_nitni(self, v.compiler.modelbuilder.compile_dir)
 
@@ -178,7 +182,7 @@ redef class AMethPropdef
                                arguments_for_c.add(arg.name)
                        else
                                v.add("struct nitni_instance* var_for_c_{a};")
-                               v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));")
+                               v.add("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
                                v.add("var_for_c_{a}->value = {arg.name};")
                                arguments_for_c.add("var_for_c_{a}")
                        end
@@ -235,7 +239,7 @@ redef class AMethPropdef
                                arguments_for_c.add(arg.name)
                        else
                                v.add("struct nitni_instance* var_for_c_{a};")
-                               v.add("var_for_c_{a} = malloc(sizeof(struct nitni_instance));")
+                               v.add("var_for_c_{a} = nit_alloc(sizeof(struct nitni_instance));")
                                v.add("var_for_c_{a}->value = {arg.name};")
                                arguments_for_c.add("var_for_c_{a}")
                        end
@@ -294,7 +298,7 @@ redef class AbstractCompilerVisitor
                        add("return {src};")
                else
                        add("struct nitni_instance* ret_for_c;")
-                       add("ret_for_c = malloc(sizeof(struct nitni_instance));")
+                       add("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
                        add("ret_for_c->value = {src};")
                        add("return ret_for_c;")
                end
@@ -316,14 +320,17 @@ redef class MType
        private fun compile_extern_helper_functions(v: AbstractCompilerVisitor, ccu: CCompilationUnit)
        do
                # actually, we do not need to do anything when using the bohem garbage collector
+               var call_context = from_c_call_context
 
                # incr_ref
-               var nitni_visitor = v.compiler.new_visitor
-               ccu.header_decl.add("#define {mangled_cname}_incr_ref(from) while(0)\{\}\n")
-
-               # incr_ref
-               nitni_visitor = v.compiler.new_visitor
-               ccu.header_decl.add("#define {mangled_cname}_decr_ref(from) while(0)\{\}\n")
+               ccu.header_decl.add "#ifndef {mangled_cname}_incr_ref\n"
+               ccu.header_decl.add "   #define {mangled_cname}_incr_ref(from) nitni_global_ref_incr(({call_context.name_mtype(self)})(from))\n"
+               ccu.header_decl.add "#endif\n"
+
+               # decr_ref
+               ccu.header_decl.add "#ifndef {mangled_cname}_decr_ref\n"
+               ccu.header_decl.add "   #define {mangled_cname}_decr_ref(from) nitni_global_ref_decr(({call_context.name_mtype(self)})(from))\n"
+               ccu.header_decl.add "#endif\n"
        end
 end
 
@@ -354,7 +361,7 @@ redef class MNullableType
                var full_internal_csignature = "{cname_blind} {full_cname}()"
                nitni_visitor.add("{full_internal_csignature} \{")
                nitni_visitor.add("struct nitni_instance* ret_for_c;")
-               nitni_visitor.add("ret_for_c = malloc(sizeof(struct nitni_instance));")
+               nitni_visitor.add("ret_for_c = nit_alloc(sizeof(struct nitni_instance));")
                nitni_visitor.add("ret_for_c->value = NULL;")
                nitni_visitor.add("return ret_for_c;")
                nitni_visitor.add("\}")
index 9e79281..322fcda 100644 (file)
@@ -77,6 +77,7 @@ redef class ModelBuilder
                end
 
                # The main function of the C
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # Compile until all runtime_functions are visited
@@ -269,7 +270,14 @@ class GlobalCompiler
 
        redef fun compile_nitni_structs
        do
-               self.header.add_decl("struct nitni_instance \{ val *value; \};")
+               self.header.add_decl """
+struct nitni_instance \{
+       struct nitni_instance *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+       val *value;
+\};"""
+               super
        end
 end
 
diff --git a/src/model/README.md b/src/model/README.md
new file mode 100644 (file)
index 0000000..5e5d735
--- /dev/null
@@ -0,0 +1,43 @@
+The meta model of Nit programs
+
+These modules define the entities of the Nit meta-model like modules, classes, types and properties
+They also provide an API to build and query models.
+All model classes starts with the `M` letter (`MModule`, `MClass`, etc.)
+
+The model is used by tools that need a higher view than a AST (see `parser`).
+The model represents What the programmer means.
+
+Because of the specification of the Nit language, the model is complex and sometime difficult to understand.
+
+## POSet
+
+There is a lot of classes and relation in the model.
+Most of there relations are based on posets (from the lib `poset`.)
+
+Posets are *partially-ordered sets*; they are used to modelize hierarchies of things (eg. hierarchies of modules)
+
+The poset is an expressive data structure that generalizes most services about hierarchies.
+This avoid the duplication of code and the over-specialization of services.
+The drawback is that a specific request on the model use an abstract vocabulary.
+
+Example. you want the set of modules directly imported by another module.
+There is no specific method in `MModule` for that, the good way is to use services on the some posets
+
+~~~
+var res = mymodule.in_importation.direct_greaters
+~~~
+
+posets are used in two dual ways :
+
+ - by the whole hierarchy, most are in the `Model` and are named `something_somerelation_hierarchy` (eg. `Model::mmodule_importation_hierarchy`).
+ - by the view on en entity in a specific hierarchy, they are in the `MEntity` subclasses and are name `in_somerelation`. (eg. `MModule::in_importation`).
+
+
+## Refinement
+
+The refinement is the cause of the biggest difficulty with the model since the relations between entities are relative to the module considered.
+
+"How many method in this class?" -- It depends, what modules are you considering?
+"What is called by `x.foo` when `x` is dynamically a `Bar`?" -- It depends, in which main module?
+
+This relativity cause most services on model entities to have an additional parameter for the module considered.
index 2b7e5af..648eb49 100644 (file)
@@ -72,7 +72,7 @@ class MModule
        super MConcern
 
        # The model considered
-       var model: Model
+       redef var model: Model
 
        # placebo for old module nesting hierarchy
        # return null if self is not nested (ie. is a top-level module)
index 7481338..7568e9b 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# Object model of the Nit language
+# Classes, types and properties
 #
-# This module define the entities of the Nit meta-model like modules,
-# classes, types and properties
-#
-# It also provide an API to build and query models.
-#
-# All model classes starts with the M letter (`MModule`, `MClass`, etc.)
-#
-# TODO: better doc
+# All three concepts are defined in this same module because these are strongly connected:
+# * types are based on classes
+# * classes contains properties
+# * some properties are types (virtual types)
 #
 # TODO: liearization, extern stuff
 # FIXME: better handling of the types
@@ -319,9 +315,20 @@ end
 #
 # This characteristic helps the reasoning about classes in a program since a
 # single `MClass` object always denote the same class.
-# However, because a `MClass` is global, it does not really have properties nor
-# belong to a hierarchy since the property and the
-# hierarchy of a class depends of a module.
+#
+# The drawback is that classes (`MClass`) contain almost nothing by themselves.
+# These do not really have properties nor belong to a hierarchy since the property and the
+# hierarchy of a class depends of the refinement in the modules.
+#
+# Most services on classes require the precision of a module, and no one can asks what are
+# the super-classes of a class nor what are properties of a class without precising what is
+# the module considered.
+#
+# For instance, during the typing of a source-file, the module considered is the module of the file.
+# eg. the question *is the method `foo` exists in the class `Bar`?* must be reformulated into
+# *is the method `foo` exists in the class `Bar` in the current module?*
+#
+# During some global analysis, the module considered may be the main module of the program.
 class MClass
        super MEntity
 
@@ -380,6 +387,8 @@ class MClass
                end
        end
 
+       redef fun model do return intro_mmodule.model
+
        # All class definitions (introduction and refinements)
        var mclassdefs: Array[MClassDef] = new Array[MClassDef]
 
@@ -448,7 +457,14 @@ end
 #
 # A `MClassDef` is associated with an explicit (or almost) definition of a
 # class. Unlike `MClass`, a `MClassDef` is a local definition that belong to
-# a specific module
+# a specific class and a specific module, and contains declarations like super-classes
+# or properties.
+#
+# It is the class definitions that are the backbone of most things in the model:
+# ClassDefs are defined with regard with other classdefs.
+# Refinement and specialization are combined to produce a big poset called the `Model::mclassdef_hierarchy`.
+#
+# Moreover, the extension and the intention of types is defined by looking at the MClassDefs.
 class MClassDef
        super MEntity
 
@@ -496,6 +512,8 @@ class MClassDef
        # Actually the name of the `mclass`
        redef fun name do return mclass.name
 
+       redef fun model do return mmodule.model
+
        # All declared super-types
        # FIXME: quite ugly but not better idea yet
        var supertypes: Array[MClassType] = new Array[MClassType]
@@ -587,8 +605,7 @@ end
 abstract class MType
        super MEntity
 
-       # The model of the type
-       fun model: Model is abstract
+       redef fun name do return to_s
 
        # Return true if `self` is an subtype of `sup`.
        # The typing is done using the standard typing policy of Nit.
@@ -769,7 +786,7 @@ abstract class MType
 
        # Replace formals generic types in self with resolved values in `mtype`
        # If `cleanup_virtual` is true, then virtual types are also replaced
-       # with their bounds
+       # with their bounds.
        #
        # This function returns self if `need_anchor` is false.
        #
@@ -830,8 +847,6 @@ abstract class MType
        #
        # The resolution can be done because `E` make sense for the class A (see `can_resolve_for`)
        #
-       # TODO: Explain the cleanup_virtual
-       #
        # FIXME: the parameter `cleanup_virtual` is just a bad idea, but having
        # two function instead of one seems also to be a bad idea.
        #
@@ -1199,8 +1214,8 @@ end
 # It's mean that all refinements of a same class "share" the parameter type,
 # but that a generic subclass has its on parameter types.
 #
-# However, in the sense of the meta-model, the a parameter type of a class is
-# a valid types in a subclass. The "in the sense of the meta-model" is
+# However, in the sense of the meta-model, a parameter type of a class is
+# a valid type in a subclass. The "in the sense of the meta-model" is
 # important because, in the Nit language, the programmer cannot refers
 # directly to the parameter types of the super-classes.
 #
@@ -1496,8 +1511,10 @@ end
 
 # A parameter in a signature
 class MParameter
+       super MEntity
+
        # The name of the parameter
-       var name: String
+       redef var name: String
 
        # The static type of the parameter
        var mtype: MType
@@ -1505,6 +1522,12 @@ class MParameter
        # Is the parameter a vararg?
        var is_vararg: Bool
 
+       init(name: String, mtype: MType, is_vararg: Bool) do
+               self.name = name
+               self.mtype = mtype
+               self.is_vararg = is_vararg
+       end
+
        redef fun to_s
        do
                if is_vararg then
@@ -1521,6 +1544,8 @@ class MParameter
                var res = new MParameter(self.name, newtype, self.is_vararg)
                return res
        end
+
+       redef fun model do return mtype.model
 end
 
 # A service (global property) that generalize method, attribute, etc.
@@ -1581,6 +1606,8 @@ abstract class MProperty
        # associated definition, this method will abort
        fun intro: MPROPDEF do return mpropdefs.first
 
+       redef fun model do return intro.model
+
        # Alias for `name`
        redef fun to_s do return name
 
@@ -1835,6 +1862,8 @@ abstract class MPropDef
        # Actually the name of the `mproperty`
        redef fun name do return mproperty.name
 
+       redef fun model do return mclassdef.model
+
        # Internal name combining the module, the class and the property
        # Example: "mymodule#MyClass#mymethod"
        redef var to_s: String
index 666ce8e..ef17543 100644 (file)
@@ -27,6 +27,9 @@ end
 abstract class MEntity
        # The short (unqualified) name of this model entity
        fun name: String is abstract
+
+       # A Model Entity has a direct link to its model
+       fun model: Model is abstract
 end
 
 # Something that represents a concern
index 7bd502e..0b8010e 100644 (file)
@@ -27,7 +27,7 @@ class MProject
        redef var name: String
 
        # The model of the project
-       var model: Model
+       redef var model: Model
 
        # The root of the group tree
        var root: nullable MGroup writable = null
@@ -95,6 +95,8 @@ class MGroup
                end
        end
 
+       redef fun model do return mproject.model
+
        redef fun parent_concern do
                if not is_root then return parent
                return mproject
diff --git a/src/parser/README b/src/parser/README
deleted file mode 100644 (file)
index 981578a..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-This directory contains the nit parser. It is generated from a grammar for sablecc3 ( http://www.sablecc.org ).
-In order to generate nit parser, you need the alternate SableCC3 generator ( http://www.mare.ee/indrek/sablecc/ ).
-
-Contents:
-
-       fact_parser.pl: Script used to factorize parser.nit
-       Makefile: Update grammar and generate .nit files
-       nit.sablecc3xx: Extended sablecc3 grammar (see prescc.pl)
-       prescc.pl: Program to transform an extended sablecc3 to a standard one
-       parser_nodes.nit: token and nodes classes hierarchy used by the parser and the lexer
-       tables.nit, tables_nit.h: Interfaces to access the tables needed by the parser and the lexer
-       test_parser.nit:
-       xss/*.xss: alternate SableCC3 template files for the Nit language
-
-
-The following are generated but present to avoid the need of sablecc3:
-
-       lexer.nit: generated lexer
-       parser.nit: generated parser
-       parser_prod.nit: All production with generated visit methods
-       tables_nit.c: The tables needed by the parser and the lexer
-       parser_abs.nit: Raw generated token and nodes classes used to maintain coherence of parser_nodes.nit
-
-
-Other temp files produced by the Makefile:
-
-       .nit.sablecc3: Sablecc3 grammar after processing
-       .nit.sablecc3.dump: Dump of the grammar to improve sablecc3 multiple runs
-       .parser-nofact.nit: The parser generated by SableCC3 before factorization by fact_parser.pl
-
diff --git a/src/parser/README.md b/src/parser/README.md
new file mode 100644 (file)
index 0000000..3595aa7
--- /dev/null
@@ -0,0 +1,42 @@
+Parser and AST for the Nit language
+
+The parser ans the AST are mostly used by all tools.
+
+The `parser` is the tool that transform source-files into abstract syntax trees (AST) (see `parser_nodes`)
+While the AST is a higher abstraction than blob of text, the AST is still limited and represents only *What the programmer says*.
+
+Classes of nodes of the AST starts with the letter `A` (for most things, eg. `AClassdef`) or `T` (for token eg. `TId`), there is no real reason except historical that might be solved with a new parser.
+
+Variable names of the AST usually starts with `n` (for node). This is also historical but some names with a `a` (to mimic the class name) remains.
+
+## SableCC
+
+Most files in this directory are generated from a grammar for sablecc3 ( http://www.sablecc.org ).
+In order to generate nit parser, you need the alternate SableCC3 generator ( http://www.mare.ee/indrek/sablecc/ ).
+
+## Contents
+
+* fact_parser.pl: Script used to factorize parser.nit
+* Makefile: Update grammar and generate .nit files
+* nit.sablecc3xx: Extended sablecc3 grammar (see prescc.pl)
+* prescc.pl: Program to transform an extended sablecc3 to a standard one
+* parser_nodes.nit: token and nodes classes hierarchy used by the parser and the lexer
+* tables.nit, tables_nit.h: Interfaces to access the tables needed by the parser and the lexer
+* xss/*.xss: alternate SableCC3 template files for the Nit language
+
+
+The following are generated but present to avoid the need of sablecc3:
+
+* lexer.nit: generated lexer
+* parser.nit: generated parser
+* parser_prod.nit: All production with generated visit methods
+* tables_nit.c: The tables needed by the parser and the lexer
+* parser_abs.nit: Raw generated token and nodes classes used to maintain coherence of parser_nodes.nit
+
+
+Other temp files produced by the Makefile:
+
+* .nit.sablecc3: Sablecc3 grammar after processing
+* .nit.sablecc3.dump: Dump of the grammar to improve sablecc3 multiple runs
+* .parser-nofact.nit: The parser generated by SableCC3 before factorization by fact_parser.pl
+
index 8d1b008..bf3cb12 100644 (file)
@@ -105,6 +105,7 @@ redef class ModelBuilder
 
                # The main function of the C
                compiler.new_file("{mainmodule.name}.main")
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # compile methods
@@ -870,9 +871,17 @@ class SeparateCompiler
 
        redef fun compile_nitni_structs
        do
-               self.header.add_decl("struct nitni_instance \{struct instance *value;\};")
+               self.header.add_decl """
+struct nitni_instance \{
+       struct nitni_instance *next,
+               *prev; /* adjacent global references in global list */
+       int count; /* number of time this global reference has been marked */
+       struct instance *value;
+\};
+"""
+               super
        end
-       
+
        redef fun finalize_ffi_for_module(mmodule)
        do
                var old_module = self.mainmodule
index 126cde0..a60cccd 100644 (file)
@@ -71,6 +71,7 @@ redef class ModelBuilder
 
                # The main function of the C
                compiler.new_file("{mainmodule.name}.main")
+               compiler.compile_nitni_global_ref_functions
                compiler.compile_main_function
 
                # compile methods
index 0cb7961..73d8683 100644 (file)
@@ -153,6 +153,7 @@ class VirtualMachine super NaiveInterpreter
                for(i=0; i<size; i++)
                        attributes[i] = null_instance;
 
+               Instance_incr_ref(null_instance);
                return attributes;
        `}
 
@@ -199,9 +200,6 @@ class VirtualMachine super NaiveInterpreter
 
                var id = mproperty.intro_mclassdef.mclass.vtable.id
                
-               # TODO : ugly hack
-               recv.attributes[mproperty] = value
-
                # Replace the old value of mproperty in recv
                write_attribute_ph(recv.internal_attributes, recv.vtable.internal_vtable,
                                        recv.vtable.mask, id, mproperty.offset, value)
@@ -223,6 +221,7 @@ class VirtualMachine super NaiveInterpreter
                int absolute_offset = *(pointer + 1);
 
                ((Instance *)instance)[absolute_offset + offset] = value;
+               Instance_incr_ref(value);
        `}
 end
 
@@ -438,4 +437,4 @@ class MemoryManager
 
                return vtable;
        `}
-end
\ No newline at end of file
+end
index eff6c9d..67896c0 100644 (file)
@@ -7,3 +7,4 @@ shoot_linux
 websocket_server
 converter
 mnit_linux
+friendz_linux
diff --git a/tests/sav/fixme/nitvm_args3.res b/tests/sav/fixme/nitvm_args3.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
diff --git a/tests/sav/friendz.res b/tests/sav/friendz.res
new file mode 100644 (file)
index 0000000..6f89576
--- /dev/null
@@ -0,0 +1 @@
+Runtime error: Abstract method `generate_input` called on `App` (../lib/mnit/mnit_app.nit:63)
diff --git a/tests/sav/friendz_android.res b/tests/sav/friendz_android.res
new file mode 100644 (file)
index 0000000..174d681
--- /dev/null
@@ -0,0 +1 @@
+Not executable (platform?)
diff --git a/tests/sav/javap_visitor.res b/tests/sav/javap_visitor.res
new file mode 100644 (file)
index 0000000..4ad3dc3
--- /dev/null
@@ -0,0 +1 @@
+UNDEFINED
diff --git a/tests/sav/test_sqlite3_nity.res b/tests/sav/test_sqlite3_nity.res
new file mode 100644 (file)
index 0000000..4101597
--- /dev/null
@@ -0,0 +1,12 @@
+####
+uname: Bob
+pass: zzz
+activated: 1
+perc: 77.7
+####
+uname: Guillaume
+pass: xxx
+activated: 1
+perc: 88.8
+true
+false
diff --git a/tests/sav/test_sqlite3_nity_alt1.res b/tests/sav/test_sqlite3_nity_alt1.res
new file mode 100644 (file)
index 0000000..4aa3a13
--- /dev/null
@@ -0,0 +1,2 @@
+Runtime error: Assert failed (alt/test_sqlite3_nity_alt1.nit:27)
+unable to open database file
diff --git a/tests/sav/test_sqlite3_nity_alt2.res b/tests/sav/test_sqlite3_nity_alt2.res
new file mode 100644 (file)
index 0000000..8d75750
--- /dev/null
@@ -0,0 +1,2 @@
+Runtime error: Assert failed (alt/test_sqlite3_nity_alt2.nit:39)
+SQL logic error or missing database
diff --git a/tests/test_ffi_c_lots_of_refs.nit b/tests/test_ffi_c_lots_of_refs.nit
new file mode 100644 (file)
index 0000000..79c2654
--- /dev/null
@@ -0,0 +1,49 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+extern class As `{ A* `}
+       new (size: Int) `{
+               return malloc(sizeof(A) * size);
+       `}
+
+       fun []=(i: Int, v: A) `{
+               recv[i] = v;
+               A_incr_ref(v);
+       `}
+
+       fun [](i: Int): A `{
+               return recv[i];
+       `}
+end
+
+class A
+       var i: Int
+       var s: String
+
+       init(i: Int)
+       do
+               self.i = i
+               self.s = i.to_s
+       end
+
+       redef fun to_s do return "<{i} {s}>"
+end
+
+var length = 1000000
+var aaa = new As(length)
+for i in length.times do aaa[i] = new A(i)
+sys.force_garbage_collection
+for i in length.times do assert aaa[i].to_s == "<{i} {i}>"
similarity index 89%
rename from tests/test_sqlite3.nit
rename to tests/test_sqlite3_native.nit
index 6ddb8a7..614619e 100644 (file)
@@ -15,9 +15,9 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-module test_sqlite3
+module test_sqlite3_native
 
-import sqlite3
+import sqlite3::native_sqlite3
 
 var filename = "test.db"
 filename.file_delete
@@ -26,7 +26,7 @@ var insert_req_1 = "INSERT INTO users VALUES('Bob', 'zzz', 1)"
 var insert_req_2 = "INSERT INTO users VALUES('Guillaume', 'xxx', 1)"
 var select_req = "SELECT * FROM users"
 
-var db = new Sqlite3.open(filename)
+var db = new NativeSqlite3.open(filename)
 assert sqlite_open: db.error.is_ok
 
 db.exec(create_req)
@@ -53,11 +53,11 @@ end
 
 db.close
 
-db = new Sqlite3.open(filename)
+db = new NativeSqlite3.open(filename)
 assert sqlite_reopen: db.error.is_ok
 
 stmt = db.prepare(select_req)
 assert sqlite_reselect: db.error.is_ok
 assert stmt != null
 stmt.step
-assert sqlite_column_0_0_reopened: stmt.column_text(0) == "Bob"
+assert sqlite_column_0_0_reopened: stmt.column_text(0).to_s == "Bob"
diff --git a/tests/test_sqlite3_nity.nit b/tests/test_sqlite3_nity.nit
new file mode 100644 (file)
index 0000000..ea09f8c
--- /dev/null
@@ -0,0 +1,63 @@
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Copyright 2013 Guillaume Auger <jeho@resist.ca>
+# Copyright 2013-2014 Alexis Laferrière <alexis.laf@xymus.net>
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import sqlite3
+
+var path = "test_nity.db"
+#alt1#path = "/../invalid_path.db"
+if path.file_exists then path.file_delete
+
+var db = new Sqlite3DB.open(path)
+assert db.is_open else print db.error or else "no error?"
+
+assert db.create_table("IF NOT EXISTS users (uname TEXT PRIMARY KEY, pass TEXT NOT NULL, activated INTEGER, perc FLOAT)") else
+       print db.error or else "no error?"
+end
+
+assert db.insert("INTO users VALUES('Bob', 'zzz', 1, 77.7)") else
+       print db.error or else "no error?"
+end
+
+assert db.insert("INTO users VALUES('Guillaume', 'xxx', 1, 88.8)") else
+       print db.error or else "no error?"
+end
+
+#alt2#assert db.insert("INTO notable VALUES('Alexis', 'asdf', 2, 99.9)") else
+#alt2# print db.error or else "no error?"
+#alt2#end
+
+for row in db.select("* FROM users") do
+       print "####"
+
+       printn "{row[0].name}: "
+       print row[0]
+
+       printn "{row[1].name}: "
+       var val = row[1].value
+       assert val isa Text
+       print val.to_s
+
+       printn "{row[2].name}: "
+       print row[2].to_i
+
+       printn "{row[3].name}: "
+       print row[3].to_f
+end
+
+print db.is_open
+db.close
+print db.is_open