Merge: Nitin tests
[nit.git] / contrib / nitin / nitin.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
18
19 # The Nit interactive interpreter
20 module nitin
21
22 import nitc::interpreter
23 import nitc::frontend
24 import nitc::parser_util
25
26 redef class ToolContext
27
28 # --no-prompt
29 var opt_no_prompt = new OptionBool("Disable writing a prompt.", "--no-prompt")
30
31 redef init do
32 super
33 option_context.add_option(opt_no_prompt)
34 end
35
36 # Parse a full module given as a string
37 #
38 # Return a AModule or a AError
39 fun p_module(string: String): ANode
40 do
41 var source = new SourceFile.from_string("", string)
42 var lexer = new Lexer(source)
43 var parser = new Parser(lexer)
44 var tree = parser.parse
45
46 var eof = tree.n_eof
47 if eof isa AError then
48 return eof
49 end
50 return tree.n_base.as(not null)
51 end
52
53 # Read an user-line with a given `prompt`
54 #
55 # Return `null` if end of file
56 fun readline(prompt: String): nullable String do
57 printn prompt
58 var res = stdin.read_line
59 if res == "" and stdin.eof then return null
60 return res
61 end
62
63 # Add `text` in the history for `readline`.
64 #
65 # With the default implementation, the history is dropped
66 fun readline_add_history(text: String) do end
67
68 # Parse the input of the user as a module
69 fun i_parse(prompt: String): nullable ANode
70 do
71 var oldtext = ""
72
73 loop
74 var s
75 if opt_no_prompt.value then
76 s = stdin.read_line
77 if s == "" and stdin.eof then s = null
78 else
79 s = readline(prompt)
80 end
81 if s == null then return null
82 if s == "" then continue
83
84 if s.chars.first == ':' then
85 var res = new TString
86 res.text = s
87 return res
88 end
89
90 var text = oldtext + s + "\n"
91 oldtext = ""
92 var n = p_module(text)
93
94 if n isa AParserError and (n.token isa EOF) then
95 # Unexpected end of file, thus continuing
96 if oldtext == "" then prompt = "." * prompt.length
97 oldtext = text
98 continue
99 end
100
101 readline_add_history(text.chomp)
102 return n
103 end
104 end
105 end
106
107
108 # Create a tool context to handle options and paths
109 var toolcontext = new ToolContext
110 toolcontext.option_context.options_before_rest = true
111 toolcontext.accept_no_arguments = true
112 toolcontext.keep_going = true
113 toolcontext.process_options(args)
114
115 # We need a model to collect stufs
116 var model = new Model
117 # An a model builder to parse files
118 var modelbuilder = new ModelBuilder(model, toolcontext)
119
120 var arguments = toolcontext.option_context.rest
121
122 # Our initial program is an empty module
123 var amodule = toolcontext.parse_module("")
124 var mmodule = modelbuilder.load_rt_module(null, amodule, "input-0")
125 modelbuilder.run_phases
126 if not toolcontext.check_errors then return
127 assert mmodule != null
128 var mmodules = [mmodule]
129 var mainmodule = toolcontext.make_main_module(mmodules)
130
131 # Start and run the interpreter on the empty module
132 var interpreter = new NaiveInterpreter(modelbuilder, mainmodule, arguments)
133 interpreter.start(mainmodule)
134
135 # Get the main object and the main method
136 var mainobj = interpreter.mainobj
137 assert mainobj != null
138 var sys_type = mainobj.mtype.as(MClassType)
139 var mainprop = mainmodule.try_get_primitive_method("main", sys_type.mclass)
140 assert mainprop != null
141
142 var l = 0
143 loop
144 # Next piece of Nit code
145 var n = toolcontext.i_parse("-->")
146 if n == null then
147 break
148 end
149
150 # Special adhoc command
151 if n isa TString then
152 var s = n.text
153 if s == ":q" then
154 break
155 else
156 print "`:q` to quit"
157 end
158 continue
159 end
160
161 # An error
162 if n isa AError then
163 print "{n.location.colored_line("0;31")}: {n.message}"
164 continue
165 end
166
167 #n.dump_tree
168
169 # A syntactically module!
170 amodule = n.as(AModule)
171
172 # Try to load it as a submodule
173 l += 1
174 var newmodule = modelbuilder.load_rt_module(mainmodule, amodule, "input-{l}")
175 if newmodule == null then continue
176 modelbuilder.run_phases
177 if not toolcontext.check_errors then
178 toolcontext.error_count = 0
179 continue
180 end
181 # Everything is fine, the module is the new main module!
182 mainmodule = newmodule
183 interpreter.mainmodule = mainmodule
184
185 # Run the main if the AST contains a main
186 if amodule.n_classdefs.not_empty and amodule.n_classdefs.last isa AMainClassdef then
187 interpreter.send(mainprop, [mainobj])
188 end
189 end