Merge: Functional api
[nit.git] / contrib / nitin / nitin.nit
index 149e616..6651636 100644 (file)
 # The Nit interactive interpreter
 module nitin
 
+import prompt
+
 import nitc::interpreter
 import nitc::frontend
 import nitc::parser_util
+intrude import nitc::scope
 
 redef class ToolContext
 
@@ -55,21 +58,6 @@ redef class ToolContext
                return tree.n_base.as(not null)
        end
 
-       # Read an user-line with a given `prompt`
-       #
-       # Return `null` if end of file
-       fun readline(prompt: String): nullable String do
-               printn prompt
-               var res = stdin.read_line
-               if res == "" and stdin.eof then return null
-               return res
-       end
-
-       # Add `text` in the history for `readline`.
-       #
-       # With the default implementation, the history is dropped
-       fun readline_add_history(text: String) do end
-
        # The last line number read by `i_parse`
        var last_line = 0
 
@@ -84,7 +72,7 @@ redef class ToolContext
                                s = stdin.read_line
                                if s == "" and stdin.eof then s = null
                        else
-                               s = readline(prompt)
+                               s = sys.prompt(prompt)
                        end
                        if s == null then return null
                        if s == "" then
@@ -106,7 +94,7 @@ redef class ToolContext
                        oldtext = ""
                        var n = p_module(text)
 
-                       if n isa AParserError and (n.token isa EOF) then
+                       if n isa AParserError and (n.token isa EOF or n.token isa TBadTString or n.token isa TBadExtern) then
                                # Unexpected end of file, thus continuing
                                if oldtext == "" then prompt = "." * prompt.length
                                oldtext = text
@@ -114,12 +102,68 @@ redef class ToolContext
                        end
 
                        last_line = n.location.file.line_starts.length - 1
-                       readline_add_history(text.chomp)
+                       prompt_add_history(text.chomp)
                        return n
                end
        end
 end
 
+redef class AMethPropdef
+       var injected_variables: nullable Map[Variable, Instance] = null is writable
+       var new_variables: nullable Array[Variable] = null
+
+       redef fun accept_scope_visitor(v)
+       do
+               var injected_variables = self.injected_variables
+               if injected_variables == null then
+                       super
+                       return
+               end
+
+               # Inject main variables in the initial scope
+               var scope = v.scopes.first
+               for variable in injected_variables.keys do
+                       scope.variables[variable.name] = variable
+               end
+
+               super
+
+               # Gather new top-level variables as main variables
+               scope = v.scopes.first
+               var new_variables = new Array[Variable]
+               for variable in scope.variables.values do
+                       if not injected_variables.has_key(variable) then
+                               new_variables.add(variable)
+                       end
+               end
+               self.new_variables = new_variables
+       end
+
+       redef fun call_commons(v, m, a, f)
+       do
+               var injected_variables = self.injected_variables
+               if injected_variables == null then return super
+
+               # Inject main variables in the frame
+               assert f isa InterpreterFrame
+               for variable, i in injected_variables do
+                       f.map[variable] = i
+               end
+
+               var res = super
+
+               # Update the values of the variables
+               for variable in injected_variables.keys do
+                       injected_variables[variable] = f.map[variable]
+               end
+               # Retrieve the values of the new main variables
+               for variable in new_variables.as(not null) do
+                       injected_variables[variable] = f.map[variable]
+               end
+
+               return res
+       end
+end
 
 # Create a tool context to handle options and paths
 var toolcontext = new ToolContext
@@ -155,6 +199,8 @@ var sys_type = mainobj.mtype.as(MClassType)
 var mainprop = mainmodule.try_get_primitive_method("main", sys_type.mclass)
 assert mainprop != null
 
+var main_variables = new Map[Variable, Instance]
+
 var l = 0
 loop
        # Next piece of Nit code
@@ -177,6 +223,7 @@ loop
        # An error
        if n isa AError then
                modelbuilder.error(n, n.message)
+               toolcontext.check_errors
                continue
        end
 
@@ -194,6 +241,7 @@ loop
        if amodule.n_classdefs.not_empty and amodule.n_classdefs.last isa AMainClassdef then
                main_method = amodule.n_classdefs.last.n_propdefs.first
                assert main_method isa AMethPropdef
+               main_method.injected_variables = main_variables
        end
 
        modelbuilder.run_phases