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
24 import nitc
::interpreter
26 import nitc
::parser_util
27 intrude import nitc
::scope
29 redef class ToolContext
32 var opt_no_prompt
= new OptionBool("Disable writing a prompt.", "--no-prompt")
35 var opt_source_name
= new OptionString("Set a name for the input source.", "--source-name")
39 option_context
.add_option
(opt_no_prompt
, opt_source_name
)
42 # Parse a full module given as a string
44 # Return a AModule or a AError
45 fun p_module
(string
: String): ANode
47 var source_name
= opt_source_name
.value
or else ""
48 string
= "\n" * last_line
+ string
49 var source
= new SourceFile.from_string
(source_name
, string
)
50 var lexer
= new Lexer(source
)
51 var parser
= new Parser(lexer
)
52 var tree
= parser
.parse
55 if eof
isa AError then
58 return tree
.n_base
.as(not null)
61 # The last line number read by `i_parse`
64 # Parse the input of the user as a module
65 fun i_parse
(prompt
: String): nullable ANode
71 if opt_no_prompt
.value
then
73 if s
== "" and stdin
.eof
then s
= null
75 s
= sys
.prompt
(prompt
)
77 if s
== null then return null
87 if s
.chars
.first
== ':' then
93 var text
= oldtext
+ s
+ "\n"
95 var n
= p_module
(text
)
97 if n
isa AParserError and (n
.token
isa EOF or n
.token
isa TBadTString or n
.token
isa TBadExtern) then
98 # Unexpected end of file, thus continuing
99 if oldtext
== "" then prompt
= "." * prompt
.length
104 last_line
= n
.location
.file
.line_starts
.length
- 1
105 prompt_add_history
(text
.chomp
)
111 redef class AMethPropdef
112 var injected_variables
: nullable Map[Variable, Instance] = null is writable
113 var new_variables
: nullable Array[Variable] = null
115 redef fun accept_scope_visitor
(v
)
117 var injected_variables
= self.injected_variables
118 if injected_variables
== null then
123 # Inject main variables in the initial scope
124 var scope
= v
.scopes
.first
125 for variable
in injected_variables
.keys
do
126 scope
.variables
[variable
.name
] = variable
131 # Gather new top-level variables as main variables
132 scope
= v
.scopes
.first
133 var new_variables
= new Array[Variable]
134 for variable
in scope
.variables
.values
do
135 if not injected_variables
.has_key
(variable
) then
136 new_variables
.add
(variable
)
139 self.new_variables
= new_variables
142 redef fun call_commons
(v
, m
, a
, f
)
144 var injected_variables
= self.injected_variables
145 if injected_variables
== null then return super
147 # Inject main variables in the frame
148 assert f
isa InterpreterFrame
149 for variable
, i
in injected_variables
do
155 # Update the values of the variables
156 for variable
in injected_variables
.keys
do
157 injected_variables
[variable
] = f
.map
[variable
]
159 # Retrieve the values of the new main variables
160 for variable
in new_variables
.as(not null) do
161 injected_variables
[variable
] = f
.map
[variable
]
168 # Create a tool context to handle options and paths
169 var toolcontext
= new ToolContext
170 toolcontext
.option_context
.options_before_rest
= true
171 toolcontext
.accept_no_arguments
= true
172 toolcontext
.keep_going
= true
173 toolcontext
.process_options
(args
)
175 # We need a model to collect stufs
176 var model
= new Model
177 # An a model builder to parse files
178 var modelbuilder
= new ModelBuilder(model
, toolcontext
)
180 var arguments
= toolcontext
.option_context
.rest
182 # Our initial program is an empty module
183 var amodule
= toolcontext
.parse_module
("")
184 var mmodule
= modelbuilder
.load_rt_module
(null, amodule
, "input-0")
185 modelbuilder
.run_phases
186 if not toolcontext
.check_errors
then return
187 assert mmodule
!= null
188 var mmodules
= [mmodule
]
189 var mainmodule
= toolcontext
.make_main_module
(mmodules
)
191 # Start and run the interpreter on the empty module
192 var interpreter
= new NaiveInterpreter(modelbuilder
, mainmodule
, arguments
)
193 interpreter
.start
(mainmodule
)
195 # Get the main object and the main method
196 var mainobj
= interpreter
.mainobj
197 assert mainobj
!= null
198 var sys_type
= mainobj
.mtype
.as(MClassType)
199 var mainprop
= mainmodule
.try_get_primitive_method
("main", sys_type
.mclass
)
200 assert mainprop
!= null
202 var main_variables
= new Map[Variable, Instance]
206 # Next piece of Nit code
207 var n
= toolcontext
.i_parse
("-->")
212 # Special adhoc command
213 if n
isa TString then
225 modelbuilder
.error
(n
, n
.message
)
226 toolcontext
.check_errors
232 # A syntactically module!
233 amodule
= n
.as(AModule)
235 # Try to load it as a submodule
237 var newmodule
= modelbuilder
.load_rt_module
(mainmodule
, amodule
, "input-{l}")
238 if newmodule
== null then continue
240 var main_method
= null
241 if amodule
.n_classdefs
.not_empty
and amodule
.n_classdefs
.last
isa AMainClassdef then
242 main_method
= amodule
.n_classdefs
.last
.n_propdefs
.first
243 assert main_method
isa AMethPropdef
244 main_method
.injected_variables
= main_variables
247 modelbuilder
.run_phases
248 if not toolcontext
.check_errors
then
249 toolcontext
.error_count
= 0
252 # Everything is fine, the module is the new main module!
253 mainmodule
= newmodule
254 interpreter
.mainmodule
= mainmodule
256 # Run the main if the AST contains a main
257 if main_method
!= null then
259 interpreter
.catch_count
+= 1
260 interpreter
.send
(mainprop
, [mainobj
])
262 var e
= interpreter
.last_error
266 print
"{en.location}: Runtime error: {e.message}\n{en.location.colored_line("0;31")}"
268 print
"Runtime error: {e.message}"
271 print interpreter
.stack_trace
272 interpreter
.frames
.clear