1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2013 Lucas Bajolet <lucas.bajolet@gmail.com>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Debugging of a nit program using the NaiveInterpreter
21 intrude import naive_interpreter
23 redef class ToolContext
25 var opt_debugger_mode
: OptionBool = new OptionBool("Launches the target program with the debugger attached to it", "-d")
27 var opt_debugger_autorun
: OptionBool = new OptionBool("Launches the target program with the interpreter, such as when the program fails, the debugging prompt is summoned", "-c")
32 self.option_context
.add_option
(self.opt_debugger_mode
)
33 self.option_context
.add_option
(self.opt_debugger_autorun
)
37 redef class ModelBuilder
38 # Execute the program from the entry point (Sys::main) of the `mainmodule'
39 # `arguments' are the command-line arguments in order
41 # 1. the AST is fully loaded.
42 # 2. the model is fully built.
43 # 3. the instructions are fully analysed.
44 fun run_debugger
(mainmodule
: MModule, arguments
: Array[String])
47 self.toolcontext
.info
("*** START INTERPRETING ***", 1)
49 var interpreter
= new Debugger(self, mainmodule
, arguments
)
51 init_naive_interpreter
(interpreter
, mainmodule
)
54 self.toolcontext
.info
("*** END INTERPRETING: {time1-time0} ***", 2)
57 fun run_debugger_autorun
(mainmodule
: MModule, arguments
: Array[String])
60 self.toolcontext
.info
("*** START INTERPRETING ***", 1)
62 var interpreter
= new Debugger(self, mainmodule
, arguments
)
63 interpreter
.autocontinue
= true
65 init_naive_interpreter
(interpreter
, mainmodule
)
68 self.toolcontext
.info
("*** END INTERPRETING: {time1-time0} ***", 2)
72 # The class extending NaiveInterpreter by adding debugging methods
74 super NaiveInterpreter
76 # Keeps the frame count in memory to find when to stop
77 # and launch the command prompt after a step out call
78 var step_stack_count
= 1
80 # Triggers a step over an instruction in a nit program
81 var stop_after_step_over_trigger
= true
83 # Triggers a step out of an instruction
84 var stop_after_step_out_trigger
= false
86 # Triggers a step in a instruction (enters a function
87 # if the instruction is a function call)
88 var step_in_trigger
= false
90 # HashMap containing the breakpoints bound to a file
91 var breakpoints
= new HashMap[String, HashSet[Breakpoint]]
93 # Contains the current file
96 # Aliases hashmap (maps an alias to a variable name)
97 var aliases
= new HashMap[String, String]
99 # Set containing all the traced variables and their related frame
100 private var traces
= new HashSet[TraceObject]
102 # Map containing all the positions for the positions of the arguments traced
104 private var fun_call_arguments_positions
= new HashMap[Int, TraceObject]
106 # Triggers the remapping of a trace object in the local context after a function call
107 var aftermath
= false
109 # Used to prevent the case when the body of the function called is empty
110 # If it is not, then, the remapping won't be happening
111 var frame_count_aftermath
= 1
113 # Auto continues the execution until the end or until an error is encountered
114 var autocontinue
= false
116 #######################################################################
117 ## Execution of statement function ##
118 #######################################################################
120 # Main loop, every call to a debug command is done here
121 redef fun stmt
(n
: nullable AExpr)
123 if n
== null then return
125 var frame
= self.frame
126 var old
= frame
.current_node
127 frame
.current_node
= n
129 if not self.autocontinue
then
130 if not n
isa ABlockExpr then
135 check_funcall_and_traced_args
(n
)
139 check_if_vars_are_traced
(n
)
144 frame
.current_node
= old
147 # Encpasulates the behaviour for step over/out
148 private fun steps_fun_call
(n
: AExpr)
150 if self.stop_after_step_over_trigger
then
151 if self.frames
.length
<= self.step_stack_count
then
152 n
.debug
("Execute stmt {n.to_s}")
153 while process_debug_command
(gets
) do end
155 else if self.stop_after_step_out_trigger
then
156 if frames
.length
< self.step_stack_count
then
157 n
.debug
("Execute stmt {n.to_s}")
158 while process_debug_command
(gets
) do end
160 else if step_in_trigger
then
161 n
.debug
("Execute stmt {n.to_s}")
162 while process_debug_command
(gets
) do end
166 # Checks if a breakpoint is encountered, and launches the debugging prompt if true
167 private fun breakpoint_check
(n
: AExpr)
169 var currFileNameSplit
= self.frame
.current_node
.location
.file
.filename
.to_s
.split_with
("/")
171 self.curr_file
= currFileNameSplit
[currFileNameSplit
.length-1
]
173 var breakpoint
= find_breakpoint
(curr_file
, n
.location
.line_start
)
175 if breakpoints
.keys
.has
(curr_file
) and breakpoint
!= null then
179 if not breakpoint
.is_valid
181 remove_breakpoint
(curr_file
, n
.location
.line_start
)
184 n
.debug
("Execute stmt {n.to_s}")
185 while process_debug_command
(gets
) do end
189 # Check if a variable of current expression is traced
190 # Then prints and/or breaks for command prompt
191 private fun check_if_vars_are_traced
(n
: AExpr)
193 var identifiers_in_instruction
= get_identifiers_in_current_instruction
(n
.location
.text
)
195 for i
in identifiers_in_instruction
do
196 var variable
= seek_variable
(i
, frame
)
197 for j
in self.traces
do
198 if j
.is_variable_traced_in_frame
(i
, frame
) then
199 n
.debug
("Traced variable {i} used")
200 if j
.break_on_encounter
then while process_debug_command
(gets
) do end
207 # Function remapping all the traced objects to match their name in the local context
208 private fun remap
(n
: AExpr)
210 if self.aftermath
then
212 # Trace every argument variable pre-specified
213 if self.frame_count_aftermath
< frames
.length
and fun_call_arguments_positions
.length
> 0 then
215 var ids_in_fun_def
= get_identifiers_in_current_instruction
(get_function_arguments
(frame
.mpropdef
.location
.text
))
217 for i
in fun_call_arguments_positions
.keys
do
218 self.fun_call_arguments_positions
[i
].add_frame_variable
(frame
, ids_in_fun_def
[i
])
222 self.aftermath
= false
226 # If the current instruction is a function call
227 # We analyse its signature and the position of traced arguments if the call
228 # For future remapping when inside the function
229 private fun check_funcall_and_traced_args
(n
: AExpr) do
230 # If we have a function call, we need to see if any of the arguments is traced (including the caller)
231 # if it is, next time we face an instruction, we'll trace the local version on the traced variable in the next frame
232 if n
isa ACallExpr then
233 self.aftermath
= true
234 self.frame_count_aftermath
= frames
.length
235 fun_call_arguments_positions
.clear
236 var fun_arguments
= get_identifiers_in_current_instruction
(get_function_arguments
(n
.location
.text
))
238 for i
in self.traces
do
239 for j
in [0 .. fun_arguments
.length
- 1] do
240 if i
.is_variable_traced_in_frame
(fun_arguments
[j
],frame
) then
241 fun_call_arguments_positions
[j
] = i
248 #######################################################################
249 ## Processing commands functions ##
250 #######################################################################
252 # Takes a user command as a parameter
254 # Returns a boolean value, representing whether or not to
255 # continue reading commands from the console input
256 fun process_debug_command
(command
:String): Bool
261 # Kills the current program
262 if command
== "kill" then
265 else if command
== "finish"
269 else if command
== "s"
273 else if command
== "n" then
275 # Continues execution until the end
276 else if command
== "c" then
279 var parts_of_command
= command
.split_with
(' ')
280 # Shows the value of a variable in the current frame
281 if parts_of_command
[0] == "p" or parts_of_command
[0] == "print" then
282 print_command
(parts_of_command
)
283 # Places a breakpoint on line x of file y
284 else if parts_of_command
[0] == "break" or parts_of_command
[0] == "b"
286 process_place_break_fun
(parts_of_command
)
287 # Places a temporary breakpoint on line x of file y
288 else if parts_of_command
[0] == "tbreak" and (parts_of_command
.length
== 2 or parts_of_command
.length
== 3)
290 process_place_tbreak_fun
(parts_of_command
)
291 # Removes a breakpoint on line x of file y
292 else if parts_of_command
[0] == "d" or parts_of_command
[0] == "delete" then
293 process_remove_break_fun
(parts_of_command
)
294 # Sets an alias for a variable
295 else if parts_of_command
.length
== 3 and parts_of_command
[1] == "as"
297 add_alias
(parts_of_command
[0], parts_of_command
[2])
298 # Modifies the value of a variable in the current frame
299 else if parts_of_command
.length
>= 3 and parts_of_command
[1] == "=" then
300 process_mod_function
(parts_of_command
)
301 # Traces the modifications on a variable
302 else if parts_of_command
.length
>= 2 and parts_of_command
[0] == "trace" then
303 process_trace_command
(parts_of_command
)
304 # Untraces the modifications on a variable
305 else if parts_of_command
.length
== 2 and parts_of_command
[0] == "untrace" then
306 process_untrace_command
(parts_of_command
)
307 # Lists all the commands available
315 #######################################################################
316 ## Processing specific command functions ##
317 #######################################################################
319 # Sets the flags to step-over an instruction in the current file
322 self.step_stack_count
= frames
.length
323 self.stop_after_step_over_trigger
= true
324 self.stop_after_step_out_trigger
= false
325 self.step_in_trigger
= false
329 #Sets the flags to step-out of a function
332 self.stop_after_step_over_trigger
= false
333 self.stop_after_step_out_trigger
= true
334 self.step_in_trigger
= false
335 self.step_stack_count
= frames
.length
339 # Sets the flags to step-in an instruction
342 self.step_in_trigger
= true
343 self.stop_after_step_over_trigger
= false
344 self.stop_after_step_out_trigger
= false
348 # Sets the flags to continue execution
349 fun continue_exec
: Bool
351 self.stop_after_step_over_trigger
= false
352 self.stop_after_step_out_trigger
= false
353 self.step_in_trigger
= false
357 # Prints the demanded variable in the command
359 # The name of the variable in in position 1 of the array 'parts_of_command'
360 fun print_command
(parts_of_command
: Array[String])
362 if parts_of_command
[1] == "*" then
363 var map_of_instances
= frame
.map
365 var keys
= map_of_instances
.iterator
367 print
"Variables collection : \n"
369 for instance
in map_of_instances
.keys
do
370 print
"Variable {instance.to_s}, Instance {map_of_instances[instance].to_s}"
373 print
"\nEnd of current instruction \n"
374 else if parts_of_command
[1] == "stack" then
375 print
self.stack_trace
376 else if parts_of_command
[1].has
('[') and parts_of_command
[1].has
(']') then
377 process_array_command
(parts_of_command
)
379 var instance
= seek_variable
(get_real_variable_name
(parts_of_command
[1]), frame
)
383 print_instance
(instance
)
385 print
"Cannot find variable {parts_of_command[1]}"
390 # Processes the input string to know where to put a breakpoint
391 fun process_place_break_fun
(parts_of_command
: Array[String])
393 var bp
= get_breakpoint_from_command
(parts_of_command
)
401 # Returns a breakpoint containing the informations stored in the command
402 fun get_breakpoint_from_command
(parts_of_command
: Array[String]): nullable Breakpoint
404 if parts_of_command
[1].is_numeric
then
405 return new Breakpoint(parts_of_command
[1].to_i
, curr_file
)
406 else if parts_of_command
.length
>= 3 and parts_of_command
[2].is_numeric
then
407 return new Breakpoint(parts_of_command
[2].to_i
, parts_of_command
[1])
413 # Processes the command of removing a breakpoint on specified line and file
414 fun process_remove_break_fun
(parts_of_command
: Array[String])
416 if parts_of_command
[1].is_numeric
then
417 remove_breakpoint
(self.curr_file
, parts_of_command
[1].to_i
)
418 else if parts_of_command
.length
>= 3 and parts_of_command
[2].is_numeric
then
419 remove_breakpoint
(parts_of_command
[1], parts_of_command
[2].to_i
)
425 # Processes an array print command
426 fun process_array_command
(parts_of_command
: Array[String])
428 var index_of_first_brace
= parts_of_command
[1].index_of
('[')
429 var variable_name
= get_real_variable_name
(parts_of_command
[1].substring
(0,index_of_first_brace
))
430 var braces
= parts_of_command
[1].substring_from
(index_of_first_brace
)
432 var indexes
= remove_braces
(braces
)
434 var index_array
= new Array[Array[Int]]
436 if indexes
!= null then
437 for index
in indexes
do
438 var temp_indexes_array
= process_index
(index
)
439 if temp_indexes_array
!= null then
440 index_array
.push
(temp_indexes_array
)
441 #print index_array.last
446 var instance
= seek_variable
(variable_name
, frame
)
448 if instance
!= null then
449 print_nested_collection
(instance
, index_array
, 0, variable_name
, "")
451 print
"Cannot find variable {variable_name}"
455 # Processes the modification function to modify a variable dynamically
457 # Command of type variable = value
458 fun process_mod_function
(parts_of_command
: Array[String])
460 parts_of_command
[0] = get_real_variable_name
(parts_of_command
[0])
461 var parts_of_variable
= parts_of_command
[0].split_with
(".")
463 if parts_of_variable
.length
> 1 then
464 var last_part
= parts_of_variable
.pop
465 var first_part
= parts_of_command
[0].substring
(0,parts_of_command
[0].length
- last_part
.length
- 1)
466 var papa
= seek_variable
(first_part
, frame
)
468 if papa
!= null and papa
isa MutableInstance then
469 var attribute
= get_attribute_in_mutable_instance
(papa
, last_part
)
471 if attribute
!= null then
472 modify_argument_of_complex_type
(papa
, attribute
, parts_of_command
[2])
476 var target
= seek_variable
(parts_of_variable
[0], frame
)
477 if target
!= null then
478 modify_in_frame
(target
, parts_of_command
[2])
483 # Processes the untrace variable command
485 # Command pattern : "untrace variable"
486 fun process_untrace_command
(parts_of_command
: Array[String])
488 var variable_name
= get_real_variable_name
(parts_of_command
[1])
489 if untrace_variable
(variable_name
) then
490 print
"Untraced variable {parts_of_command[1]}"
492 print
"{parts_of_command[1]} is not traced"
496 # Processes the trace variable command
498 # Command pattern : "trace variable [break/print]"
499 fun process_trace_command
(parts_of_command
: Array[String])
501 var variable_name
= get_real_variable_name
(parts_of_command
[1])
504 if seek_variable
(variable_name
, frame
) == null then
505 print
"Cannot find a variable called {parts_of_command[1]}"
509 if parts_of_command
.length
== 3 then
510 if parts_of_command
[2] == "break" then
519 trace_variable
(variable_name
, breaker
)
521 print
"Successfully tracing {parts_of_command[1]}"
524 #######################################################################
525 ## Trace Management functions ##
526 #######################################################################
528 # Effectively untraces the variable called *variable_name*
530 # Returns true if the variable exists, false otherwise
531 private fun untrace_variable
(variable_name
: String): Bool
533 var to_remove
: nullable TraceObject = null
534 for i
in self.traces
do
535 if i
.is_variable_traced_in_frame
(variable_name
, frame
) then
540 if to_remove
!= null then
541 self.traces
.remove
(to_remove
)
548 # Effectively traces the variable *variable_name* either in print or break mode depending on the value of breaker (break if true, print if false)
550 private fun trace_variable
(variable_name
: String, breaker
: Bool)
552 for i
in self.traces
do
553 if i
.is_variable_traced_in_frame
(variable_name
, frame
) then
554 print
"This variable is already traced"
559 var trace_object
: TraceObject
562 trace_object
= new TraceObject(true)
564 trace_object
= new TraceObject(false)
567 # We trace the current variable found for the current frame
568 trace_object
.add_frame_variable
(self.frame
, variable_name
)
570 var position_of_variable_in_arguments
= get_position_of_variable_in_arguments
(frame
, variable_name
)
572 # Start parsing the frames starting with the parent of the current one, until the highest
573 # When the variable traced is declared locally, the loop stops
574 for i
in [1 .. frames
.length-1
] do
576 # If the variable was reported to be an argument of the previous frame
577 if position_of_variable_in_arguments
!= -1 then
579 var local_name
= get_identifiers_in_current_instruction
(get_function_arguments
(frames
[i
].current_node
.location
.text
))[position_of_variable_in_arguments
]
581 position_of_variable_in_arguments
= get_position_of_variable_in_arguments
(frames
[i
], local_name
)
583 trace_object
.add_frame_variable
(frames
[i
], local_name
)
589 self.traces
.add
(trace_object
)
592 # If the variable *variable_name* is an argument of the function being executed in the frame *frame*
593 # The function returns its position in the arguments
594 # Else, it returns -1
595 private fun get_position_of_variable_in_arguments
(frame
: Frame, variable_name
: String): Int
597 var identifiers
= get_identifiers_in_current_instruction
(get_function_arguments
(frame
.mpropdef
.location
.text
))
598 for i
in [0 .. identifiers
.length-1
] do
599 # If the current traced variable is an argument of the current function, we trace its parent (at least)
600 if identifiers
[i
] == variable_name
then return i
605 # Gets all the identifiers of an instruction (uses the rules of Nit as of Mar 05 2013)
607 fun get_identifiers_in_current_instruction
(instruction
: AbstractString): Array[String]
609 var result_array
= new Array[String]
610 var instruction_buffer
= new Buffer
612 var trigger_char_escape
= false
613 var trigger_string_escape
= false
614 var trigger_concat_in_string
= false
616 for i
in instruction
do
617 if trigger_char_escape
then
618 if i
== '\'' then trigger_char_escape = false
619 else if trigger_string_escape then
621 trigger_concat_in_string = true
622 trigger_string_escape = false
623 else if i == '\
"' then trigger_string_escape = false
625 if i.is_alphanumeric or i == '_' then
626 instruction_buffer.add(i)
627 else if i == '.' then
628 if instruction_buffer.is_numeric or (instruction_buffer[0] >= 'A' and instruction_buffer[0] <= 'Z') then
629 instruction_buffer.clear
631 result_array.push(instruction_buffer.to_s)
632 instruction_buffer.add(i)
634 else if i == '\'' then
635 trigger_char_escape = true
636 else if i == '\"' then
637 trigger_string_escape = true
638 else if i == '}' then
639 trigger_concat_in_string = false
640 trigger_string_escape = true
642 if instruction_buffer.length > 0 and not instruction_buffer.is_numeric and not (instruction_buffer[0] >= 'A
' and instruction_buffer[0] <= 'Z
') then result_array.push(instruction_buffer.to_s)
643 instruction_buffer.clear
648 if instruction_buffer.length > 0 and not instruction_buffer.is_numeric and not (instruction_buffer[0] >= 'A
' and instruction_buffer[0] <= 'Z
') then result_array.push(instruction_buffer.to_s)
653 # Takes a function call or declaration and strips all but the arguments
655 fun get_function_arguments(function: AbstractString): String
658 var trigger_copy = false
661 if i == ')' then break
662 if trigger_copy then buf.add(i)
663 if i == '(' then trigger_copy = true
669 #######################################################################
670 ## Alias management functions ##
671 #######################################################################
673 # Adds a new alias to the tables
674 fun add_alias(var_represented: String, alias: String)
676 self.aliases[alias] = var_represented
679 # Gets the real name of a variable hidden by an alias
680 fun get_variable_name_by_alias(alias: String): nullable String
682 if self.aliases.keys.has(alias) then
683 return self.aliases[alias]
689 # Gets the variable named by name, whether it is an alias or not
690 fun get_real_variable_name(name: String): String
692 var explode_string = name.split_with(".")
693 var final_string = new Buffer
694 for i in explode_string do
695 var alias_resolved = get_variable_name_by_alias(i)
696 if alias_resolved != null then
697 final_string.append(get_real_variable_name(alias_resolved))
699 final_string.append(i)
701 final_string.append(".")
704 return final_string.substring(0,final_string.length-1).to_s
707 #######################################################################
708 ## Print functions ##
709 #######################################################################
711 # Prints an object instance and its attributes if it has some
713 # If it is a primitive type, its value is directly printed
714 fun print_instance(instance: Instance)
716 if instance isa MutableInstance then
717 var attributes = instance.attributes
718 print "Object : {instance}"
720 for current_attribute in attributes.keys do
721 print "Attribute : {current_attribute.to_s} \nValeur : {attributes[current_attribute].to_s}"
724 print "Found variable {instance}"
728 # Prints the attributes demanded in a SequenceRead
729 # Used recursively to print nested collections
730 fun print_nested_collection(instance: Instance, indexes: Array[Array[Int]], depth: Int, variable_name: String, depth_string: String)
732 var collection: nullable SequenceRead[Object] = null
733 var real_collection_length: nullable Int = null
735 if instance isa MutableInstance then
736 real_collection_length = get_collection_instance_real_length(instance)
737 collection = get_primary_collection(instance)
740 if collection != null and real_collection_length != null then
741 for i in indexes[depth] do
742 if i >= 0 and i < real_collection_length then
743 if depth == indexes.length-1 then
744 print "{variable_name}{depth_string}[{i}] = {collection[i]}"
746 var item_i = collection[i]
748 if item_i isa MutableInstance then
749 print_nested_collection(item_i, indexes, depth+1, variable_name, depth_string+"[{i}]")
751 print "The item at {variable_name}{depth_string}[{i}] is not a collection"
756 print "Out of bounds exception : i = {i} and collection_length = {real_collection_length.to_s}"
760 else if i >= real_collection_length then
766 if collection == null then
767 print "Cannot find {variable_name}{depth_string}"
768 else if real_collection_length != null then
769 print "Cannot find attribute length in {instance}"
771 print "Unknown error."
777 #######################################################################
778 ## Variable Exploration functions ##
779 #######################################################################
781 # Seeks a variable from the current frame called 'variable_path
', can introspect complex objects using function get_variable_in_mutable_instance
782 private fun seek_variable(variable_path: String, frame: Frame): nullable Instance
784 var full_variable = variable_path.split_with(".")
786 var full_variable_iterator = full_variable.iterator
788 var first_instance = get_variable_in_frame(full_variable_iterator.item, frame)
790 if first_instance == null then return null
792 if full_variable.length <= 1 then return first_instance
794 full_variable_iterator.next
796 if not (first_instance isa MutableInstance and full_variable_iterator.is_ok) then return null
798 return get_variable_in_mutable_instance(first_instance, full_variable_iterator)
801 # Gets a variable 'variable_name
' contained in the frame 'frame
'
802 private fun get_variable_in_frame(variable_name: String, frame: Frame): nullable Instance
804 if variable_name == "self" then
805 if frame.arguments.length >= 1 then return frame.arguments.first
808 var map_of_instances = frame.map
810 for key in map_of_instances.keys do
811 if key.to_s == variable_name then
812 return map_of_instances[key]
819 # Gets an attribute 'attribute_name
' contained in variable 'variable
'
820 fun get_attribute_in_mutable_instance(variable: MutableInstance, attribute_name: String): nullable MAttribute
822 var map_of_attributes = variable.attributes
824 for key in map_of_attributes.keys do
825 if key.to_s.substring_from(1) == attribute_name then
833 # Recursive function, returns the variable described by 'total_chain
'
834 fun get_variable_in_mutable_instance(variable: MutableInstance, iterator: Iterator[String]): nullable Instance
836 var attribute = get_attribute_in_mutable_instance(variable, iterator.item)
838 if attribute == null then return null
842 if iterator.is_ok then
843 var new_variable = variable.attributes[attribute]
844 if new_variable isa MutableInstance then
845 return get_variable_in_mutable_instance(new_variable, iterator)
850 return variable.attributes[attribute]
854 #######################################################################
855 ## Array exploring functions ##
856 #######################################################################
858 # Gets the length of a collection
859 # Used by the debugger, else if we call Collection.length, it returns the capacity instead
860 fun get_collection_instance_real_length(collection: MutableInstance): nullable Int
862 var collection_length_attribute = get_attribute_in_mutable_instance(collection, "length")
864 var real_collection_length: nullable Int = null
866 if collection_length_attribute != null then
867 var primitive_length_instance = collection.attributes[collection_length_attribute]
868 if primitive_length_instance isa PrimitiveInstance[Int] then
869 return primitive_length_instance.val
876 # Processes the indexes of a print array call
877 # Returns an array containing all the indexes demanded
878 fun process_index(index_string: String): nullable Array[Int]
880 var from_end_index = index_string.index_of('.')
881 var to_start_index = index_string.last_index_of('.')
883 if from_end_index != -1 and to_start_index != -1 then
884 var index_from_string = index_string.substring(0,from_end_index)
885 var index_to_string = index_string.substring_from(to_start_index+1)
887 if index_from_string.is_numeric and index_to_string.is_numeric then
888 var result_array = new Array[Int]
890 var index_from = index_from_string.to_i
891 var index_to = index_to_string.to_i
893 for i in [index_from..index_to] do
900 if index_string.is_numeric
902 var result_array = new Array[Int]
904 result_array.push(index_string.to_i)
915 # Gets a collection in a MutableInstance
916 fun get_primary_collection(container: MutableInstance): nullable SequenceRead[Object]
918 var items_of_array = get_attribute_in_mutable_instance(container, "items")
919 if items_of_array != null then
920 var array = container.attributes[items_of_array]
922 if array isa PrimitiveInstance[Object] then
923 var sequenceRead_final = array.val
924 if sequenceRead_final isa SequenceRead[Object] then
925 return sequenceRead_final
933 # Removes the braces '[' ']' in a print array command
934 # Returns an array containing their content
935 fun remove_braces(braces: String): nullable Array[String]
937 var buffer = new Buffer
939 var result_array = new Array[String]
941 var number_of_opening_brackets = 0
942 var number_of_closing_brackets = 0
944 var last_was_opening_bracket = false
948 if last_was_opening_bracket then
952 number_of_opening_brackets += 1
953 last_was_opening_bracket = true
954 else if i == ']' then
955 if not last_was_opening_bracket then
959 result_array.push(buffer.to_s)
961 number_of_closing_brackets += 1
962 last_was_opening_bracket = false
963 else if i.is_numeric or i == '.' then
964 buffer.append(i.to_s)
965 else if not i == ' ' then
970 if number_of_opening_brackets != number_of_closing_brackets then
977 #######################################################################
978 ## Breakpoint placing functions ##
979 #######################################################################
981 # Places a breakpoint on line 'line_to_break
' for file 'file_to_break
'
982 fun place_breakpoint(breakpoint: Breakpoint)
984 if not self.breakpoints.keys.has(breakpoint.file) then
985 self.breakpoints[breakpoint.file] = new HashSet[Breakpoint]
987 if find_breakpoint(breakpoint.file, breakpoint.line) == null then
988 self.breakpoints[breakpoint.file].add(breakpoint)
989 print "Breakpoint added on line {breakpoint.line} for file {breakpoint.file}"
991 print "Breakpoint already present on line {breakpoint.line} for file {breakpoint.file}"
995 #Places a breakpoint that will trigger once and be destroyed afterwards
996 fun process_place_tbreak_fun(parts_of_command: Array[String])
998 var bp = get_breakpoint_from_command(parts_of_command)
1001 bp.set_max_breaks(1)
1002 place_breakpoint(bp)
1008 #######################################################################
1009 ## Breakpoint removing functions ##
1010 #######################################################################
1012 # Removes a breakpoint on line 'line_to_break
' for file 'file_to_break
'
1013 fun remove_breakpoint(file_to_break: String, line_to_break: Int)
1015 if self.breakpoints.keys.has(file_to_break) then
1016 var bp = find_breakpoint(file_to_break, line_to_break)
1019 self.breakpoints[file_to_break].remove(bp)
1020 print "Breakpoint removed on line {line_to_break} for file {file_to_break}"
1025 print "No breakpoint existing on line {line_to_break} for file {file_to_break}"
1028 #######################################################################
1029 ## Breakpoint searching functions ##
1030 #######################################################################
1032 # Finds a breakpoint for 'file
' and 'line
' in the class HashMap
1033 fun find_breakpoint(file: String, line: Int): nullable Breakpoint
1035 if self.breakpoints.keys.has(file)
1037 for i in self.breakpoints[file]
1049 #######################################################################
1050 ## Runtime modification functions ##
1051 #######################################################################
1053 # Modifies the value of a variable contained in a frame
1054 fun modify_in_frame(variable: Instance, value: String)
1056 var new_variable = get_variable_of_type_with_value(variable.mtype.to_s, value)
1057 if new_variable != null
1059 var keys = frame.map.keys
1062 if frame.map[key] == variable
1064 frame.map[key] = new_variable
1070 # Modifies the value of a variable contained in a MutableInstance
1071 fun modify_argument_of_complex_type(papa: MutableInstance, attribute: MAttribute, value: String)
1073 var final_variable = papa.attributes[attribute]
1074 var type_of_variable = final_variable.mtype.to_s
1075 var new_variable = get_variable_of_type_with_value(type_of_variable, value)
1076 if new_variable != null
1078 papa.attributes[attribute] = new_variable
1082 #######################################################################
1083 ## Variable generator functions ##
1084 #######################################################################
1086 # Returns a new variable of the type 'type_of_variable
' with a value of 'value
'
1087 fun get_variable_of_type_with_value(type_of_variable: String, value: String): nullable Instance
1089 if type_of_variable == "Int" then
1090 return get_int(value)
1091 else if type_of_variable == "Float" then
1092 return get_float(value)
1093 else if type_of_variable == "Bool" then
1094 return get_bool(value)
1095 else if type_of_variable == "Char" then
1096 return get_char(value)
1102 # Returns a new int instance with value 'value
'
1103 fun get_int(value: String): nullable Instance
1105 if value.is_numeric then
1106 return int_instance(value.to_i)
1112 # Returns a new float instance with value 'value
'
1113 fun get_float(value:String): nullable Instance
1115 if value.is_numeric then
1116 return float_instance(value.to_f)
1122 # Returns a new char instance with value 'value
'
1123 fun get_char(value: String): nullable Instance
1125 if value.length >= 1 then
1126 return char_instance(value[0])
1132 # Returns a new bool instance with value 'value
'
1133 fun get_bool(value: String): nullable Instance
1135 if value.to_lower == "true" then
1136 return self.true_instance
1137 else if value.to_lower == "false" then
1138 return self.false_instance
1140 print "Invalid value, a boolean must be set at \"true\" or \"false\""
1145 #######################################################################
1146 ## Command listing function ##
1147 #######################################################################
1149 # Lists the commands available when using the debugger
1152 print "\nCommand not recognized\n"
1153 print "Commands accepted : \n"
1154 print "[break/b] line : Adds a breakpoint on line *line_nb* of the current file\n"
1155 print "[break/b] file_name line_nb : Adds a breakpoint on line *line_nb* of file *file_name* \n"
1156 print "[p/print] variable : [p/print] * shows the status of all the variables\n"
1157 print "[p/print] variable[i] : Prints the value of the variable contained at position *i* in SequenceRead collection *variable*\n"
1158 print "[p/print] variable[i..j]: Prints the value of all the variables contained between positions *i* and *j* in SequenceRead collection *variable*\n"
1159 print "[p/print] stack: Prints a stack trace at current instruction\n"
1160 print "Note : The arrays can be multi-dimensional (Ex : variable[i..j][k] will print all the values at position *k* of all the SequenceRead collections contained between positions *i* and *j* in SequenceRead collection *variable*)\n"
1161 print "s : steps in on the current function\n"
1162 print "n : steps-over the current instruction\n"
1163 print "finish : steps out of the current function\n"
1164 print "variable as alias : Adds an alias called *alias* for the variable *variable*"
1165 print "An alias can reference another alias\n"
1166 print "variable = value : Sets the value of *variable* to *value*\n"
1167 print "[d/delete] line_nb : Removes a breakpoint on line *line_nb* of the current file \n"
1168 print "[d/delete] file_name line_nb : Removes a breakpoint on line *line_nb* of file *file_name* \n"
1169 print "trace variable_name [break/print] : Traces the uses of the variable you chose to trace by printing the statement it appears in or by breaking on each use."
1170 print "untrace variable_name : Removes the trace on the variable you chose to trace earlier in the program"
1171 print "kill : kills the current program (Exits with an error and stack trace)\n"
1176 # Traces the modifications of an object linked to a certain frame
1177 private class TraceObject
1179 # Map of the local names bound to a frame
1180 var trace_map: HashMap[Frame, String]
1181 # Decides if breaking or printing statement when the variable is encountered
1182 var break_on_encounter: Bool
1184 init(break_on_encounter: Bool)
1186 trace_map = new HashMap[Frame, String]
1187 self.break_on_encounter = break_on_encounter
1190 # Adds the local alias for a variable and the frame bound to it
1191 fun add_frame_variable(frame: Frame, variable_name: String)
1193 self.trace_map[frame] = variable_name
1196 # Checks if the prompted variable is traced in the specified frame
1197 fun is_variable_traced_in_frame(variable_name: String, frame: Frame): Bool
1199 if self.trace_map.has_key(frame) then
1200 if self.trace_map[frame] == variable_name then
1212 # Breaks automatically when encountering an error
1213 # Permits the injunction of commands before crashing
1214 redef private fun fatal(v: NaiveInterpreter, message: String)
1216 if v isa Debugger then
1217 print "An error was encountered, the program will stop now."
1219 while v.process_debug_command(gets) do end