--- /dev/null
+# An experimental Nit interactive interpreter
+
+This tool is outside src/ because:
+
+1. Is is greatly experimental
+2. It can depend on readline (GPL3) whereas the rest of nitc is Apache2.
+ Both are compatible but the final binary result is GLP3.
+
+## Features
+
+* use GNU readline to read lines
+* use importation/refinement to handle incremental execution (so basically everything works out of the box)
+* maintain an interpreter and live objects (the model grows but the interpreter and runtime data are reused)
+
+Main missing features
+
+* top-level variables are local
+* runtime error/aborts just abort the interactive loop
+* FFI is strange
+* No model/object inspection
+
+## Examples
+
+`-->` is the prompt, `...` the continuation prompt. What follows is the user input.
+The rest is the output.
+
+### Basic instructions
+
+~~~raw
+$ nitin
+--> print 5+2
+7
+~~~
+
+### Complex and control statements
+
+~~~raw
+-->for i in [0..5[ do
+...print i
+...end
+0
+1
+2
+3
+4
+~~~
+
+You can use `do` blocks to delay the execution and control the scope of variables.
+
+~~~raw
+-->do
+...var sum=0
+...for i in [0..50[ do
+...sum += i
+...end
+...print sum
+...end
+1225
+~~~
+
+### Classes and methods
+
+~~~raw
+-->class A
+...fun foo do
+...print "hello"
+...end
+...end
+-->(new A).foo
+hello
+~~~
+
+### Error management
+
+In case of static errors, the history (up arrow) can be reused and updated.
+
+~~~raw
+-->class
+...end
+ end
+ ^: Syntax Error: unexpected keyword 'end'.
+-->class A **up arrow and update, thanks readline**
+end
+1,7: Redef Error: `A` is an imported class. Add the `redef` keyword to refine it.
+ class A
+ ^
+-->redef class A **up arrow and update again, thanks readline**
+redef fun foo do print "Bye"
+end
+-->(new A).foo
+bye
+~~~
+
+### Class refinement
+
+Already instantiated objects gain the new methods, attributes and specializations.
+However, the new attributes are left uninitialized (default values or init are not recomputed on existing objects)
+
+Top-level methods automatically refine Sys.
+
+~~~raw
+-->foo
+1,1--3: Error: method or variable `foo` unknown in `Sys`.
+ foo
+ ^
+-->fun foo do
+...print "I'm sys"
+...end
+-->foo
+I'm sys
+~~~
+
+You can store global variables as attributes of Sys
+
+~~~raw
+-->redef class Sys
+...var my_int: Int is writable
+...end
+-->my_int = 5
+-->print my_int
+5
+~~~
+
+### Dynamic importation
+
+~~~
+-->print([0..10[.to_a.to_json)
+1,20--26: Error: method `to_json` does not exists in `Array[Int]`.
+ print([0..10[.to_a.to_json)
+ ^
+-->import json
+-->print([0..10[.to_a.to_json)
+[0,1,2,3,4,5,6,7,8,9]
+~~~
--- /dev/null
+# This file is part of NIT ( http://www.nitlanguage.org ).
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# The Nit interactive interpreter
+module nitin
+
+import nitc::interpreter
+import nitc::frontend
+import nitc::parser_util
+
+redef class ToolContext
+ # Parse a full module given as a string
+ #
+ # Return a AModule or a AError
+ fun p_module(string: String): ANode
+ do
+ var source = new SourceFile.from_string("", string)
+ var lexer = new Lexer(source)
+ var parser = new Parser(lexer)
+ var tree = parser.parse
+
+ var eof = tree.n_eof
+ if eof isa AError then
+ return eof
+ end
+ 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
+
+ # Parse the input of the user as a module
+ fun i_parse(prompt: String): nullable ANode
+ do
+ var oldtext = ""
+
+ loop
+ var s = readline(prompt)
+ if s == null then return null
+ if s == "" then continue
+
+ if s.chars.first == ':' then
+ var res = new TString
+ res.text = s
+ return res
+ end
+
+ var text = oldtext + s + "\n"
+ oldtext = ""
+ var n = p_module(text)
+
+ if n isa AParserError and (n.token isa EOF) then
+ # Unexpected end of file, thus continuing
+ if oldtext == "" then prompt = "." * prompt.length
+ oldtext = text
+ continue
+ end
+
+ readline_add_history(text.chomp)
+ return n
+ end
+ end
+end
+
+
+# Create a tool context to handle options and paths
+var toolcontext = new ToolContext
+toolcontext.option_context.options_before_rest = true
+toolcontext.accept_no_arguments = true
+toolcontext.keep_going = true
+toolcontext.process_options(args)
+
+# We need a model to collect stufs
+var model = new Model
+# An a model builder to parse files
+var modelbuilder = new ModelBuilder(model, toolcontext)
+
+var arguments = toolcontext.option_context.rest
+
+# Our initial program is an empty module
+var amodule = toolcontext.parse_module("")
+var mmodule = modelbuilder.load_rt_module(null, amodule, "input-0")
+modelbuilder.run_phases
+if not toolcontext.check_errors then return
+assert mmodule != null
+var mmodules = [mmodule]
+var mainmodule = toolcontext.make_main_module(mmodules)
+
+# Start and run the interpreter on the empty module
+var interpreter = new NaiveInterpreter(modelbuilder, mainmodule, arguments)
+interpreter.start(mainmodule)
+
+# Get the main object and the main method
+var mainobj = interpreter.mainobj
+assert mainobj != null
+var sys_type = mainobj.mtype.as(MClassType)
+var mainprop = mainmodule.try_get_primitive_method("main", sys_type.mclass)
+assert mainprop != null
+
+var l = 0
+loop
+ # Next piece of Nit code
+ var n = toolcontext.i_parse("-->")
+ if n == null then
+ break
+ end
+
+ # Special adhoc command
+ if n isa TString then
+ var s = n.text
+ if s == ":q" then
+ break
+ else
+ print "`:q` to quit"
+ end
+ continue
+ end
+
+ # An error
+ if n isa AError then
+ print "{n.location.colored_line("0;31")}: {n.message}"
+ continue
+ end
+
+ #n.dump_tree
+
+ # A syntactically module!
+ amodule = n.as(AModule)
+
+ # Try to load it as a submodule
+ l += 1
+ var newmodule = modelbuilder.load_rt_module(mainmodule, amodule, "input-{l}")
+ if newmodule == null then continue
+ modelbuilder.run_phases
+ if not toolcontext.check_errors then
+ toolcontext.error_count = 0
+ continue
+ end
+ # Everything is fine, the module is the new main module!
+ mainmodule = newmodule
+ interpreter.mainmodule = mainmodule
+
+ # Run the main if the AST contains a main
+ if amodule.n_classdefs.not_empty and amodule.n_classdefs.last isa AMainClassdef then
+ interpreter.send(mainprop, [mainobj])
+ end
+end