1 # This file is part of NIT ( http://www.nitlanguage.org ).
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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.
19 # The Nit interactive interpreter
22 import nitc
::interpreter
24 import nitc
::parser_util
25 intrude import nitc
::scope
27 redef class ToolContext
30 var opt_no_prompt
= new OptionBool("Disable writing a prompt.", "--no-prompt")
33 var opt_source_name
= new OptionString("Set a name for the input source.", "--source-name")
37 option_context
.add_option
(opt_no_prompt
, opt_source_name
)
40 # Parse a full module given as a string
42 # Return a AModule or a AError
43 fun p_module
(string
: String): ANode
45 var source_name
= opt_source_name
.value
or else ""
46 string
= "\n" * last_line
+ string
47 var source
= new SourceFile.from_string
(source_name
, string
)
48 var lexer
= new Lexer(source
)
49 var parser
= new Parser(lexer
)
50 var tree
= parser
.parse
53 if eof
isa AError then
56 return tree
.n_base
.as(not null)
59 # Read an user-line with a given `prompt`
61 # Return `null` if end of file
62 fun readline
(prompt
: String): nullable String do
64 var res
= stdin
.read_line
65 if res
== "" and stdin
.eof
then return null
69 # Add `text` in the history for `readline`.
71 # With the default implementation, the history is dropped
72 fun readline_add_history
(text
: String) do end
74 # The last line number read by `i_parse`
77 # Parse the input of the user as a module
78 fun i_parse
(prompt
: String): nullable ANode
84 if opt_no_prompt
.value
then
86 if s
== "" and stdin
.eof
then s
= null
90 if s
== null then return null
100 if s
.chars
.first
== ':' then
101 var res
= new TString
106 var text
= oldtext
+ s
+ "\n"
108 var n
= p_module
(text
)
110 if n
isa AParserError and (n
.token
isa EOF or n
.token
isa TBadTString or n
.token
isa TBadExtern) then
111 # Unexpected end of file, thus continuing
112 if oldtext
== "" then prompt
= "." * prompt
.length
117 last_line
= n
.location
.file
.line_starts
.length
- 1
118 readline_add_history
(text
.chomp
)
124 redef class AMethPropdef
125 var injected_variables
: nullable Map[Variable, Instance] = null is writable
126 var new_variables
: nullable Array[Variable] = null
128 redef fun accept_scope_visitor
(v
)
130 var injected_variables
= self.injected_variables
131 if injected_variables
== null then
136 # Inject main variables in the initial scope
137 var scope
= v
.scopes
.first
138 for variable
in injected_variables
.keys
do
139 scope
.variables
[variable
.name
] = variable
144 # Gather new top-level variables as main variables
145 scope
= v
.scopes
.first
146 var new_variables
= new Array[Variable]
147 for variable
in scope
.variables
.values
do
148 if not injected_variables
.has_key
(variable
) then
149 new_variables
.add
(variable
)
152 self.new_variables
= new_variables
155 redef fun call_commons
(v
, m
, a
, f
)
157 var injected_variables
= self.injected_variables
158 if injected_variables
== null then return super
160 # Inject main variables in the frame
161 assert f
isa InterpreterFrame
162 for variable
, i
in injected_variables
do
168 # Update the values of the variables
169 for variable
in injected_variables
.keys
do
170 injected_variables
[variable
] = f
.map
[variable
]
172 # Retrieve the values of the new main variables
173 for variable
in new_variables
.as(not null) do
174 injected_variables
[variable
] = f
.map
[variable
]
181 # Create a tool context to handle options and paths
182 var toolcontext
= new ToolContext
183 toolcontext
.option_context
.options_before_rest
= true
184 toolcontext
.accept_no_arguments
= true
185 toolcontext
.keep_going
= true
186 toolcontext
.process_options
(args
)
188 # We need a model to collect stufs
189 var model
= new Model
190 # An a model builder to parse files
191 var modelbuilder
= new ModelBuilder(model
, toolcontext
)
193 var arguments
= toolcontext
.option_context
.rest
195 # Our initial program is an empty module
196 var amodule
= toolcontext
.parse_module
("")
197 var mmodule
= modelbuilder
.load_rt_module
(null, amodule
, "input-0")
198 modelbuilder
.run_phases
199 if not toolcontext
.check_errors
then return
200 assert mmodule
!= null
201 var mmodules
= [mmodule
]
202 var mainmodule
= toolcontext
.make_main_module
(mmodules
)
204 # Start and run the interpreter on the empty module
205 var interpreter
= new NaiveInterpreter(modelbuilder
, mainmodule
, arguments
)
206 interpreter
.start
(mainmodule
)
208 # Get the main object and the main method
209 var mainobj
= interpreter
.mainobj
210 assert mainobj
!= null
211 var sys_type
= mainobj
.mtype
.as(MClassType)
212 var mainprop
= mainmodule
.try_get_primitive_method
("main", sys_type
.mclass
)
213 assert mainprop
!= null
215 var main_variables
= new Map[Variable, Instance]
219 # Next piece of Nit code
220 var n
= toolcontext
.i_parse
("-->")
225 # Special adhoc command
226 if n
isa TString then
238 modelbuilder
.error
(n
, n
.message
)
239 toolcontext
.check_errors
245 # A syntactically module!
246 amodule
= n
.as(AModule)
248 # Try to load it as a submodule
250 var newmodule
= modelbuilder
.load_rt_module
(mainmodule
, amodule
, "input-{l}")
251 if newmodule
== null then continue
253 var main_method
= null
254 if amodule
.n_classdefs
.not_empty
and amodule
.n_classdefs
.last
isa AMainClassdef then
255 main_method
= amodule
.n_classdefs
.last
.n_propdefs
.first
256 assert main_method
isa AMethPropdef
257 main_method
.injected_variables
= main_variables
260 modelbuilder
.run_phases
261 if not toolcontext
.check_errors
then
262 toolcontext
.error_count
= 0
265 # Everything is fine, the module is the new main module!
266 mainmodule
= newmodule
267 interpreter
.mainmodule
= mainmodule
269 # Run the main if the AST contains a main
270 if main_method
!= null then
272 interpreter
.catch_count
+= 1
273 interpreter
.send
(mainprop
, [mainobj
])
275 var e
= interpreter
.last_error
279 print
"{en.location}: Runtime error: {e.message}\n{en.location.colored_line("0;31")}"
281 print
"Runtime error: {e.message}"
284 print interpreter
.stack_trace
285 interpreter
.frames
.clear