Minimal Asteroid Debugger Reference Guide
The Minimal Asteroid Debugger is a source code debugger for the Asteroid programming language. It supports the following:
Breakpoints
Single stepping at the source level
Stepping through function calls
Examining contents of variables in the current scope
The commandline interface of the debugger is modeled after debuggers such as the GNU debugger gdb.
Usage
In order to invoke the debugger type,
asteroid -d <FILENAME>
This will start a debugging session of the file <FILENAME>.
Here is an example where <FILENAME> is list.ast,
$ asteroid -d list.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type 'help' for additional information
mad>
The help command will display all available commands with a short description of each.
Here is a table of the available commands in the the debugger,
breakpoints ........................ show all breakpoints
clear .............................. clear all breakpoints
continue ........................... continue execution to next breakpoint
down ............................... move down one stack frame
frame .............................. display current stack frame number
help ............................... display help
list [<num>|*]...................... display <num> (default 4) lines of source code, * displays all lines in file
next ............................... step execution across a nested scope
print <name>[@<num>|<name>]+|* [-v]. print contents of <name>, * lists all vars in scope, recursively access (nested) objects with @, '-v' enables verbose printing of nested data
quit ............................... quit debugger
stack [<num>|* [-v]]................ display runtime stack, list all items in specific frame with an index or all frames with '*', '-v' toggles verbose printing
set [<func>|<line#> [<file>]] ...... set a breakpoint, breakpoints may only be set on valid statements on already loaded files
step ............................... step to next executable statement
trace [<num> [<num>]]............... display runtime stack trace, can specify either the first n frames or all of the frames between the start and end
up ................................. move up one stack frame
where .............................. print current program line
The | means that either the item to the left or to the right of it may be used. Any items enclosed in square brackets
[] are considered optional, meaning that they are not required to be provided in order to the command to be valid.
The + means that any number of the item to its left may be supplied to the command. Items enclosed in angle brackets
<> represent placeholders, indicating that an actual value must be used when executing the command. The text between
the brackets indicates what should be used.
All debugging sessions can be stopped at any time by using the quit command.
Source Code
When running a debugging session, the list command will display some of the source
code for the current file. In the output, the > character at the beginning of a line
indicates the current line to be executed. Any lines beginning with a * are lines with
a breakpoint.
The list command may also be executed with an optional parameter, either some positive integer
or *. When called with a positive integer, list will output the current line and that many
lines above and below that line, or until the beginning or the end of the file. By default,
list will display 4 lines above and below the current line. To display every line in the current file,
use the * argument.
To get only the current line, the where command will display the current file and line number, alongside
the contents of that line. Here are those commands in action,
$ asteroid -d list.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> list
> 1 load system io.
2
3 for i in 1 to 5 do
4 let x = i.
5 io @println x.
mad> list 2
> 1 load system io.
2
3 for i in 1 to 5 do
mad> list *
1 load system io.
2
3 for i in 1 to 5 do
4 let x = i.
5 io @println x.
6 end
7 [EOF]
mad> where
list.ast:1:load system io.
mad> quit
$
Breakpoints
For breakpoints, we can use the set command to set a breakpoint on either a line or
a function definition. When setting breakpoints, only lines containing valid statements or
already loaded functions are allowed to be set. Blank lines and incomplete statements will
be rejected. In addition, breakpoints may only be set on lines and functions defined in files
that have already been loaded. By default, breakpoints will be set in their current file.
If set is called without an argument, a breakpoint will be placed on the current line.
To examine all of the set breakpoints, the command breakpoints will display every breakpoint
in the order that it was set in. The clear command will remove all of the breakpoints. Here is
an example of this behavior,
$ asteroid -d list.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> list
> 1 load system io.
2
3 for i in 1 to 5 do
4 let x = i.
5 io @println x.
mad> set 4
mad> set 2
error: cannot place breakpoints on blank lines
mad> breakpoints
breakpoints:
list.ast:4
mad> list
> 1 load system io.
2
3 for i in 1 to 5 do
* 4 let x = i.
5 io @println x.
mad> clear
mad> breakpoints
breakpoints:
mad> list
> 1 load system io.
2
3 for i in 1 to 5 do
4 let x = i.
5 io @println x.
mad> quit
$
Execution
The Minimal Asteroid Debugger provides multiple commands to allow for a developer to resume and gradually
execute their debugging sessions. The continue command will resume the session until either a breakpoint
is encountered or the session executes the final line of the program. To gradually step through a session,
the next and step commands allow a developer to walk through their program one statement at a time.
When executing step, the debugging session with enter a nested scope and set the next statement to be
executed as the first statement in the new scope. By contrast, next will step over nested scopes and will
set the next statement to be after that scope ends. The following example illustratea the difference between
each of the three commands,
load system io.
function some_func with () do
io @println "Inside a function".
io @println "Leaving the function..."
end
io @println "Outside a function".
some_func ().
io @println "Left the function".
When calling continue, the debugging session will stop on line 4 followed by line 10,
$ asteroid -d nested_scopes.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> list
> 1 load system io.
2
3 function some_func with () do
4 io @println "Inside a function".
5 io @println "Leaving the function..."
mad> set 4
mad> set 10
mad> continue
Outside a function
reached breakpoint (nested_scopes.ast:4)
1 load system io.
2
3 function some_func with () do
> 4 io @println "Inside a function".
5 io @println "Leaving the function..."
6 end
7
8 io @println "Outside a function".
mad> continue
Inside a function
Leaving the function...
reached breakpoint (nested_scopes.ast:10)
7
8 io @println "Outside a function".
9 some_func ().
> 10 io @println "Left the function".
11 [EOF]
mad> continue
Left the function
stopping MAD
mad> quit
$
When calling step, the debugging session will enter any scope, including load statements and function calls,
$ asteroid -d nested_scopes.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> list
> 1 load system io.
2
3 function some_func with () do
4 io @println "Inside a function".
5 io @println "Leaving the function..."
mad> step
> 1 load system io.
2
3 function some_func with () do
4 io @println "Inside a function".
5 io @println "Leaving the function..."
mad> step
entering module io
> 1 load system io.
2
3 function some_func with () do
4 io @println "Inside a function".
5 io @println "Leaving the function..."
mad> step
10 ------------------------------------------------------------------
11
12 ------------------------------------------------------------------
> 13 structure "MAD.txt" with
14 ------------------------------------------------------------------
15 -- Basic file i/o
16
17 data fd.
mad> continue
Outside a function
Inside a function
Leaving the function...
Left the function
stopping MAD
mad> quit
$
Calling next only stops on valid statements within the current scope, never entering a lower scope.
$ asteroid -d nested_scopes.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> next
> 1 load system io.
2
3 function some_func with () do
4 io @println "Inside a function".
5 io @println "Leaving the function..."
mad> next
1 load system io.
2
> 3 function some_func with () do
4 io @println "Inside a function".
5 io @println "Leaving the function..."
6 end
7
mad> next
5 io @println "Leaving the function..."
6 end
7
> 8 io @println "Outside a function".
9 some_func ().
10 io @println "Left the function".
11 [EOF]
mad> next
Outside a function
6 end
7
8 io @println "Outside a function".
> 9 some_func ().
10 io @println "Left the function".
11 [EOF]
mad> next
Inside a function
Leaving the function...
7
8 io @println "Outside a function".
9 some_func ().
> 10 io @println "Left the function".
11 [EOF]
mad> next
Left the function
stopping MAD
mad> quit
$
Data
During the execution of an Asteroid program, all defined data within a given scope may be displayed using
the print command. This command accepts either the name of a structure, a function, a variable, or *,
which will output every defined item within the currently viewed scope.
When displaying a variable that holds a list, a tuple, a string, or an instance of some structure, print will
allow the display of specific data at a given index or data member. Like in Asteroid, this would be accomplished
by adding @ after the name, followed by either an integer of at least 0 (for lists, tuples, and strings) or some
other name (for instances of structures). For highly nested data, these access patterns may be infinitely added,
provided that the pattern is both valid and accesses a list, tuple, string, or structure instance.
Additionally, the -v option may be added to print commands. When called on lists, tuples, or instances of structures,
every data member will be printed on an individual line with the depth of a specific item indicated by the amount of whitespace
to the left of the line. Structure instances will display every unique data member and method on a new line, with the corresponding
value separated by a colon on the same line. Here is a demonstration with the following example,
load system io.
structure Foo with
data bar.
data baz.
function quux with () do
io @println "Calling quux".
end
end
let lst = [1, [2, 3], (4, 5, "678"), Foo(9, ["ten", "eleven"])].
io @println lst.
This is the debugging session,
$ asteroid -d nested_data.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> set 13
mad> continue
reached breakpoint (nested_data.ast:13)
10 end
11
12 let lst = [1, [2, 3], (4, 5, "678"), Foo(9, ["ten", "eleven"])].
> 13 io @println lst.
14 [EOF]
mad> print *
Error: (struct...)
Exception: (struct...)
len: (function ...)
-- ...
io: (module...)
Foo: (struct...)
lst: [1,[2,3],(4,5,678),Foo(9,[ten,eleven])]
mad> print lst
lst: [1,[2,3],(4,5,678),Foo(9,[ten,eleven])]
mad> print lst@0
lst@0: 1
mad> print lst@1
lst@1: [2,3]
mad> print lst@1@0
lst@1@0: 2
mad> print lst@3
lst@3: Foo(9,[ten,eleven])
mad> print lst@3@baz
lst@3@baz: [ten,eleven]
mad> print lst@3@baz@1
lst@3@baz@1: eleven
mad> print lst@3@baz@1@3
lst@3@baz@1@3: v
mad> print lst -v
lst: [
1,
[
2,
3
],
(
4,
5,
678
),
Foo(
bar: 9,
baz: [
ten,
eleven
],
quux: (function ...)
)
]
mad> quit
$
Stack Traces
The Minimal Asteroid Debugger allows developers to switch between any currently loaded scope. To determine
which scope is currently viewed, the frame command will display the number of the current scope.
The up and down commands allow developers to move to a higher or lower scope respectively, or until
hitting either the toplevel scope or the most recently defined scope respectively.
To get a more detailed view of the stack, the trace command will display every scope from the currently
viewed scope until the toplevel scope. trace may also accept some positive integer, which will display that
number of scopes beginning with the currently viewed scope. To display a specific range of scope, trace may
accept two positive integers indicating the lowest and highest scope numbers to be displayed.
Since Asteroid is a statically scoped language, the data in a particular scope may be different between scopes.
To examine this difference, the stack command may be used to examine the data of a particular scope. By default,
stack will behave identically to trace when called with no arguments. However, stack can accept an optional
argument either representing a specific scope number or *. When called with a specific scope number, stack
will display every defined item in that scope. The argument * will display every defined item in every defined scope.
Similar to print, the -v argument allows for the verbose printing of any nested data and can be used with any of
the previously defined arguments.
The following is an demonstration using the example program,
load system io.
function factorial
with 0 do
1.
with n:%integer do
n * factorial (n - 1).
end
io @println (factorial 4).
This is the demonstration,
$ asteroid -d factorial.ast
Minimal Asteroid Debugger 0.0.2
(c) University of Rhode Island
type "help" for additional information
mad> set 5
mad> continue
reached breakpoint (factorial.ast:5)
2
3 function factorial
4 with 0 do
> 5 1.
6 with n:%integer do
7 n * factorial (n - 1).
8 end
9
mad> frame
you are looking at frame #0
mad> trace
Runtime stack trace (most recent call first):
frame #0: factorial.ast @factorial
frame #1: factorial.ast @factorial
frame #2: factorial.ast @factorial
frame #3: factorial.ast @factorial
frame #4: factorial.ast @factorial
frame #5: factorial.ast @<toplevel>
mad> stack
Runtime stack (most recent call first):
frame #0: factorial.ast @factorial
frame #1: factorial.ast @factorial
frame #2: factorial.ast @factorial
frame #3: factorial.ast @factorial
frame #4: factorial.ast @factorial
frame #5: factorial.ast @<toplevel>
mad> down
error: no such frame
mad> up
you are looking at frame #1
mad> up
you are looking at frame #2
mad> trace
Runtime stack trace (most recent call first):
frame #2: factorial.ast @factorial
frame #3: factorial.ast @factorial
frame #4: factorial.ast @factorial
frame #5: factorial.ast @<toplevel>
mad> trace 3
Runtime stack trace (most recent call first):
frame #2: factorial.ast @factorial
frame #3: factorial.ast @factorial
frame #4: factorial.ast @factorial
mad> trace 0 3
Runtime stack trace (most recent call first):
frame #0: factorial.ast @factorial
frame #1: factorial.ast @factorial
frame #2: factorial.ast @factorial
mad> stack 0
frame #0: factorial.ast @factorial
mad> stack 3
frame #3: factorial.ast @factorial
__AST__TEMP81__: 3
n: 3
mad> stack 4
frame #4: factorial.ast @factorial
__AST__TEMP81__: 4
n: 4
mad> stack *
Runtime stack (most recent call first):
frame #2: factorial.ast @factorial
__AST__TEMP81__: 2
n: 2
frame #3: factorial.ast @factorial
__AST__TEMP81__: 3
n: 3
frame #4: factorial.ast @factorial
__AST__TEMP81__: 4
n: 4
frame #5: factorial.ast @<toplevel>
Error: (struct...)
Exception: (struct...)
-- ...
io: (module...)
factorial: (function ...)
mad> continue
24
stopping MAD
mad> quit
$