Repository: asteroid-lang/asteroid (language: python)

Issue:
Document the Python API
Document the Asteroid-Python API including,

- Escape
- Foreign
- Interpreter internal API to the symbol table etc
Debugger
This is a preliminary pull request so that other folks can have an easy way to check out the new debugger code. 

Engineering/design/* suggestions/comments/* are all very much appreciated and welcome. Luckily, everything is fairly independent and loosely coupled so I can change basically anything without too much difficulty.

It should be noted that I've worked on this in a vacuum for the entire summer. So, there's likely dozens of very low hanging bugs for us to squash 😄.

You can run the debugger with `asteroid --adb <FILENAME>` (`python asteroid/__init__.py --adb <FILENAME>). There are some basic docs in `adb/docs/`. I kept them fairly small so that if we make major changes now I can easily change things. 

There is an experimental `explicit` mode that exposes pattern matching. Right now it is *very* experimental and that should be noted on any release documents. My plan for the semester is to beef it up and improve the debugger overall. 

RE: How do these changes affect asteroid development?
> For most situations, the inclusion of the debugger does not change the development experience. Following what I've done in other nodes and unify clauses should be pretty straight forward. There's documentation at the bottom of the `walk.py` file for all of the functions I used to get the debugger's behavior working within the walker. 
Asteroid pure functional mode
Introduce a switch, e.g. -F, that puts Asteroid into a purely functional mode. No procedural constructs like loop- and if-statements will be allowed in user programs/modules.

This is an extension purely aimed at instructional support (for 301) I don't think there is any value of this with writing production level code.
command line switch not recognized without filename present
The asteroid interpreter does not recognize command line switches if there is no input file present, e.g.
```
lutz$ dbgasteroid -F
unknown file -F
```
but
```
lutz$ dbgasteroid -F break.ast 
error: break.ast: 5: loop is not supported in functional mode
lutz$
```
repl ignores mode flags such as -r and -F
repl ignores mode flags such as -r and -F, these flags need to be carried into the repl and passed to the interpreter.


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 0ee199b2..5cf73270 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,19 @@ be found at the website [asteroid-lang.org](https://asteroid-lang.org).
 Documentation on Asteroid can be found at
 [asteroid-lang.readthedocs.io](https://asteroid-lang.readthedocs.io).
 
+## New in Release 1.1.0
+
+* An experimental implementation of an interactive debugger that supports debugging pattern matching and in 
+  particular, pattern matching with first-class patterns.
+  
+* A Python API allowing the developer to call the Asteroid interpreter from within a Python program and also embed
+  Python code in an Asteroid program.
+
+* Eliminated the `stride` notation in list comprehensions and replaced it with the `step` notation.
+
+* Lots of bug fixes.
+
+
 ## New in Release 1.0.0
 
 * Interpreter line-editing features similar to Python include interactive editing, history substitution and code completion on systems that support the GNU Readline library.
diff --git a/asteroid/__init__.py b/asteroid/__init__.py
old mode 100644
new mode 100755
index 54084cd6..0166f07e
--- a/asteroid/__init__.py
+++ b/asteroid/__init__.py
@@ -11,6 +11,7 @@
 from asteroid.interp import interp
 from asteroid.repl import repl
 from asteroid.version import VERSION
+from asteroid.adb import adb
 
 def display_help():
     print("** Asteroid Version {} **".format(VERSION))
@@ -18,33 +19,44 @@ def display_help():
     print("usage: asteroid [-<switch>] <input file>")
     print("")
     print("command line flags:")
-    print(" -s    enable symbol table dump")
-    print(" -t    AST dump")
-    print(" -v    version")
-    print(" -w    disable tree walk")
-    print(" -z    generate pstats")
-    print(" -p    disable prologue")
-    print(" -h    display help")
-    print(" -r    disable redundant pattern detector")
-    print(" -e    show Python exceptions")
+    print(" -s          enable symbol table dump")
+    print(" -t          AST dump")
+    print(" -v          version")
+    print(" -w          disable tree walk")
+    print(" -z          generate pstats")
+    print(" -p          disable prologue")
+    print(" -h          display help")
+    print(" -r          disable redundant pattern detector")
+    print(" -e          show Python exceptions")
+    print(" -F          functional mode")
+    print(" -g, --adb   run program through debugger")
 
 def main():
     # defaults for the flags - when the flag is set on the command line
     # it simply toggles the default value in this table.
     flags = {
-        '-s' : False, # symbol table dump flag
-        '-t' : False, # AST dump flag
-        '-v' : False, # version flag
-        '-w' : True,  # tree walk flag
-        '-z' : False, # generate pstats flag
-        '-p' : True,  # prologue flag
-        '-h' : False, # display help flag
-        '-r' : True,  # redundant pattern dectector
-        '-e' : False, # show full exceptions
+        '-s' : False,  # symbol table dump flag
+        '-t' : False,  # AST dump flag
+        '-v' : False,  # version flag
+        '-w' : True,   # tree walk flag
+        '-z' : False,  # generate pstats flag
+        '-p' : True,   # prologue flag
+        '-h' : False,  # display help flag
+        '-r' : True,   # redundant pattern dectector
+        '-e' : False,  # show full exceptions
+        '-F' : False,  # functional mode
+        '--adb': False, # debugger flag 
+        '-g': False    # Short debugger flag
     }
 
     flag_names = list(flags.keys())
 
+    if len(sys.argv) == 1:
+        repl(redundancy=flags['-r'],
+             prologue=flags['-p'],
+             functional_mode=flags['-F'])
+        sys.exit(0)
+
     for fl in sys.argv:
         if fl[0] != '-':
             continue
@@ -53,10 +65,6 @@ def main():
             sys.exit(0)
         flags[fl] = not flags[fl]
 
-    if len(sys.argv) == 1:
-        repl()
-        sys.exit(0)
-
     if flags['-h']:
         display_help()
         sys.exit(0)
@@ -67,24 +75,49 @@ def main():
 
     input_file = sys.argv[-1]
 
+    if input_file[0] == '-':
+        if flags['--adb']:
+            print("Please provide a file to debug")
+            sys.exit(1)
+        repl(redundancy=flags['-r'],
+             prologue=flags['-p'],
+             functional_mode=flags['-F'])
+        sys.exit(0)
+
     if not os.path.isfile(input_file):
         print("unknown file {}".format(input_file))
         sys.exit(0)
 
+    if flags['--adb'] or flags['-g']:
+        # Create a new debugger
+        db = adb.ADB()
+
+        # Set the debugger's internal interpretation options
+        db.interp_options = {
+            'redundancy': flags['-r'],
+            'prologue': flags['-p'],
+            'functional_mode': flags['-F'],
+            'exceptions': flags['-e'],
+        }
+
+        db.run(input_file)
+        sys.exit(0)
+    
     f = open(input_file, 'r')
     input_stream = f.read()
     f.close()
 
     # execute interpreter
     interp_object = \
-    '''interp(input_stream=input_stream,
-           input_name = input_file,
+    '''interp(program=input_stream,
+           program_name = input_file,
            tree_dump=flags['-t'],
            do_walk=flags['-w'],
            symtab_dump=flags['-s'],
            exceptions=flags['-e'],
            redundancy=flags['-r'],
-           prologue=flags['-p'])'''
+           prologue=flags['-p'],
+           functional_mode=flags['-F'])'''
 
     if flags['-z']:
         # generates pstats into the file 'pstats'
diff --git a/asteroid/adb/__init__.py b/asteroid/adb/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/asteroid/adb/adb.py b/asteroid/adb/adb.py
new file mode 100644
index 00000000..5c355f09
--- /dev/null
+++ b/asteroid/adb/adb.py
@@ -0,0 +1,976 @@
+"""
+The Asteroid Debugger
+"""
+
+from asteroid.repl import repl
+from asteroid.adb.command import DebuggerParser
+from asteroid.interp import interp, load_prologue
+from asteroid.state import dump_trace, state
+from asteroid.support import term2string, map2boolean
+
+from asteroid.walk import function_return_value
+
+# Used for showing full exceptions
+import traceback
+
+class ADB:
+    """
+    This class implements the behavior and state managment for the
+    asteroid debugger
+    """
+    def __init__(self):
+        # List of options to run the interpreter under
+        self.interp_options = {
+            'redundancy': False,
+            'prologue': True,
+            'functional_mode': False,
+            'exceptions': False,
+        }
+
+        # Table of breakpoints and conditions
+        self.breakpoints = {}
+
+        #############################
+        # Execution control table
+        self.exc = {
+            'CONTINUE': False,
+            'STEP':     False,
+            'RETURN':   False,
+            'UNTIL':    False,
+            'NEXT':     True,
+        }
+
+        #############################
+        # A copy of the most recent return value
+        self.retval = None
+
+        #############################
+        # If our program is executing at the top level
+        self.top_level = True
+
+        # Explicit mode is the verbose mode where more information
+        # about the computation is detailed
+        self.explicit_enabled = False
+
+        #############################
+        # The current level of tabulation
+        self.tab_level = 0
+
+        #############################
+        # Debugger's internal lineinfo
+        # We maintain this separate from the state's lineinfo
+        # to help keep track of the currently executing line.
+        # Between things like imports or function calls to
+        # other modules, the state's lineinfo can fall slightly
+        # behind.
+        self.lineinfo = None
+
+        #############################
+        # Name-content file dictionary
+        self.program_text = {}
+        
+        # The original filename
+        self.filename = None
+        
+        #############################
+        # The parser for incoming commands
+        self.dbgp = DebuggerParser()
+        
+        #############################
+        # Dictionary of macros
+        self.macros = {
+            # A clear screen macro
+            'clear': [('EVAL', 'escape "import os;os.system(\'clear\') "')]
+        }
+
+        #############################
+        # The queue of commands being executed
+        self.command_queue = []
+
+        #############################
+        # Stack frame information
+        self.config_offset = 0          # The index of the current config we're using
+        self.original_config = None     # The original config we started with (before moving between frames)
+        self.original_lineinfo = None   # The original lineinfo we started with (before moving between frames)
+
+        #############################
+        # Old lineinfo to be stored when until command is used
+        self.old_lineinfo = None
+
+    def reset_defaults(self):
+        """
+        Resets the debugger's default config
+        """
+        # Reset defaults
+        self.exc['CONTINUE'] = False
+        self.exc['STEP'] = False
+        self.exc['NEXT'] = True
+        self.exc['RETURN'] = False
+        self.exc['UNTIL'] = False
+
+        self.top_level = True
+        self.explicit_enabled = False
+        self.old_lineinfo = None
+
+    def reset_config(self):
+        """
+        Reset the symbol table's original config
+        """
+        if self.original_config:
+            state.symbol_table.set_config(self.original_config)
+            self.original_config = None
+            self.original_lineinfo = None
+            self.config_offset = 0
+
+    def make_tab_level(self):
+        """
+        Make the tab level for nested messaging
+        """
+        return self.tab_level*"|   "
+
+    def message_explicit(self, message, level = "primary"):
+        """
+        Sends a message in explicit mode
+
+        Explicit messaging only gets shown in two scenarios if explicit mode is enabled:
+            1. The user is NOT continuing and they're in the same file
+            2. The user is stepping through a function call
+        """
+        if self.explicit_enabled and \
+            (not self.exc['CONTINUE'] and self.lineinfo[0] == self.filename) or \
+            (self.exc['STEP']):
+
+            tl = self.make_tab_level()
+            if level == 'primary':
+                print("{}- {}".format(tl, message))
+            elif level == 'secondary':
+                print("{} ** {}".format(tl, message))
+            else:
+                print("{}  * {}".format(tl, message))
+
+    def message(self, message):
+        """
+        print a formatted message through the debugger
+        """
+        print("----- {} -----".format(message))
+
+    def handle_run_exception(self, e):
+        # Handle exceptions from the interpretation session
+        (module, lineno) = state.lineinfo
+        print("\nERROR: {}: {}: {}".format(module, lineno, e))
+        if self.interp_options['exceptions']:
+            print(traceback.format_exc())   
+        
+        # Set out lineinfo here to be sure that the file is in
+        # our program_text dictionary
+        self.set_lineinfo( (module, lineno) )
+        
+        print("    ==>> " + self.program_text[module][lineno - 1].strip())
+        print()
+        self.message("Error occured, session will restart after commands")
+
+    def run(self, filename):
+        """
+        This function runs the given filename through the asteroid debugger
+        """
+        
+        # Set our primary filename
+        self.filename = filename
+
+        # Open and read the file
+        f = open(filename, 'r')
+        input_stream = f.read()
+        f.close()
+
+        from asteroid.adb.version import VERSION
+        print("Welcome to ADB version {}.".format(VERSION))
+        print("Type \"help\" to recieve help.")
+        print("ADB is experimental and under active development")
+        print("Report any suggestions or bugs to https://github.com/asteroid-lang/")
+        print()
+
+        # Main debug loop
+        while True:
+            try:
+                # Reset the debugging status
+                import asteroid.walk
+                asteroid.walk.debugging = False
+
+                # Reinitialize the state and reload the prologue
+                state.initialize()
+
+                # Load the prologue if user requests
+                if self.interp_options['prologue']:
+                    load_prologue()
+
+                # Interpret our file
+                interp(input_stream,
+                    program_name = filename,
+
+                    # If we wanted to load the prologue, we already would have
+                    prologue=False,
+
+                    # Don't re-initialize the state
+                    initialize_state=False,
+
+                    # User options
+                    redundancy=self.interp_options['redundancy'],
+                    functional_mode=self.interp_options['functional_mode'],
+
+                    # Set exceptions so we can wrap them
+                    exceptions=True,
+                    debugger=self)
+
+                asteroid.walk.debugging = True
+                
+                # Give us one final tick before restarting
+                # This gives us one last tick before EOF is reached
+                self.set_lineinfo( (self.filename, len(self.program_text[self.filename])) )
+                self.tick()
+                print()
+
+                # Restart session
+                self.message("End of file reached, restarting session")
+                self.reset_defaults()
+            
+            except (EOFError, KeyboardInterrupt):
+                break;
+
+            except Exception as e:
+                # Handle the runtime exception
+                self.handle_run_exception(e)
+
+                # One last tick so the user can play around in
+                # the error scope/line
+                try:
+                    self.tick()
+                except (EOFError, KeyboardInterrupt):
+                    # If the user tries to exit with CTRL+C/D, exit
+                    break;
+                
+                # Reset the debugger's default state
+                self.message("Session restarted")
+                self.reset_defaults()
+                continue
+
+    def has_breakpoint_here(self):
+        """
+        Check if the user has set a breakpoint at the current line
+        """
+        # Condition 1: Is there a breakpoint at this line?
+        breakpoint_at_line = (self.lineinfo[1] in self.breakpoints)
+
+        # Condition 2, are we in the original file (TODO: Expand this)
+        in_same_file = (self.lineinfo[0] == self.filename)
+
+        # Preliminary check
+        if not (breakpoint_at_line and in_same_file):
+            return False
+        
+        # Get the break condition for this breakpoint
+        break_cond = self.breakpoints.get(self.lineinfo[1])
+
+        # Assume the break condition is true until proven false
+        break_cond_met = True
+
+        # If there's a break condition
+        if break_cond:
+
+            # Save our old lineinfo/explicit state
+            old_lineinfo = self.lineinfo
+            old_explicit = self.explicit_enabled
+            self.explicit_enabled = False
+
+            old_state_lineinfo = state.lineinfo
+
+            import asteroid.walk
+            asteroid.walk.debugging = False
+            # interpret the break conition
+            try:
+                interp(break_cond,
+                    program_name = "<COMMAND>",
+                    prologue=False,
+                    initialize_state=False,
+
+                    redundancy=self.interp_options['redundancy'],
+                    functional_mode=self.interp_options['functional_mode'],
+
+                    debugger=None,
+                    exceptions=True
+            )
+
+            # If an error occurs in the break condition, show the error
+            except Exception as e:
+                print("Breakpoint condition error: {}".format(e))
+
+            # Else, get the value of the expression calculated.
+            else:
+                break_cond_met = map2boolean(function_return_value[-1])[1]
+
+            asteroid.walk.debugging = True
+
+            # Reenable everything
+            self.explicit_enabled = old_explicit
+            self.set_lineinfo(old_lineinfo)
+
+            # Reset the state's internal lineinfo
+            state.lineinfo = old_state_lineinfo
+
+        # If there's no break cond, then by default it is true
+        else:
+            break_cond_met = True
+
+        # Return the state of the break condition
+        return break_cond_met
+
+    def set_top_level(self, tl):
+        """
+        Set our flag that tells the debugger if it's at the top level of a program or not
+
+        We use this to get the `next` command working
+        """
+        self.top_level = tl
+
+    def set_lineinfo(self, lineinfo):
+        """
+        Set the debugger's internal lineinfo
+        """
+        from os.path import exists
+
+        self.lineinfo = lineinfo
+        
+        # If the program text isn't already loaded and the file actually exists
+        # (isn't a stream line <input> or <command>)
+        if lineinfo[0][0] == '<' and lineinfo[0][-1] == '>':
+            return
+
+        if not self.program_text.get(lineinfo[0]) and exists(lineinfo[0]):
+            with open(lineinfo[0], "r") as f:
+                self.program_text[lineinfo[0]] = f.readlines()
+            
+            # Always add an EOF specifier
+            self.program_text[lineinfo[0]].append("[EOF]\n")
+
+    def print_given_line(self, lineinfo, header=True):
+        """
+        print the current line nicely
+        """
+
+        # Get the program text
+        pt = self.program_text[lineinfo[0]]
+
+        # Isolate the current line
+        prog_line = pt[lineinfo[1] - 1][:-1].strip()
+
+        outline = ""
+
+        # The current call stack
+        call_stack = [s[-1] for s in state.trace_stack][1:]
+
+        if header:
+            # Format it nicely
+            outline =  ("[" + lineinfo[0] + " (" + str(lineinfo[1]) + ")]")
+
+            # Display the call stack
+            if len(call_stack) > 0 and (self.config_offset < len(call_stack)):
+                outline += " ("
+
+                offset = -self.config_offset - 1
+                for c in call_stack[:offset]:
+                    outline += c + "->"
+                outline += call_stack[offset] + ")"
+
+        # If the line is empty don't bother showing it
+        if prog_line != "" and header:
+            outline += ("\n-->> " + prog_line)
+        elif prog_line != "" and not header:
+            outline += ("    " + prog_line)
+        print(outline)
+
+    def list_breakpoints(self):
+        """
+        List the breakpoints and their conditions
+        """
+        self.message("Breakpoints")
+        
+        # For eaxch breakpoint
+        for b in self.breakpoints:
+
+            # Get the condition
+            c = self.breakpoints[b]
+
+            # print the breakpoint number and condition if available
+            print("* {} {}".format(
+                b, ": " + c if c else ''))
+
+    def list_program(self, relative=False):
+        """
+        List the program contents
+        """
+        self.message("Program Listing")
+
+        # Get the program text for the current file
+        pt = self.program_text[self.lineinfo[0]]
+
+        # Length around the current line to display if relative
+        length = 4
+
+        # Relative offset for the file listing
+        start = 0
+
+        if relative:
+            lineno = self.lineinfo[1]
+
+            # Compute the start and end of relative listing
+            start = (lineno - length) if lineno >= length else 0
+            end = lineno + length if lineno < len(pt) - 2 else len(pt)
+
+            # Set the program text to the slice between start and end
+            pt = pt[start:end]
+
+        # Start of line is blank by default
+        start_of_line = "  "
+
+        # GO through each line in the program text
+        for ix, l in enumerate(pt):
+
+            # If the offset line number is in breakpoints
+            if ix+1+start in self.breakpoints:
+                # Set the special start of line
+                start_of_line = "* "
+
+            # If the offset linenumber is the current line
+            if self.lineinfo[1] == ix+1+start:
+                # Set the special start of line
+                start_of_line = "> "
+
+            # print the given line
+            print(start_of_line, ix+1+start, l[:-1])
+
+            # Reset the start of line
+            start_of_line = "  "
+
+    def set_exc(self, step=False, cont=False, next=False):
+        """
+        Set the debugger movement configuration
+        """
+        self.exc['STEP'] = step
+        self.exc['CONTINUE'] = cont
+        self.exc['NEXT'] = next
+
+    def display_macros(self):
+        """
+        Displays all currently active macros
+        """
+        for m in self.macros:
+            print("* {} : {}".format(
+                m, self.macro_to_string(m)))
+    
+    def macro_to_string(self, macro_name):
+        contents = self.macros[macro_name]
+
+        outstr = ""
+
+        for cmd in contents:
+            if cmd == ('MACRO',):                      outstr += 'macro'
+            elif cmd[0] == 'EVAL':                     outstr += 'eval("{}")'.format(cmd[1])
+            elif cmd == ('BANG', ):                    outstr += '!'
+            elif cmd[0] == 'HELP':                     outstr += 'help {}'.format(cmd[1])
+            elif cmd == ('UP',):                       outstr += '<'
+            elif cmd == ('DOWN',):                     outstr += '>'
+            elif cmd == ('WHERE',):                    outstr += 'where'
+            elif cmd == ('LONGLIST',):                 outstr += 'longlist'
+            elif cmd == ('LIST',):                     outstr += 'list'
+            elif cmd == ('RETVAL',):                   outstr += '__retval__'
+            elif cmd[0] ==  'UNTIL':                   outstr += 'until{}'.format((" "+cmd[1]) if cmd[1] else "")
+            elif cmd == ('RETURN',):                   outstr += 'return'
+            elif cmd[0] == 'EXPLICIT':                 outstr += 'explicit {}'.format(
+                cmd[1] if cmd[1] else "")
+            elif cmd == ('STEP', ):                    outstr += 'step'
+            elif cmd == ('CONTINUE', ):                outstr += 'continue'
+            elif cmd == ('NEXT', ):                    outstr += 'next'
+
+            # TODO: (OWM) print out conditions for these
+            elif cmd[0] == 'BREAK':                    outstr += "break {}".format(
+                ' '.join([str(n) for n in cmd[1]]))
+            elif cmd == 'DELETE':                      outstr += "delete {}".format(
+                ' '.join([str(n) for n in cmd[1]]))
+            
+            elif cmd[0] == 'NAME':                     outstr += str(cmd[1])
+            elif cmd == ('QUIT', ):                    outstr += 'quit'
+            elif cmd == ('NOOP', ):                    outstr += 'noop'
+
+            outstr += '; '
+
+        return outstr
+
+    def set_new_macro(self, name, l):
+        """
+        Sets a new macro
+        """
+        self.macros[name] = l
+        self.message("Macro {}".format(name))
+
+    def do_eval_command(self, value):
+        """
+        Evaluates a given value
+        """
+        # Save the old lineinfo and explicit state
+        old_lineinfo = self.lineinfo
+        old_explicit = self.explicit_enabled
+        old_state_lineinfo = state.lineinfo
+        
+        # Set the explicit state to false
+        self.explicit_enabled = False
+        
+        # Set the debugging flag to false
+        import asteroid.walk
+        asteroid.walk.debugging = False
+        
+        # Run the asteroid code
+        try:
+            interp(value,
+                program_name = "<EVAL>",
+                prologue=False,
+                initialize_state=False,
+
+                redundancy=self.interp_options['redundancy'],
+                functional_mode=self.interp_options['functional_mode'],
+
+                debugger=None,
+                exceptions=True)
+
+        except Exception as e:
+            print("Command error: {}".format(e))
+
+        else:
+            # Check if there's actually a return value in
+            # the register
+            if function_return_value[-1]:
+                # Get the last return value (type, value)
+                ret = function_return_value[-1]
+
+                # If it isn't none, print out the value
+                if ret[1] != None:
+                    print(term2string(ret))
+
+        # Reset debugging state
+        asteroid.walk.debugging = True
+        
+        # Reset explicit mode and lineinfo
+        self.explicit_enabled = old_explicit
+        self.set_lineinfo(old_lineinfo)
+        
+        # Reset the state's internal lineinfo
+        state.lineinfo = old_state_lineinfo
+
+    def do_repl_command(self):
+        """
+        Runs a repl in the current stack frame
+        """
+        # Keep our lineinfo, and explicit state. Disable explicit state
+        old_lineinfo = self.lineinfo
+        old_explicit = self.explicit_enabled
+        self.explicit_enabled = False
+
+        # Save the *state*'s old lineinfo
+        old_state_lineinfo = state.lineinfo
+
+        # Turn off debugging
+        import asteroid.walk
+        asteroid.walk.debugging = False
+        
+        # Run the repl
+        repl(
+            new=False,
+            prologue=False,
+            redundancy=self.interp_options['redundancy'],
+            functional_mode=self.interp_options['functional_mode']
+            )
+
+        # Restore debugging flag and give state its old lineinfo
+        asteroid.walk.debugging = True
+        state.lineinfo = old_state_lineinfo
+
+        # Reenable explicit and reset lineinfo
+        self.explicit_enabled = old_explicit
+        self.set_lineinfo(old_lineinfo)
+
+    def do_help_command(self, name):
+        """
+        Lists help options and prints help info
+        """
+        from asteroid.adb.help import command_description_table
+        
+        # If a command name is supplied
+        if name:
+            # Get the command description for that name
+            help_msg = command_description_table.get(name)
+            # if there's a description, print the info
+            if help_msg:
+                self.message("Info for {}".format(name))
+                print(help_msg)
+            # Else, print an error
+            else:
+                self.message("Unknown command for help: {}".format(
+                    name
+                ))
+        
+        # If no command is supplied, then just print out the default command menu
+        else:
+            print("Type 'help NAME' to get help for a command")
+            print("Available commands:")
+            for c in command_description_table:
+                print("* {}".format(c))
+
+    def move_frame_up(self):
+        """
+        Moves the context to the next higher stack frame
+        """
+        if self.config_offset == len(state.trace_stack) - 1:
+            self.message("At topmost frame")
+        else:
+            self.config_offset += 1
+            # We're at the bottommost frame and want to go up, but need
+            # to save the original config
+            if self.original_config == None:
+                self.original_config = state.symbol_table.get_config()
+                self.original_lineinfo = state.lineinfo
+            
+            # Get the associated module and line for this frame
+            (module, line, _) = state.trace_stack[-self.config_offset]
+            
+            # Set the config
+            state.symbol_table.set_config(
+                state.symbol_table.saved_configs[-self.config_offset]
+            )
+
+            self.set_lineinfo( (module, line) )
+            self.print_given_line(self.lineinfo)
+
+    def move_frame_down(self):
+        """
+        Moves the context to the next lowest stack frame
+        """
+        stack = state.trace_stack
+        
+        if self.config_offset == 0:
+            self.message("At bottommost frame")
+        
+        else:
+            self.config_offset -= 1
+            bottom_level = (self.config_offset == 0)
+        
+            if bottom_level:
+                if self.original_config:
+                    state.symbol_table.set_config(self.original_config)
+                    self.set_lineinfo( (self.original_lineinfo) )
+            else:
+                # We're at the bottommost frame and want to go up, but need
+                # to save the original config
+                (module, line, _) = state.trace_stack[-self.config_offset]
+                
+                state.symbol_table.set_config(
+                    state.symbol_table.saved_configs[-self.config_offset])
+
+                self.set_lineinfo( (module, line) )
+                
+            self.print_given_line(self.lineinfo)
+
+    # This function is super spaghetti but the behavior is complicated
+    def do_where_command(self):
+        """
+        Displays a list of available frames and shows the user
+        where they currently are
+        """
+        self.message("Available Frames")
+        stack_copy = state.trace_stack[1:].copy()
+
+        # The call stack
+        call_stack = [s[-1] for s in state.trace_stack][1:]
+
+        # If we're inside of some scope, append a "bottom" to the stack copy
+        if len(call_stack) > 0:
+            stack_copy.append((*state.lineinfo, "<bottom>"))
+        
+        start_of_line = "*"
+        
+        # If we're just at the top level, just note that
+        if len(stack_copy) == 0:
+            print("-> <toplevel>")
+        
+        # For each list in the stack
+        for i, s in enumerate(stack_copy):
+            # There's only the top level
+            if len(stack_copy) == 1:
+                start_of_line = ">"
+        
+            # We're at the bottom of the stack
+            elif (self.config_offset == 0 and len(stack_copy) > 0) and i == len(stack_copy) - 1:
+                start_of_line = ">"
+        
+            # We're traversing frames
+            elif self.config_offset != 0:
+                if i == (len(stack_copy) - self.config_offset) - 1:
+                    start_of_line = ">"
+
+            # Bottom of stack
+            if s[2] == "<bottom>":
+                print("{} {} {}".format(start_of_line, s[0], s[1]))
+                self.print_given_line( (s[0], s[1]) , header=False)
+        
+            else:
+                print("{} {} {} (Calling {})".format(start_of_line, s[0], s[1], s[2]))
+                self.print_given_line( (s[0], s[1]) , header=False)
+            start_of_line = "*"
+
+    def walk_command(self, cmd):
+        """
+        Walk a given command
+        """
+        # Loop sentinel value
+        # This is returned as True ~iff~ a movement command is
+        # executed
+        exit_loop = False
+
+        # Match command to behavior
+        if cmd[0] == 'MACRO' and len(cmd) == 1: self.display_macros()
+
+        elif cmd[0] == 'MACRO':
+            name, l = cmd[1], cmd[2]
+            self.set_new_macro(name, l)
+
+        elif cmd[0] == 'EVAL':
+            value = cmd[1]
+            self.do_eval_command(value)
+
+        elif cmd[0] == 'HELP':
+            name = cmd[1]
+            self.do_help_command(name)
+
+        elif cmd == ('BANG', ):            self.do_repl_command()
+        elif cmd == ('UP',):               self.move_frame_up()
+        elif cmd == ('DOWN',):             self.move_frame_down()
+        elif cmd == ('WHERE',):            self.do_where_command()
+        elif cmd == ('LONGLIST',):         self.list_program()
+        elif cmd == ('LIST',):             self.list_program(relative=True)
+    
+        elif cmd == ('RETVAL',):
+            if self.retval:
+                self.message("Most recent return value: {}".format(self.retval))
+            else:
+                self.message("No values have been returned yet")
+        elif cmd[0] == 'UNTIL':
+            lineno = cmd[1]
+
+            self.exc['UNTIL'] = True
+            if lineno:
+                self.old_lineinfo = (self.lineinfo[0], int(lineno) - 1)
+            else:
+                self.old_lineinfo = self.lineinfo
+
+            self.set_exc()
+            exit_loop = True
+        elif cmd == ('RETURN',):
+            if len(state.trace_stack) == 1:
+                self.message("Cannot continue to return on the top level")
+            else:
+                self.exc['RETURN'] = True
+                self.set_exc(step=False, next=False, cont=False)
+                exit_loop = True
+
+        elif cmd[0] == 'EXPLICIT':
+            set_explicit = cmd[1]
+
+            if set_explicit == False:
+                self.explicit_enabled = False
+            elif set_explicit == True:
+                self.explicit_enabled = True
+            else:
+                self.explicit_enabled = not self.explicit_enabled
+        # Step
+        elif cmd == ('STEP', ):
+            self.set_exc(step=True)
+            exit_loop = True
+        # Continue
+        elif cmd == ('CONTINUE', ):
+            self.set_exc(cont=True)
+            exit_loop = True
+        # Next
+        elif cmd == ('NEXT', ):
+            self.set_exc(next=True)
+            exit_loop = True
+        # Break 
+        elif cmd[0] == 'BREAK':
+            nums, conds = cmd[1], cmd[2]
+            if nums:
+                for ix, n in enumerate(nums):
+                    self.breakpoints[n] = conds[ix]
+            else:
+                self.list_breakpoints()
+        # Delete
+        elif cmd[0] == 'DELETE':
+            nums = cmd[1]
+            for n in nums:
+                self.breakpoints.pop(n)
+        # Macro/Unknown
+        elif cmd[0] == 'NAME':
+            v = cmd[1]
+            # If the command name is in macros
+            if v in self.macros:                    
+                self.command_queue += self.macros[v]
+            else:
+                raise ValueError("Unknown macro: {}".format(str(v)))
+        # Quit command
+        elif cmd == ('QUIT', ):
+            raise SystemExit()
+
+        elif cmd == ('NOOP', ):
+            pass
+        
+        else:
+            raise ValueError("Unknown command: {}".format(str(cmd)))
+
+        return exit_loop
+
+    def command_loop(self, in_pattern=False):
+        """
+        Main command loop for ADB
+        """
+        exit_loop = False
+
+        # Main command loop
+        while not exit_loop:
+
+            # in_pattern in explicit mode is a flag for currently stepping
+            # through a pattern
+            if in_pattern:
+                query_symbol = "[Pattern] "
+            else:
+                # Format the input symbol to reflect explicitness
+                query_symbol = "(ADB)[e] " if self.explicit_enabled else "(ADB) "
+
+            # Get the command
+            cmd = input(query_symbol)
+            
+            # Try to walk the command
+            try:
+                # Parse the command
+                (LINE, node) = self.dbgp.parse(cmd)
+
+                # Add the new commands to the queue
+                self.command_queue += node
+
+                # Add the list of commands to the queue
+                while self.command_queue:
+                    # Walk the command and get the exit state
+                    exit_loop = self.walk_command(self.command_queue.pop(0))
+
+                    # Exit if necessary
+                    if exit_loop:
+                        self.reset_config()
+                        break;
+            
+            # Intercept debugger command errors
+            except ValueError as e:
+                print("Debugger command error [{}]".format(e))
+
+            # If we are in a pattern but disabled explicit mode, that's
+            # grounds for loop exiting
+            if in_pattern and not self.explicit_enabled:
+                exit_loop = True
+
+        return exit_loop
+
+    def tick(self):
+        """
+        "Tick" the debugger. This refers to hitting some point where the user
+        has decided they would like the debugger to come back to life and entering
+        the command selection phase.
+
+        The debugger uses a queue to store working commands. This allows for more
+        complex command execution
+        """
+        exit_loop = False
+        
+        # Clear out the command queue
+        while self.command_queue:
+            exit_loop = self.walk_command(self.command_queue.pop(0))
+            if exit_loop:
+                if self.original_config:
+                    self.reset_config()
+                return
+        
+        # print the current line with lineinfo
+        self.print_given_line(self.lineinfo)
+
+        # Main command loop
+        self.command_loop()
+
+        # Reset the tab level
+        self.tab_level = 0
+    
+    def notify_explicit(self):
+        """
+        Run a command loop ~iff~ we're in explicit mode
+        """
+        if self.exc['STEP'] and self.explicit_enabled:
+            self.command_loop(in_pattern=True)
+
+    def notify(self, at_return=False):
+        """
+        Notify the debugger that a potential tick-point has
+        occured and do the necessary checks to see if we can
+        tick here.
+
+        This function is a little complicated because the
+        behavior is complicated.
+
+        Explicit mode is a mode in which extra steps in
+        computations are revealed to the user
+        """
+
+        # If we have a breakpoint here and we're not trying to go
+        # to the next top level statement, then tick
+        if self.has_breakpoint_here() and not self.exc['NEXT']:
+            self.message("Breakpoint")
+            self.tick()
+
+        # If we're at the top level and we're not continuing
+        # to the next breakpoint, and we're going to the next statement
+        # do a tick
+        elif self.top_level and self.exc['NEXT'] and not self.exc['CONTINUE']:
+            if self.has_breakpoint_here():
+                self.message("Breakpoint")
+            self.tick()
+
+        # If we're at a return statement and we're continuing onto a return
+        # we need to first send the message that we've arrived and then
+        # set the execution control such that when the return statement
+        # is walked, we always catch it
+        elif at_return and self.exc['RETURN']:
+            self.exc['RETURN'] = False
+            self.message('Return reached!')
+
+            # This is set to step so that we always hit the impending
+            # return statement
+            self.set_exc(step=True)
+
+        # If the until command is active, we basically continue until we're
+        # at a greater linenumber within the file.
+        elif self.exc['UNTIL']:
+            # Grab the old and current lineinfo
+            (old_file_name, old_lineno) = self.old_lineinfo
+            (cur_file_name, cur_lineno) = self.lineinfo
+            
+            # Compare them
+            if (old_file_name == cur_file_name) and (cur_lineno > old_lineno):
+                self.exc['UNTIL'] = False
+                self.tick()
+
+        # Otherwhise, if we're stepping through the program, always tick
+        elif self.exc['STEP']:
+            self.tick()
+        
+        # Reset the top level so that nested statements don't come in
+        self.set_top_level(False)
\ No newline at end of file
diff --git a/asteroid/adb/command.py b/asteroid/adb/command.py
new file mode 100644
index 00000000..05dcd52c
--- /dev/null
+++ b/asteroid/adb/command.py
@@ -0,0 +1,257 @@
+import re
+
+class Token:
+    def __init__(self,type,value):
+        self.type = type
+        self.value = value
+
+    def __str__(self):
+        return 'Token({},{})'.format(self.type,self.value)
+
+class DebuggerLexer:
+    def __init__(self, input_string):
+        self.token_specs = [
+        #   type:          value:
+            ('STRING',     r'"(\\"|\\.|[^"])*"'),
+            
+            ('NEXT',        r'\bnext\b|\bn\b'),
+            ('STEP',        r'\bstep\b|\bs\b'),
+            ('CONTINUE',    r'\bcontinue\b|\bcont\b|\bc\b'),
+            ('RETURN',      r'\breturn\b|\bret\b|\br\b'),
+            ('UNTIL',       r'\buntil\b|\bu\b'),
+
+            ('EVAL',        r'\beval\b'),
+            ('BANG',        r'!'),
+
+            ('BREAK',       r'\bbreak\b|\bb\b'),
+            ('DELETE',      r'\bdelete\b|\bdel\b|\bd\b'),
+
+            ('MACRO',       r'\bmacro\b'),
+            ('LIST',        r'\blist\b|\bl\b'),
+            ('LONGLIST',    r'\blonglist\b|\bll\b'),
+
+            ('RETVAL',      r'\b__retval__\b|\b_\b'),
+            
+            ('EXPLICIT',    r'\bexplicit\b|\be\b'),
+            ('ON',          r'\bon\b'),
+            ('OFF',         r'\boff\b'),
+
+            ('HELP',        r'\bh\b|\bhelp\b'),
+            ('WHERE',       r'\bwhere\b|\bw\b'),
+            ('UP',          r'\<'),
+            ('DOWN',        r'\>'),
+
+            ('IF',          r'\bif\b'),
+
+            ('NUM',         r'[+-]?([0-9]*[.])?[0-9]+'),
+            ('EQUAL',       r'='),
+            ('SEMI',        r';'),
+            ('COMMA',       r','),
+            ('LPAREN',      r'\('),
+            ('RPAREN',      r'\)'),
+
+            ('QUIT',        r'\bquit\b|\bq\b'),
+
+            ('NAME',        r'[a-zA-Z_\$][a-zA-Z0-9_\$]*'),
+            ('WHITESPACE',  r'[ \t\n]+'),
+            ('UNKNOWN',     r'.')
+        ]
+
+        # used for sanity checking in lexer.
+        self.token_types = set(type for (type,_) in self.token_specs)
+        self.tokens = self.tokenize(input_string)
+        # the following is always valid because we will always have
+        # at least the EOF token on the tokens list.
+        self.curr_token_ix = 0
+
+    def pointer(self):
+        return self.tokens[self.curr_token_ix]
+
+    def next(self):
+        if not self.end_of_file():
+            self.curr_token_ix += 1
+        return self.pointer()
+
+    def match(self, token_type):
+        if token_type == self.pointer().type:
+            tk = self.pointer()
+            self.next()
+            return tk
+        elif token_type not in self.token_types:
+            raise ValueError("unknown token type '{}'".format(token_type))
+        else:
+            raise ValueError('unexpected token {} while parsing, expected {}'
+                              .format(self.pointer().type, token_type))
+
+    def end_of_file(self):
+        if self.pointer().type == 'EOF':
+            return True
+        else:
+            return False
+
+    def tokenize(self, code):
+        tokens = []
+        re_list = ['(?P<{}>{})'.format(type,re) for (type,re) in self.token_specs]
+        combined_re = '|'.join(re_list)
+        match_object_list = list(re.finditer(combined_re, code))
+        for mo in match_object_list:
+            type = mo.lastgroup
+            value = mo.group()
+
+            if type == 'WHITESPACE':
+                continue #ignore
+            elif type == 'STRING':
+                tokens.append(Token('STRING', value.encode('utf-8')
+                                                   .decode('unicode-escape')[1:-1]))
+            elif type == 'UNKNOWN':
+                raise ValueError("unexpected character '{}'".format(value))
+            else: 
+                tokens.append(Token(type, value))
+        tokens.append(Token('EOF', '\eof'))
+        return tokens
+
+class DebuggerParser:
+    def __init__(self):
+        self.dlx = None
+
+    def parse(self, input_string):
+        self.dlx = DebuggerLexer(input_string)
+        return self.line()
+
+    def line(self):
+        if self.dlx.pointer().type == 'MACRO':
+            return self.macro()
+
+        else:
+            cmds = [self.command()]
+            while self.dlx.pointer().type in ['SEMI']:
+                self.dlx.match('SEMI')
+
+                if self.dlx.pointer().type == 'EOF':
+                    break
+
+                cmds += [self.command()]
+
+            return ('LINE', cmds)
+    
+    def macro(self):
+        self.dlx.match('MACRO')
+
+        if self.dlx.pointer().type == 'EOF':
+            return ('LINE', [('MACRO',)])
+
+        else:
+            name = self.dlx.match('NAME')
+            self.dlx.match('EQUAL')
+
+            l = self.line()
+
+            return ('LINE', [('MACRO', name.value, l[1])] )
+
+    def eval_cmd(self):
+        self.dlx.match("EVAL")
+        self.dlx.match('LPAREN')
+        code = self.dlx.match('STRING')
+        self.dlx.match('RPAREN')
+        
+        return ('EVAL', code.value)
+    
+    def break_cmd(self):
+        self.dlx.match('BREAK')
+        nums = []
+        conds = []
+        first = True
+
+        while self.dlx.pointer().type == 'NUM':
+            if first:
+                first = False
+            
+            nums.append(self.dlx.match('NUM').value)
+
+            if self.dlx.pointer().type == 'IF':
+                self.dlx.match('IF')
+                self.dlx.match('EVAL')
+                self.dlx.match('LPAREN')
+                code = self.dlx.match('STRING')
+                self.dlx.match('RPAREN')
+                conds.append(code.value)
+            else:
+                conds.append(None)
+
+            if not first:
+                if self.dlx.pointer().type == 'COMMA':
+                    self.dlx.match('COMMA')
+                    continue
+                elif self.dlx.pointer().type == 'EOF':
+                    self.dlx.match('EOF')
+                    break
+
+        return ('BREAK', list(map(int, nums)), conds)
+
+    def delete_cmd(self):
+        self.dlx.match('DELETE')
+        nums = [self.dlx.match('NUM').value]
+        
+        while self.dlx.pointer().type == 'NUM':
+            nums.append(self.dlx.match('NUM').value)
+        
+        return ('DELETE', list(map(int, nums)))
+
+
+    def help_cmd(self):
+        self.dlx.match('HELP')
+        
+        n = None
+        if self.dlx.pointer().type != 'EOF':
+            n = self.dlx.pointer().value
+            self.dlx.next()
+        
+        return ('HELP', n)
+
+    def explicit_cmd(self):
+        self.dlx.match('EXPLICIT')
+
+        set_exp = None
+        if self.dlx.pointer().type in ['ON', 'OFF']:
+            set_exp = (self.dlx.pointer().type == 'ON')
+            self.dlx.next()
+        
+        return ('EXPLICIT', set_exp)
+
+    def until_cmd(self):
+        self.dlx.match('UNTIL')
+        
+        n = None
+        if self.dlx.pointer().type == 'NUM':
+            n = self.dlx.pointer().value
+            self.dlx.next()
+        
+        return ('UNTIL', n)
+
+    def command(self):
+        t = self.dlx.pointer().type
+        
+        if t ==  'EVAL':     return self.eval_cmd()
+        elif t ==  'BREAK':    return self.break_cmd()
+        elif t ==  'DELETE':   return self.delete_cmd()
+        elif t ==  'HELP':     return self.help_cmd()
+        elif t ==  'EXPLICIT': return self.explicit_cmd()
+        elif t ==  'UNTIL':    return self.until_cmd()
+        
+        elif t in ['BANG', 'LONGLIST', 'LIST', 'QUIT', 'RETURN', 'RETVAL',
+             'STEP', 'CONTINUE', 'NEXT', 'UP', 'DOWN', 'WHERE']:
+            t = self.dlx.pointer().type
+            self.dlx.match(t)
+            return (t,)
+        
+        elif t ==  'NAME':
+            n = self.dlx.match('NAME').value
+            return ('NAME', n)
+        
+        elif t in  ['EOF', 'SEMI']:
+            return ('NOOP',)
+        
+        else:
+            raise ValueError("Unknown command: {}".format(
+                str(self.dlx.pointer().value)
+            ))
\ No newline at end of file
diff --git a/asteroid/adb/docs/guide.md b/asteroid/adb/docs/guide.md
new file mode 100644
index 00000000..605975f8
--- /dev/null
+++ b/asteroid/adb/docs/guide.md
@@ -0,0 +1,181 @@
+# ADB Reference Guide
+The Asteroid Debugger (ADB) 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
+* Stack frame inspection
+* Evaluation of arbitrary Asteroid code
+* Macros
+* REPL Instances
+
+ADB features a unique "explicit" mode which details much of the pattern matching and underlying
+mechanics of Asteroid allow developers to debug pattern matches. Explicit mode
+details many of the steps of pattern matching, function calling, statement execution, and
+return values. Explicit mode is very experimental and is under current research. As a result,
+explicit mode is currently incomplete.
+
+## Usage
+```
+asteroid --adb <FILENAME>
+```
+
+## Debugging sessions
+The debugger's prompt `(ADB)` allows the user to enter commands to effect the source environment
+and debugger behavior.
+
+Each debugging session will have a few pieces of information:
+
+__Filename and line number__
+```
+[/home/user/test.ast (1)]
+```
+
+__The current line which will be executed__
+```
+-->> let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+```
+
+__The command prompt__
+```
+(ADB)
+```
+
+ADB runs like any other debugger, here's a small example session where
+we see running commands, listing the program, and setting and continuing to breakpoints.
+```
+[/home/user/test.ast (1)]
+-->> let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+(ADB) ll
+----- Program Listing -----
+>  1 let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+   2 
+   3 let d = pattern %[(x:*p, y:*p, z:*p)]%.
+   4 let x:*p = 99.
+   5 
+   6 let t:*d = (1,2,3).
+   7 [EOF]
+(ADB) break 6
+(ADB) continue
+----- Breakpoint -----
+[/home/user/test.ast (6)]
+-->> let t:*d = (1,2,3).
+(ADB) 
+```
+
+## Commands
+Below is a list of the commands available to the debugger. Most of which can be shortened.
+These shortenings are shown in parenthesis (`c(ommand)`).
+
+### List
+`l(ist)` lists the lines around the currently executing line
+
+### Longlist
+`ll (longlist)` lists the entire program
+
+### Step
+`s(tep)` through to the next statement or through function call.
+
+### Continue
+`c(ontinue) cont(inue)` continue execution until the next breakpoint.
+
+### Next
+`n(ext)` continue onto the next executing line at the current scope.
+
+## Until
+`u(ntil) ?lineno` By default, continue execution until a line with a greater number
+than the current one is reached.
+
+Given an optional line number, continue execution until a line number greater than
+or equal to that number is reached
+
+## Return
+`(r(et))urn` Continue execution until the return of the current function is reached
+
+### Breakpoints
+`b(reak) number*` set a breakpoint at one or more lines. Running without any arguments
+lists your breakpoints.
+
+Example: `b 1 2 3`, `break`.
+
+Conditional breakpoints can be set in the same way, just attach `if eval("condition")`
+after each breakpoint number.
+
+Example:
+```
+-- Set a conditional breakpoint on 10 and normal breakpoints on 11, 15, and 23.
+b 10 if eval("x == 123") 11 15 23
+```
+
+### Delete
+`d(elete) (number)+` `del(ete) (number)+` delete a list of breakpoints.
+
+Example:
+```
+del 1 5 8 9
+```
+
+### Macro
+`macro (name) (command list)`. Define a macro. Running just `macro` lists your macros.
+
+Example macro that continues to a breakpoint and prints the value of x:
+```
+macro gox = c; eval("io@println(x)");
+```
+
+### Eval
+`eval("asteroid code")` Evaluate the asteroid code between quotes. Works exactly like a single-line
+repl.
+
+Example, print out the value of `x`:
+```
+eval("x")
+```
+
+### !
+`!` Open up a repl in the current context
+
+## \_\_retval\_\_
+`(_)_retval` Print the most recent return value
+
+### Help
+`h(elp) (command)?` gives help for a given command. Running just `help` shows all available commands.
+Example:
+```
+h macro
+help break
+```
+
+### Retval
+`((r)et)val` Prints the most recent return value
+
+### <
+`<` move up one stack frame
+
+### >
+`>` move down one stack frame
+
+### Where
+`w(here)` displays the frame stack and the currently active frame.
+
+### Explicit
+`e(xplicit) (on|off)?` By default, this command run without an argument toggles
+explicit mode. If given a literal `on` or `off`, explicit mode will be switched
+to the corresponding state.
+
+Explicit:
+```
+-- Toggle Explicit mode
+explicit
+e
+
+-- Turn on/off
+explicit on
+e on
+explicit off
+e off
+```
+
+### Quit
+`q(uit)` Quits the current ADB session
\ No newline at end of file
diff --git a/asteroid/adb/help.py b/asteroid/adb/help.py
new file mode 100644
index 00000000..bd42f574
--- /dev/null
+++ b/asteroid/adb/help.py
@@ -0,0 +1,97 @@
+command_description_table = {
+    'macro':        """macro (name) = (command list)
+    Define a macro with name "name" and a list of commands.
+
+    Typing solely "macro" will list off the macros currently enabled
+    Example:
+        macro see_x = continue; explicit; n; p x;
+    """,
+    
+    'step':         """[s]tep
+    Step to the next executing line
+    """,
+
+    'until':        """[u]ntil ?lineno
+    Without a given line number, continue execution until a line that is greater than the current
+    one is reached.
+
+    With a line number, continue execution until a line that is greater than or equal to that one
+    is reached.
+
+    In both cases, stop when the current frame returns.
+    """,
+    
+    'continue':     """[[c]ont]inue
+    Continue to the next breakpoint
+    """,
+    
+    'next':         """[n]ext
+    Go to the next top level statement
+    """,
+    
+    'break':        """[b]reak (linenum (if eval("command")))
+    Set a breakpoint at linenum. If you supply a command, the breakpoint
+    becomes conditional
+    """,
+
+    'eval' :     """eval("command")
+    Execute asteroid code
+    Example:
+        eval("let x = 10. io@println(\\"10\\")")
+
+    -- Print out the contents of variable x
+        eval("x")
+    """,
+    
+    'delete':       """[[d]el]ete (linenum)
+    Delete the breakpoint at linenum
+    """,
+    
+    '!':            """!
+    Start a REPL session
+    """,
+    
+    'longlist':     """[ll] longlist
+    List the contents of the entire program
+    """,
+    
+    'list':         """[l]ist
+    List the contents of the program around the current line
+    """,
+
+    'retval':       """[[r]et]val
+    Print the most recent return value
+    """,
+    
+    '>':            """>
+    Move down one frame
+    """,
+
+    '<':            """<
+    Move up one frame
+    """,
+
+    'where':        """[w]here
+    Lists the currently available stack frames and shows the user
+    the currently active one.
+    """,
+
+    'quit':         """[q]uit
+    Exit ADB
+    """,
+    
+    'explicit':     """[e]xplicit (on|off)?
+    Toggle explicit mode or set explicit mode to a specific state.
+
+    For more information about explicit mode, refer to "ADB in action".
+
+    Example:
+        explicit    -- Toggles explicit mode
+        explicit on -- Turns explicit mode on
+        explcit off -- Turns explicit mode off
+    """,
+    
+    'help':         """[h]elp ?(name)
+    Get help for a command
+    """
+}
diff --git a/asteroid/adb/version.py b/asteroid/adb/version.py
new file mode 100644
index 00000000..a96ce905
--- /dev/null
+++ b/asteroid/adb/version.py
@@ -0,0 +1,1 @@
+VERSION = "1.0"
\ No newline at end of file
diff --git a/asteroid/frontend.py b/asteroid/frontend.py
index b95fd20e..17a53c00 100644
--- a/asteroid/frontend.py
+++ b/asteroid/frontend.py
@@ -74,10 +74,24 @@ def dbg_print(string):
 ###########################################################################################
 class Parser:
 
-    def __init__(self, filename="<input>"):
+    def __init__(self, filename="<input>", functional_mode=False):
         self.lexer = Lexer()
+        self.functional_mode = functional_mode
+        self.system_modules = os.listdir(os.path.dirname( __file__ )+'/modules')
+        self.parser_file_path = os.path.split(os.path.dirname(__file__))[0]
         state.lineinfo = (filename,1)
 
+    ###########################################################################################
+    # best guess whether we are looking at a system module or not: if the parser
+    # file path is a substring of the module file path then the module is a
+    # system module
+    def is_system_module(self):
+        name = state.lineinfo[0]
+        if (os.path.split(name)[1] in self.system_modules) and (self.parser_file_path in name):
+            return True
+        else:
+            return False
+
     ###########################################################################################
     def parse(self, input):
         self.lexer.input(input)
@@ -103,10 +117,29 @@ def stmt_list(self):
         dbg_print("parsing STMT_LIST")
 
         sl = []
-        while self.lexer.peek().type in stmt_lookahead:
+        while self.stmt_coming_up():
             sl += [('lineinfo', state.lineinfo)]
-            sl += [('clear-ret-val',)]
-            sl += [self.stmt()]
+
+            # Get the statment
+            stmt = self.stmt()
+
+            # This set of conditionals gives us the behavior we want
+            # wrt implicit return values. There's only one situation
+            # in which we want to set the return value. That is when
+            # a top level expression is the last in a stmt_list
+
+            # If there's a statement coming up or the current statement
+            # is not a top level expression, just append the statement
+            if self.stmt_coming_up() or stmt[0] != 'top-level-exp':
+                sl += [stmt]
+
+            # Otherwhise, if there's no statement coming up and the
+            # last statement was a top level expression, set the
+            # ret val
+            else:
+                sl += [('clear-ret-val',)]
+                sl += [('set-ret-val', stmt)]             
+
         return ('list', sl)
 
     ###########################################################################################
@@ -120,7 +153,6 @@ def stmt_list(self):
     #    | GLOBAL id_list '.'?
     #    | ASSERT exp '.'?
     #    | STRUCTURE ID WITH struct_stmts END
-    #    | TRAIT ID WITH trait_stmts END
     #    | LET pattern '=' exp '.'?
     #    | LOOP DO? stmt_list END
     #    | FOR pattern IN exp DO stmt_list END
@@ -189,6 +221,7 @@ def stmt(self):
                 ast_module_file = search_list[ix]
                 #lhh
                 #print("AST module: {}".format(ast_module_file))
+                # compute Path object so we can test if the file exists
                 ast_module_path = Path(ast_module_file)
                 if ast_module_path.is_file():
                     file_found = True
@@ -202,16 +235,17 @@ def stmt(self):
             #print("opening module {}".format(ast_module_file))
 
             old_lineinfo = state.lineinfo
-
             with open(ast_module_file) as f:
-                state.modules.append(module_name)
+                #state.modules.append(module_name)
+                state.modules.append(ast_module_file)
                 data = f.read()
-                fparser = Parser(module_name)
-                (STMT_LIST, fstmts) = fparser.parse(data)
 
+                # Give the absolute path to the parser
+                fparser = Parser(str(ast_module_path))
+                (STMT_LIST, fstmts) = fparser.parse(data)
+            
             state.lineinfo = old_lineinfo
-            (LIST, sl) = self.stmt_list()
-            return ('list', fstmts + sl)
+            return ('import_stmt', fstmts)
 
         elif tt == 'GLOBAL':
             dbg_print("parsing GLOBAL")
@@ -251,16 +285,19 @@ def stmt(self):
             return ('unify', p, v)
 
         elif tt == 'LOOP':
+            if self.functional_mode and not self.is_system_module():
+                raise SyntaxError("loop is not supported in functional mode")
             dbg_print("parsing LOOP")
             self.lexer.match('LOOP')
             self.lexer.match_optional('DO')
             sl = self.stmt_list()
             self.lexer.match('END')
-            #self.lexer.match_optional('LOOP')
             return ('loop',
                     ('stmt-list', sl))
 
         elif tt == 'FOR':
+            if self.functional_mode and not self.is_system_module():
+                raise SyntaxError("for loop is not supported in functional mode")
             dbg_print("parsing FOR")
             self.lexer.match('FOR')
             e = self.exp()
@@ -272,12 +309,13 @@ def stmt(self):
             self.lexer.match('DO')
             sl = self.stmt_list()
             self.lexer.match('END')
-            #self.lexer.match_optional('FOR')
             return ('for',
                     ('in-exp', e),
                     ('stmt-list', sl))
 
         elif tt == 'WHILE':
+            if self.functional_mode and not self.is_system_module():
+                raise SyntaxError("while loop is not supported in functional mode")
             dbg_print("parsing WHILE")
             self.lexer.match('WHILE')
             e = self.exp()
@@ -290,6 +328,8 @@ def stmt(self):
                     ('stmt-list', sl))
 
         elif tt == 'REPEAT':
+            if self.functional_mode and not self.is_system_module():
+                raise SyntaxError("repeat loop is not supported in functional mode")
             dbg_print("parsing REPEAT")
             self.lexer.match('REPEAT')
             self.lexer.match_optional('DO')
@@ -302,11 +342,16 @@ def stmt(self):
                     ('until-exp', e))
 
         elif tt == 'BREAK':
+            if self.functional_mode and not self.is_system_module():
+                raise SyntaxError("break statement is not supported in functional mode")
             dbg_print("parsing BREAK")
             self.lexer.match('BREAK')
             return ('break',)
 
         elif tt == 'IF':
+            if self.functional_mode and not self.is_system_module():
+                raise SyntaxError("if statement is not supported in functional mode")
+
             # if statements are coded as a list of ('if-clause', condition, stmts)
             if_list = []
 
@@ -340,7 +385,6 @@ def stmt(self):
                 if_list.append(('if-clause', ('cond', ('boolean', True)), ('stmt-list', stmts)))
 
             self.lexer.match('END')
-            #self.lexer.match_optional('IF')
             return ('if', ('list', if_list))
 
 
@@ -395,7 +439,7 @@ def stmt(self):
         elif tt in exp_lookahead:
             v = self.exp()
             self.lexer.match_optional('DOT')
-            return ('set-ret-val', v)
+            return ('top-level-exp', v)
 
         else:
             raise SyntaxError("syntax error at '{}'"
@@ -625,18 +669,18 @@ def compound(self):
         elif tt == 'TO':
             self.lexer.match('TO')
             v2 = self.exp()
-            if self.lexer.peek().type == 'STRIDE':
-                self.lexer.match('STRIDE')
+            if self.lexer.peek().type == 'STEP':
+                self.lexer.match('STEP')
                 v3 = self.exp()
                 return ('raw-to-list',
                         ('start', v),
                         ('stop', v2),
-                        ('stride', v3))
+                        ('step', v3))
             else:
                 return ('raw-to-list',
                         ('start', v),
                         ('stop', v2),
-                        ('stride', ('integer', '1')))
+                        ('step', ('integer', '1')))
 
         else:
             return v
@@ -929,3 +973,8 @@ def function_const(self):
         body_list = self.body_defs()
 
         return ('function-exp', body_list)
+
+    ###########################################################################################
+    # Minor helper function
+    def stmt_coming_up(self):
+        return self.lexer.peek().type in stmt_lookahead
\ No newline at end of file
diff --git a/asteroid/grammar.txt b/asteroid/grammar.txt
index 04358b89..be99e6ae 100644
--- a/asteroid/grammar.txt
+++ b/asteroid/grammar.txt
@@ -91,7 +91,7 @@
         (
            (IS pattern) |
            (IN exp) |               /* exp has to be a list */
-           (TO exp (STRIDE exp)?) |   /* list comprehension */
+           (TO exp (STEP exp)?) |   /* list comprehension */
         )?
 
   logic_exp0
diff --git a/asteroid/interp.py b/asteroid/interp.py
index 74a48b2a..f6d112a7 100644
--- a/asteroid/interp.py
+++ b/asteroid/interp.py
@@ -11,67 +11,92 @@
 from asteroid.support import *
 from asteroid.frontend import Parser
 from asteroid.state import state, dump_trace
-from asteroid.walk import walk
+from asteroid.walk import walk_program, debug_walk
 
 # the prologue file is expected to be in the 'modules' folder
 prologue_name = 'prologue.ast'
-
-# TODO: adjust the defaults
-def interp(input_stream,
-           input_name = "<input>",
+def load_prologue():
+    """
+    Load the prolog files and run them.
+    """
+    prologue_file = ''
+    prologue_file_base = os.path.join('modules', prologue_name)
+    module_path = os.path.join(os.path.split(os.path.abspath(__file__))[0], prologue_file_base)
+    working_path = os.path.join(os.getcwd(), prologue_file_base)
+
+    if os.path.isfile(module_path):
+        prologue_file = module_path
+    
+    elif os.path.isfile(working_path):
+        prologue_file = working_path
+    
+    else:
+        raise ValueError("Asteroid prologue '{}' not found"
+                        .format(prologue_file_base))
+    
+    with open(prologue_file) as f:
+        state.modules.append(prologue_name)
+        data = f.read()
+        pparser = Parser(prologue_file)
+        (LIST, pstmts) = pparser.parse(data)
+
+    state.AST = ('list', pstmts)
+    walk_program(state.AST)
+    state.AST = None
+
+def interp(program,
+           program_name = "<input>",
            tree_dump=False,
            do_walk=True,
            symtab_dump=False,
            exceptions=False,
            redundancy=True,
            prologue=True,
-           initialize_state = True):
+           debugger=None,
+           functional_mode=False,
+           initialize_state = True
+           ):
+    '''
+    The function 'interp' is the top-level entry point to the
+    Asteroid interpreter with the following arguments:
+      * program: a string representing an Asteroid program
+      * program_name: the of the program to be interpreted
+      * tree_dump: a flag indicating whether the AST should be dumped after parsing
+      * do_walk: a flag indicating whether the interpret should interpret the AST
+      * symtab_dump: a flag indicating whether the contents of the symbol
+                     table should be printed out
+      * exceptions: when true Python style exceptions are shown, otherwise
+                    Asteroid style exceptions are shown
+      * redundancy: a flag indicating whether the function pattern checker should be run
+      * prologue: a flag indicating whether the Asteroid prologue file should be loaded
+      * functional_mode: if set then the Asteroid interpreter behaves like an interpreter
+                         functional programming language.
+      * initialize_state: if set then the interpreter will (re)initialize its state.  
+    '''
     try:
         # initialize state
         if initialize_state:
-            state.initialize(input_name)
+            state.initialize(program_name)
 
-        #lhh
-        #print("path[0]: {}".format(sys.path[0]))
-        #print("path[1]: {}".format(sys.path[1]))
+        if prologue:
+            load_prologue()
 
         # initialize "check for useless clauses" flag
         state.eval_redundancy = redundancy
 
-        # read in prologue
-        if prologue:
-            prologue_file = ''
-            prologue_file_base = os.path.join('modules', prologue_name)
-            module_path = os.path.join(os.path.split(os.path.abspath(__file__))[0], prologue_file_base)
-            working_path = os.path.join(os.getcwd(), prologue_file_base)
-
-            if os.path.isfile(module_path):
-                prologue_file = module_path
-            elif os.path.isfile(working_path):
-                prologue_file = working_path
-            else:
-                raise ValueError("Asteroid prologue '{}' not found"
-                                .format(prologue_file_base))
-
-            with open(prologue_file) as f:
-                state.modules.append(prologue_name)
-                data = f.read()
-                pparser = Parser(prologue_name)
-                (LIST, pstmts) = pparser.parse(data)
-
         # build the AST
-        parser = Parser(input_name)
-        (LIST, istmts) = parser.parse(input_stream)
-        if prologue:
-            state.AST = ('list', pstmts + istmts)
-        else:
-            state.AST = ('list', istmts)
+        parser = Parser(program_name, functional_mode)
+        (LIST, istmts) = parser.parse(program)
+        state.AST = ('list', istmts)
 
         # walk the AST
         if tree_dump:
             dump_AST(state.AST)
         if do_walk:
-            walk(state.AST)
+            if debugger:
+                debug_walk(state.AST, debugger)
+            else:
+                walk_program(state.AST)
         if symtab_dump:
             state.symbol_table.dump()
 
@@ -102,12 +127,18 @@ def interp(input_stream,
             sys.exit(1)
 
     except  KeyboardInterrupt as e:
+        if debugger:
+            raise e
+
         dump_trace()
         print("error: keyboard interrupt")
         # needed for REPL
         if not exceptions:
             sys.exit(1)
 
+    except SystemExit:
+        exit(0)
+
     except Exception as e:
         if exceptions: # rethrow the exception so that you can see the full backtrace
             if symtab_dump:
@@ -126,4 +157,4 @@ def interp(input_stream,
         print("error: {}".format(e))
         # needed for REPL
         if not exceptions:
-            sys.exit(1)
+            sys.exit(1)
\ No newline at end of file
diff --git a/asteroid/lex.py b/asteroid/lex.py
index 61cc09ba..e096bd85 100644
--- a/asteroid/lex.py
+++ b/asteroid/lex.py
@@ -40,7 +40,7 @@
     'pattern'       : 'PATTERN',
     'repeat'        : 'REPEAT',
     'return'        : 'RETURN',
-    'stride'        : 'STRIDE',
+    'step'        : 'STEP',
     'structure'     : 'STRUCTURE',
     'system'        : 'SYSTEM',
     'throw'         : 'THROW',
diff --git a/asteroid/modules/prologue.ast b/asteroid/modules/prologue.ast
index 10a72d22..1f952912 100644
--- a/asteroid/modules/prologue.ast
+++ b/asteroid/modules/prologue.ast
@@ -93,7 +93,7 @@ function range
   with (start:%integer,stop:%integer) do
     return [start to stop-1].
   with (start:%integer,stop:%integer,inc:%integer) do
-    return [start to stop-1 stride inc].
+    return [start to stop-1 step inc].
   with stop:%integer do
     return [0 to stop-1].
   end
diff --git a/asteroid/repl.py b/asteroid/repl.py
index e10557e7..3a3f0673 100644
--- a/asteroid/repl.py
+++ b/asteroid/repl.py
@@ -1,4 +1,4 @@
-from asteroid.interp import interp
+from asteroid.interp import interp, load_prologue
 from asteroid.version import VERSION
 from asteroid.state import state
 from asteroid.globals import ExpectationError
@@ -8,11 +8,14 @@
 from sys import stdin
 import readline
 
-def repl():
-    state.initialize()
-    print_repl_menu()
+def repl(new=True, redundancy=False, prologue=False, functional_mode=False):
+
+    if new:
+        state.initialize()
+        load_prologue()
+        print_repl_menu()
     try:
-        run_repl()
+        run_repl(redundancy, prologue, functional_mode)
     except EOFError:
         print()
         pass
@@ -22,8 +25,7 @@ def print_repl_menu():
     print("Run \"asteroid -h\" for help")
     print("Press CTRL+D to exit")
 
-
-def run_repl():
+def run_repl(redundancy, prologue, functional_mode):
 
     # The two different prompt types either > for a new statement
     # or . for continuing one
@@ -63,7 +65,12 @@ def run_repl():
         """
         try:
             # Try to interpret the new statement
-            interp(line, initialize_state=False, exceptions=True)
+            interp(line,
+                   initialize_state=False,
+                   redundancy=redundancy,
+                   prologue=prologue,
+                   functional_mode=functional_mode,
+                   exceptions=True)
 
             # Try to
             line = ""
@@ -71,10 +78,10 @@ def run_repl():
             # Check for return value
             if function_return_value[-1]:
                 # Get the last return value (type, value)
-                (_, val) = function_return_value[-1]
+                retval = function_return_value[-1]
 
                 # If it isn't none, print out the value
-                if val is not None:
+                if retval[1] != None:
                     print(term2string(function_return_value[-1]))
 
 
@@ -92,6 +99,7 @@ def run_repl():
             print("error: "+str(e))
             line = ""
             current_prompt = arrow_prompt
+            raise e
 
         else:
             current_prompt = arrow_prompt
diff --git a/asteroid/support.py b/asteroid/support.py
index 0d4dbc2e..31853916 100644
--- a/asteroid/support.py
+++ b/asteroid/support.py
@@ -252,9 +252,38 @@ def term2string(term):
 
     elif TYPE == 'apply':
         (APPLY, f, args) = term
-        term_string = term2string(f)
-        term_string += ' '
-        term_string += term2string(args)
+        operator_table = {
+            '__uminus__'    : ("-", True),
+            '__uplus__'     : ("+", True),
+            '__not__'       : ("not ", True),
+            '__plus__'      : ("+", False),
+            '__minus__'     : ("-", False),
+            '__times__'     : ("*", False),
+            '__divide__'    : ("/", False),
+            '__or__'        : (" or ", False),
+            '__and__'       : (" and ", False),
+            '__eq__'        : (" == ", False),
+            '__ne__'        : (" != ", False),
+            '__le__'        : (" <= ", False),
+            '__lt__'        : (" < ", False),
+            '__ge__'        : (" >= ", False),
+            '__gt__'        : (" > ", False)
+        }
+        if isinstance(f[1], str) and f[1] in operator_table:
+            (op_symbol, is_unary) = operator_table[f[1]]
+            if is_unary:
+                term_string = "{}({})".format(
+                    op_symbol, term2string(args)
+                )
+            else:
+                (_, arglist) = args
+                term_string = "{}{}{}".format(
+                    term2string(arglist[0]), op_symbol, term2string(arglist[1])
+                )
+        else:
+            term_string = term2string(f)
+            term_string += term2string(args)
+
         return term_string
 
     elif TYPE == 'pattern':
@@ -287,10 +316,21 @@ def term2string(term):
         term_string += "]"
         return term_string
 
-    elif TYPE == 'named-pattern':       # Handle a named pattern
+    elif TYPE == 'raw-head-tail':
+        (RHT, e1, e2) = term
+        walked_e1 = term2string(e1)
+        walked_e2 = term2string(e2)
+
+        return walked_e1 + "|" + walked_e2
 
+    elif TYPE == 'named-pattern':       # Handle a named pattern
         (NAMED_PATTERN,ID,pattern) = term
-        term_string = ID[1] + ':'
+
+        if ID[0] == 'index':
+            (INDEX, i1, i2) = ID
+            term_string = term2string(i1) + "@ " + term2string(i2) + ":"
+        else:
+            term_string = ID[1] + ':'
 
         return term_string + term2string(pattern)
 
@@ -308,12 +348,14 @@ def term2string(term):
         return term_string
 
     elif TYPE == 'deref':               # Handle a first-class pattern
-        (DEREF, (ID, pName)) = term
-        term_string = pName
-        term_string += ":"
+        (DEREF, d_exp) = term
+        term_string = "*" + term2string(d_exp)
 
         #Get the actual pattern from the symbol table
-        term_string += term2string(state.symbol_table.lookup_sym(pName))
+        # NOTE: THIS BREAKS WHEN A PATTERN IS NOT DEFINED IN SCOPE
+        # TODO: FIX THIS
+        # \/\/\/\/\/\/\/\/\/
+        #term_string += term2string(state.symbol_table.lookup_sym(pName))
         return term_string
 
     elif TYPE == 'if-exp':              # Handle a conditional pattern
@@ -322,8 +364,36 @@ def term2string(term):
         # therefore we don't print the whole tree.
         return term2string(value)+' if (condition...)'
 
+    elif TYPE == 'index':
+        (INDEX, base, ix) = term
+        return term2string(base) + "@ " + term2string(ix)
+
+    elif TYPE == 'foreign':
+        return "(foreign ...)"
+
+    elif TYPE == 'is':
+        (IS, e1, e2) = term
+
+        return term2string(e1) + " is " + term2string(e2)
+
+    elif TYPE == 'in':
+        (IN, e1, e2) = term
+
+        return term2string(e1) + " in " + term2string(e2)
+
+    elif TYPE == 'to-list':
+        (TO_LIST, (START, start), (STOP, stop), (STRIDE, stride)) = term
+        return "[" + term2string(start) + " to " + term2string(stop) + ", stride: " + term2string(stride) + "]"
+
+    elif TYPE == 'function-exp':
+        return ('function exp...')
+
+    elif TYPE == 'function-val':
+        return ('function val...')
+
+    elif TYPE == 'struct':
+        return ('struct...')
     else:
-        #lhh print(term)
         raise ValueError(
             "unknown type '{}' in term2string"
             .format(TYPE))
diff --git a/asteroid/symtab.py b/asteroid/symtab.py
index bb032d32..a2b7e026 100644
--- a/asteroid/symtab.py
+++ b/asteroid/symtab.py
@@ -21,6 +21,9 @@ def __init__(self):
         self.globals = [[]]
         self.global_scope = self.scoped_symtab[0] # reference to global dictionary
 
+        # Stack of configs not currently in use
+        self.saved_configs = []
+
     def dump(self):
         print("*** Symbol Table Dump:")
         pprint(self.scoped_symtab)
diff --git a/asteroid/version.py b/asteroid/version.py
index e46b3352..1fa61f89 100644
--- a/asteroid/version.py
+++ b/asteroid/version.py
@@ -5,4 +5,4 @@
 # (c) University of Rhode Island
 ##########################################################################################
 
-VERSION = "1.0.0"
+VERSION = "1.1.0"
diff --git a/asteroid/walk.py b/asteroid/walk.py
index 8bbb81e1..d8995ab5 100644
--- a/asteroid/walk.py
+++ b/asteroid/walk.py
@@ -11,6 +11,15 @@
 from asteroid.support import *
 from asteroid.state import state, warning
 
+#########################################################################
+# These two variables are used by the debugger. "Debugging" is a flag
+# to tell if we're currently in debugging mode and "Debugger" is a
+# debugger object. For more information on the debugger's helper
+# functions. See the function definitions in this file.
+#########################################################################
+debugging = False
+debugger = None
+
 #########################################################################
 # this dictionary maps list member function names to function
 # implementations given in the Asteroid prologue.
@@ -33,16 +42,7 @@
 function_return_value = [None]
 
 ###########################################################################################
-# check if the two type tags match
-def match(tag1, tag2):
-
-    if tag1 == tag2:
-        return True
-    else:
-        return False
-
-###########################################################################################
-def unify(term, pattern, unifying = True ):
+def __unify(term, pattern, unifying = True ):
     '''
     unify term and pattern recursively and return the unifier.
     this unification allows for the same variable to appear
@@ -64,31 +64,32 @@ def unify(term, pattern, unifying = True ):
           unifying, then we are evaluating subsumption between two patterns for the
           purpose of detecting redundant/useless pattern clauses in functions.
     '''
-    #lhh
-    # print("unifying:\nterm: {}\npattern: {}\n\n".format(term, pattern))
-
-    # if unifying:
-    #     print("unifying:\nterm: {}\npattern: {}\n\n".format(term, pattern))
-    # else:
-    #     print("evaluating subsumption:\nterm: {}\npattern: {}\n\n".format(term, pattern))
 
     ### Python value level matching
     # NOTE: in the first rules where we test instances we are comparing
     # Python level values, if they don't match exactly then we have
     # a pattern match fail.
     if isinstance(term, str): # apply regular expression match
+        message_explicit("Regex match: {} and {}", [pattern, term], level="secondary")
+
         if isinstance(pattern, str) and re_match("^"+pattern+"$", term):
             # Note: a pattern needs to match the whole term.
+            message_explicit("Matched!", level="tertiary")
             return [] # return empty unifier
         else:
+            message_explicit("Failed!", level="tertiary")
             raise PatternMatchFailed(
                 "regular expression '{}' did not match '{}'"
                 .format(pattern, term))
 
     elif isinstance(term, (int, float, bool)):
+        message_explicit("Literal match: {} and {}", [pattern, term], level="primary")
+
         if term == pattern:
+            message_explicit("Matched!", level="secondary")
             return [] # return an empty unifier
         else:
+            message_explicit("Failed! {} != {}", [pattern, term], level="secondary")
             raise PatternMatchFailed(
                 "'{}' is not the same as '{}'"
                 .format(term, pattern))
@@ -101,16 +102,26 @@ def unify(term, pattern, unifying = True ):
             raise PatternMatchFailed(
                 "term and pattern lists/tuples are not the same length")
         else:
+            message_explicit("Matching lists: {} and {}",
+                [gen_t2s( ('list', pattern) ), gen_t2s( ('list', term) )],
+                increase=True, notify=True)
+
+            # Make our unifier(s)
             unifier = []
             for i in range(len(term)):
                 unifier += unify(term[i], pattern[i], unifying)
 
-            check_repeated_symbols(unifier) #Ensure we have no non-linear patterns
-            return unifier
+            message_explicit("Matched!", decrease=True)
 
+            # Ensure we have no non-linear patterns
+            check_repeated_symbols(unifier)
+
+            return unifier
+    
     elif ((not unifying) and (term[0] == 'named-pattern')):
 
         # Unpack a term-side name-pattern if evaluating redundant clauses
+        message_explicit("Evaluating named pattern", notify=True)
         return unify(term[2],pattern,unifying)
 
     elif ((not unifying) and (term[0] == 'deref')):
@@ -131,11 +142,33 @@ def unify(term, pattern, unifying = True ):
             raise PatternMatchFailed(
                 "pattern type '{}' and term type '{}' do not agree"
                 .format(pid,tid))
-        return unify(data_only(tl),data_only(pl))
+
+        message_explicit("Matching objects {}{} and {}{}",
+            [pid, gen_t2s( ('tuple', pl) ), tid, gen_t2s( ('tuple', tl) )],
+            increase=True
+        )
+
+        unifiers = []
+
+        for i in range(len(pl)):
+            # OWM: We can't actually unify function-vals. If we know the type is
+            # the same, can we just assume the function-val is the same?
+            notify_explicit()
+
+            if tl[i][0] != 'function-val':
+                unifiers += unify(tl[i], pl[i])
+        
+        message_explicit("Objects matched", decrease=True)
+        return unifiers
 
     elif pattern[0] == 'string' and term[0] != 'string':
         # regular expression applied to a non-string structure
         # this is possible because all data types are subtypes of string
+        message_explicit("Matching string {} and non-string {}",
+            [gen_t2s(pattern), gen_t2s(term)],
+            notify=True
+        )
+
         return unify(term2string(term), pattern[1])
 
     elif pattern[0] == 'if-exp':
@@ -160,6 +193,12 @@ def unify(term, pattern, unifying = True ):
         if else_exp[0] != 'null':
             raise PatternMatchFailed("conditional patterns do not support 'else' clauses")
 
+        # Explicit messaging
+        message_explicit("Conditional match: if ({})",
+            [gen_t2s(cond_exp)],
+            notify=True, increase=True
+        )
+
         unifiers = unify(term, pexp, unifying)
 
         if state.constraint_lvl:
@@ -174,8 +213,14 @@ def unify(term, pattern, unifying = True ):
             state.symbol_table.pop_scope()
 
         if bool_val[1]:
+            message_explicit("Condition met, {}",
+                [gen_t2s(cond_exp)], decrease=True
+            )
             return unifiers
         else:
+            message_explicit("Condition ({}) failed",
+                [gen_t2s(cond_exp)], decrease=True
+            )
             raise PatternMatchFailed(
                 "conditional pattern match failed")
 
@@ -195,10 +240,11 @@ def unify(term, pattern, unifying = True ):
         raise PatternMatchFailed(
             "conditional patterns not supported.")
 
-
     elif pattern[0] == 'typematch':
         typematch = pattern[1]
         nextIndex = 0 #indicates index of where we will 'look' next
+        
+        message_explicit("Typematch {} to type {}", [gen_t2s(term), typematch])
 
         if typematch in ['string','real','integer','list','tuple','boolean','none']:
 
@@ -214,8 +260,10 @@ def unify(term, pattern, unifying = True ):
                         return []
 
             if typematch == term[nextIndex]:
+                message_explicit("Success!", level="secondary")
                 return []
             else:
+                message_explicit("Failure", level="secondary")
                 raise PatternMatchFailed(
                     "expected type '{}' got a term of type '{}'"
                     .format(typematch, term[nextIndex]))
@@ -223,8 +271,10 @@ def unify(term, pattern, unifying = True ):
         elif typematch == 'function':
             # matching function and member function values
             if term[0] in ['function-val','member-function-val']:
+                message_explicit("Success!", level="secondary")
                 return []
             else:
+                message_explicit("Failure", level="secondary")
                 raise PatternMatchFailed(
                     "expected type '{}' got a term of type '{}'"
                     .format(typematch, term[0]))
@@ -235,8 +285,10 @@ def unify(term, pattern, unifying = True ):
                 # any kind of structure can be a pattern, and variables
                 # see globals.py for a definition of 'patterns'
                 if term[nextIndex] in patterns:
+                    message_explicit("Success!", level="secondary")
                     return []
                 else:
+                    message_explicit("Failure", level="secondary")
                     raise PatternMatchFailed(
                             "expected type '{}' got a term of type '{}'"
                             .format(typematch, term[0]))
@@ -249,11 +301,14 @@ def unify(term, pattern, unifying = True ):
                 #handle lists/head-tails subsuming each other
                 if (term[0] in ["list","head-tail"]):
                     if ((typematch == 'list')):
+                        message_explicit("Success!", level="secondary")
                         return []
 
                 if term[nextIndex] in pattern_subsumes:
+                    message_explicit("Success!", level="secondary")
                     return []
                 else:
+                    message_explicit("Failure", level="secondary")
                     raise PatternMatchFailed(
                         "expected type '{}' got a term of type '{}'"
                         .format(typematch, term[nextIndex]))
@@ -266,16 +321,22 @@ def unify(term, pattern, unifying = True ):
                 (STRUCT_ID, (ID, struct_id)),
                 (OBJECT_MEMORY, LIST)) = term
             if struct_id == typematch:
-                    return []
+                message_explicit("Success!", level="secondary")
+                return []
             else:
+                message_explicit("Failure", level="secondary", increase=True)
                 raise PatternMatchFailed(
                     "expected type '{}' got an object of type '{}'"
                     .format(typematch, struct_id))
 
         else:
             if state.symbol_table.lookup_sym(typematch)[0] != 'struct':
+                message_explicit("Failure", level="secondary", increase=True)
+
                 raise PatternMatchFailed( "'{}' is not a type".format(typematch) )
             else:
+                message_explicit("Failure", level="secondary", increase=True)
+
                 raise PatternMatchFailed(
                     "expected type '{}' got an object of type '{}'"
                     .format(typematch, term[0]))
@@ -284,15 +345,29 @@ def unify(term, pattern, unifying = True ):
     elif pattern[0] == 'named-pattern':
         # unpack pattern
         (NAMED_PATTERN, name_exp, p) = pattern
-
+        message_explicit("Matching term {} and pattern {} to [{}]",
+            [gen_t2s(term), gen_t2s(p), gen_t2s(name_exp)],
+            notify=True, increase=True
+        )
         # name_exp can be an id or an index expression.
-        return unify(term, p, unifying) + [(name_exp, term)]
+        unifiers = unify(term, p, unifying) + [(name_exp, term)]
+        
+        message_explicit("Matched ({} and {})", 
+            [gen_t2s(unifiers[0][0]), gen_t2s(unifiers[0][1])],
+            decrease=True
+        )
+
+        return unifiers
 
     elif pattern[0] == 'none':
         if term[0] != 'none':
+            message_explicit("{} and none do not match",
+                [gen_t2s(term)]
+            )
             raise PatternMatchFailed("expected 'none' got '{}'"
                     .format(term[0]))
         else:
+            message_explicit("None and None match", level="secondary")
             return []
 
     # NOTE: functions/foreign are allowed in terms as long as they are matched
@@ -341,8 +416,22 @@ def unify(term, pattern, unifying = True ):
             pattern_list = arg[1]
         else:
             pattern_list = [arg]
+
         # only pattern match on object data members
-        return unify(data_only(obj_memory), pattern_list, unifying)
+        message_explicit("Matching object {}{} and pattern {}{}",
+            [apply_id,  gen_t2s( ('tuple', data_only(obj_memory)) ),
+            struct_id, gen_t2s( ('tuple', pattern_list) )],
+            notify=True
+        )
+        # Running through the list elements indivuidually allows for
+        # better debugging information
+        unifiers = []
+        data = data_only(obj_memory)
+
+        for i in range(len(data)):
+            unifiers += unify(data[i], pattern_list[i], unifying)
+
+        return unifiers
 
     elif pattern[0] == 'index': # list element lval access
         unifier = (pattern, term)
@@ -364,7 +453,10 @@ def unify(term, pattern, unifying = True ):
             return [unifier]
 
     elif pattern[0] in ['head-tail', 'raw-head-tail']:
-
+        message_explicit("Matching {} to {}",
+            [gen_t2s(term), gen_t2s(pattern)],
+            notify=True
+        )
         # if we are unifying or we are not evaluating subsumption
         #  to another head-tail
         if unifying or term[0] not in ['head-tail', 'raw-head-tail']:
@@ -372,22 +464,27 @@ def unify(term, pattern, unifying = True ):
             (LIST, list_val) = term
 
             if LIST != 'list':
+                message_explicit("Failed", level="secondary")
                 raise PatternMatchFailed(
                     "head-tail operator expected type 'list' got type '{}'"
                     .format(LIST))
 
             if not len(list_val):
+                message_explicit("Failed", level="secondary")
+
                 raise PatternMatchFailed(
                     "head-tail operator expected a non-empty list")
 
             list_head = list_val[0]
             list_tail = ('list', list_val[1:])
-
             unifier = []
+
             unifier += unify(list_head, pattern_head, unifying)
             unifier += unify(list_tail, pattern_tail, unifying)
 
             check_repeated_symbols(unifier) #Ensure we have no non-linear patterns
+            message_explicit("Success!", level="secondary")
+
             return unifier
 
         else: #Else we are evaluating subsumption to another head-tail
@@ -400,6 +497,7 @@ def unify(term, pattern, unifying = True ):
 
             if (lengthH > lengthL): # If the length of the higher presedence pattern is greater
                                     # then length of the lower precedence pattern, it is not redundant
+                message_explicit("Failed", level="secondary")
                 raise PatternMatchFailed(
                     "Subsumption relatioship broken, pattern will not be rendered redundant.")
 
@@ -417,15 +515,21 @@ def unify(term, pattern, unifying = True ):
                         break
 
                 check_repeated_symbols(unifier) #Ensure we have no non-linear patterns
+                message_explicit("Success!", level="secondary")
                 return unifier
 
     elif pattern[0] == 'deref':  # ('deref', v)
         # v can be an AST representing any computation
         # that produces a pattern.
+
+        message_explicit("Dereferencing {}", [gen_t2s(pattern[1])])
+
         p = walk(pattern[1])
 
-        #lhh
-        #print("unifying \nterm:{}\npattern:{}\n".format(term,p))
+        message_explicit("{} -> {}", [gen_t2s(pattern), gen_t2s(p)], 
+            level="secondary")
+
+        notify_explicit()
 
         return unify(term,p,unifying)
 
@@ -449,17 +553,30 @@ def unify(term, pattern, unifying = True ):
         return unify(t_arg, p_arg, unifying)
 
     elif pattern[0] == 'constraint':
+        message_explicit("[Begin] constraint pattern: {}",
+            [gen_t2s(pattern[1])],
+            notify=True, increase=True
+        )
+
         state.constraint_lvl += 1
         unifier = unify(term,pattern[1])
         state.constraint_lvl -= 1
+
+        message_explicit("[End] constraint pattern", decrease=True)
         return [] #Return an empty unifier
 
-    elif not match(term[0], pattern[0]):  # nodes are not the same
+    elif term[0] != pattern[0]:  # nodes are not the same
+        message_explicit("Fail: {} and {} are not the same",
+            [term[0], pattern[0]], level="secondary")
+        
         raise PatternMatchFailed(
             "nodes '{}' and '{}' are not the same"
             .format(term[0], pattern[0]))
 
     elif len(term) != len(pattern): # nodes are not of same the arity
+        message_explicit("Fail: {} and {} are not the same arity",
+            [term[0], pattern[0]], level="secondary")
+
         raise PatternMatchFailed(
             "nodes '{}' and '{}'' are not of the same arity"
             .format(term[0], pattern[0]))
@@ -474,6 +591,7 @@ def unify(term, pattern, unifying = True ):
         #print("returning unifier: {}".format(unifier))
         return unifier
 
+
 #########################################################################
 def eval_actual_args(args):
 
@@ -703,7 +821,6 @@ def store_at_ix(structure_val, ix, value):
 
 #########################################################################
 def handle_builtins(node):
-
     (APPLY, (ID, opname), args) = node
     assert_match(APPLY, 'apply')
     assert_match(ID, 'id')
@@ -826,6 +943,13 @@ def handle_builtins(node):
 
 #########################################################################
 def handle_call(obj_ref, fval, actual_val_args, fname):
+    # Needed for later
+    global debugging
+
+    # function calls transfer control - save our caller's lineinfo
+    # we save the debug information here to preserve lineinfo between
+    # function calls between files.
+    old_lineinfo = state.lineinfo
 
     (FUNCTION_VAL, body_list, closure) = fval
     assert_match(FUNCTION_VAL, 'function-val')
@@ -838,13 +962,24 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
     # Note: we have to do this here because unifying
     # over the body patterns can introduce variable declarations,
     # think conditional pattern matching.
-    save_symtab = state.symbol_table.get_config()
+    state.symbol_table.saved_configs.append(
+        state.symbol_table.get_config()
+    )
+
     state.symbol_table.set_config(closure)
     state.symbol_table.push_scope({})
 
-    #lhh
-    #print('in handle_call')
-    #print("calling: {}\nwith: {}\n\n".format(fval,actual_val_args))
+    # Explicit message
+    message_explicit("Call: {}({})",
+        [fname, gen_t2s(actual_val_args)],
+        increase=True
+    )
+
+    # We want to return back to whatever tab level the function
+    # call started at, so, we grab this here to reset to at
+    # the return. This allows recursive functions to have
+    # the proper formatting
+    if debugging: cur_tab_level = debugger.tab_level
 
     # iterate over the bodies to find one that unifies with the actual parameters
     (BODY_LIST, (LIST, body_list_val)) = body_list
@@ -860,20 +995,39 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
         (PATTERN, p),
         (STMT_LIST, stmts)) = body_list_val[ i + 1]
 
+        message_explicit("Attempting to match {} with pattern {}",
+            [gen_t2s(actual_val_args), gen_t2s(p)], level="primary",
+            notify=True, increase=True
+        )
+        
         try:
+            # Attempt to unify the actual args and the pattern
             unifiers = unify(actual_val_args, p)
             unified = True
+
+            # Do our explicit message
+            message_explicit("Success! Matched function body", level="primary", decrease=True)
+
         except PatternMatchFailed:
+            # Reset the tab level
+            if explicit_enabled():
+                debugger.tab_level = cur_tab_level + 1
+
+            # Print the explicit messaging
+            message_explicit("Failed to match function body", level="tertiary", decrease=True)
+
             unifiers = []
             unified = False
 
         if unified:
             break
 
+    # Reset the tab level back to the function call's level
+    if debugging: debugger.tab_level = cur_tab_level
+
     if not unified:
         raise ValueError("actual argument '{}' not recognized by function '{}'"
                          .format(term2string(actual_val_args),fname))
-
     declare_formal_args(unifiers)
 
     # if we have an obj reference bind it to the
@@ -881,23 +1035,55 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
     if obj_ref:
         state.symbol_table.enter_sym('this', obj_ref)
 
+    # OWM: The following segment is a repeat of the bottom of this function.
+    # We need to do this because redundant patterns can break scope and
+    # some debugger and state features as they exit computation.
+
     # Check for useless patterns
-    if state.eval_redundancy:
-        check_redundancy(body_list, fname)
+    try:
+        if state.eval_redundancy:
+            old_debugging = debugging
+            debugging = False
+            check_redundancy(body_list, fname)
+            debugging = old_debugging
 
-    # execute the function
-    # function calls transfer control - save our caller's lineinfo
-    old_lineinfo = state.lineinfo
+    # Reset settings
+    except RedundantPatternFound as r:
+        debugging = old_debugging
+
+        # coming back from a function call - restore caller's env
+        state.lineinfo = old_lineinfo
+
+        # Keep debugger up to date
+        if debugging:
+            debugger.set_lineinfo(state.lineinfo)
+
+        state.symbol_table.pop_scope()
+        state.symbol_table.set_config(state.symbol_table.saved_configs.pop())
 
+        state.trace_stack.pop()
+
+        raise r
+
+    # execute the function
     global function_return_value
+
     try:
         function_return_value.append(None)
-        walk(stmts)
+
+        walk_stmt_list(stmts)
+
+        # Pop the return value
         val = function_return_value.pop()
+
+        # If we have one, set it as the retval
         if val:
             return_value = val
+
+        # Otherwhise default the return value to none
         else:
             return_value = ('none', None)
+
     except ReturnValue as val:
         # we got here because a return statement threw a return object
         function_return_value.pop()
@@ -905,11 +1091,21 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
 
     # coming back from a function call - restore caller's env
     state.lineinfo = old_lineinfo
+
+    # Keep debugger up to date
+    if debugging: debugger.set_lineinfo(state.lineinfo)
+
     state.symbol_table.pop_scope()
-    state.symbol_table.set_config(save_symtab)
+    state.symbol_table.set_config(state.symbol_table.saved_configs.pop())
 
     state.trace_stack.pop()
 
+    message_explicit("Return: {} from {}",
+            [("None" if (not return_value[1]) else gen_t2s(return_value)), 
+            fname],
+            decrease=True
+    )
+
     return return_value
 
 #########################################################################
@@ -917,12 +1113,10 @@ def declare_unifiers(unifiers):
     # walk the unifiers and bind name-value pairs into the symtab
 
     # TODO: check for repeated names in the unfiers
-
     for unifier in unifiers:
 
         #lhh
         #print("unifier: {}".format(unifier))
-
         (lval, value) = unifier
 
         if lval[0] == 'id':
@@ -944,35 +1138,65 @@ def declare_unifiers(unifiers):
 
         else:
             raise ValueError("unknown unifier type '{}'".format(lval[0]))
+    
+    # Explicit messaging for unifiers
+    if explicit_enabled():
+        # if we've unified anything
+        if unifiers:
+            # Create our format string and list of terms
+            fstring = ""
+            terms = []
+
+            # For each unifier except the last one
+            for (lval, value) in unifiers[:-1]:
+                # Add to our format string and our terms
+                fstring += "{} = {}, "
+                terms += [gen_t2s(lval), gen_t2s(value)]
+
+            # Get the last unifier and add it to the terms and fstring
+            (lval, value) = unifiers[-1]
+            fstring += "{} = {}"
+            terms += [gen_t2s(lval), gen_t2s(value)]
+
+            # Print our message
+            message_explicit(fstring, terms)
 
 #########################################################################
 # node functions
 #########################################################################
 def global_stmt(node):
+    notify_debugger()
 
     (GLOBAL, (LIST, id_list)) = node
     assert_match(GLOBAL, 'global')
     assert_match(LIST, 'list')
 
+    global_str = ""
+
     for id_tuple in id_list:
         (ID, id_val) = id_tuple
         if state.symbol_table.is_symbol_local(id_val):
             raise ValueError("'{}' is already local, cannot be declared global"
                              .format(id_val))
         state.symbol_table.enter_global(id_val)
+        global_str += "{}, ".format(id_val)
+    
+    message_explicit("Global defs: {}", [global_str[:-1]] )
 
 #########################################################################
 def assert_stmt(node):
+    notify_debugger()
 
     (ASSERT, exp) = node
     assert_match(ASSERT, 'assert')
 
     exp_val = walk(exp)
+
     # mapping asteroid assert into python assert
     assert exp_val[1], 'assert failed'
-
 #########################################################################
 def unify_stmt(node):
+    notify_debugger()
 
     (UNIFY, pattern, exp) = node
     assert_match(UNIFY, 'unify')
@@ -980,18 +1204,23 @@ def unify_stmt(node):
     term = walk(exp)
 
     unifiers = unify(term, pattern)
+
     declare_unifiers(unifiers)
 
 #########################################################################
 def return_stmt(node):
+    notify_debugger()
 
     (RETURN, e) = node
     assert_match(RETURN, 'return')
+    
+    retval = walk(e)
 
-    raise ReturnValue(walk(e))
+    raise ReturnValue(retval)
 
 #########################################################################
 def break_stmt(node):
+    notify_debugger()
 
     (BREAK,) = node
     assert_match(BREAK, 'break')
@@ -1000,26 +1229,30 @@ def break_stmt(node):
 
 #########################################################################
 def throw_stmt(node):
+    notify_debugger()
 
     (THROW, object) = node
     assert_match(THROW, 'throw')
+    
+    throw_object = walk(object)
 
-    raise ThrowValue(walk(object))
+    raise ThrowValue(throw_object)
 
 #########################################################################
 def try_stmt(node):
+    notify_debugger()
 
     (TRY,
      (STMT_LIST, try_stmts),
      (CATCH_LIST, (LIST, catch_list))) = node
 
+    stepping = debugger_has_stepped()
     try:
-        walk(try_stmts)
+        walk_stmt_list(try_stmts)
 
     # NOTE: in Python the 'as inst' variable is only local to the catch block???
     # NOTE: we map user visible Python exceptions into standard Asteroid exceptions
     #       by constructing Exception objects - see prologue.ast
-
     except ThrowValue as inst:
         except_val = inst.value
         inst_val = inst
@@ -1099,7 +1332,7 @@ def try_stmt(node):
             pass
         else:
             declare_unifiers(unifiers)
-            walk(catch_stmts)
+            walk_stmt_list(catch_stmts, step_state=stepping)
             return
 
     # no exception handler found - rethrow the exception
@@ -1107,6 +1340,7 @@ def try_stmt(node):
 
 #########################################################################
 def loop_stmt(node):
+    notify_debugger()
 
     (LOOP, body_stmts) = node
     assert_match(LOOP, 'loop')
@@ -1114,13 +1348,15 @@ def loop_stmt(node):
     (STMT_LIST, body) = body_stmts
 
     try:
+        stepping = debugger_has_stepped()
         while True:
-            walk(body)
+            walk_stmt_list(body, step_state=stepping)
     except Break:
         pass
 
 #########################################################################
 def while_stmt(node):
+    notify_debugger()
 
     (WHILE, cond_exp, body_stmts) = node
     assert_match(WHILE, 'while')
@@ -1129,15 +1365,18 @@ def while_stmt(node):
     (STMT_LIST, body) = body_stmts
 
     try:
+        stepping = debugger_has_stepped()
+
         (COND_TYPE, cond_val) = map2boolean(walk(cond))
         while cond_val:
-            walk(body)
+            walk_stmt_list(body, step_state=stepping)
             (COND_TYPE, cond_val) = map2boolean(walk(cond))
     except Break:
         pass
 
 #########################################################################
 def repeat_stmt(node):
+    notify_debugger()
 
     (REPEAT, body_stmts, cond_exp) = node
     assert_match(REPEAT, 'repeat')
@@ -1146,8 +1385,9 @@ def repeat_stmt(node):
     (STMT_LIST, body) = body_stmts
 
     try:
+        stepping = debugger_has_stepped()
         while True:
-            walk(body)
+            walk_stmt_list(body, step_state=stepping)
             (COND_TYPE, cond_val) = map2boolean(walk(cond))
             if cond_val:
                 break
@@ -1157,6 +1397,7 @@ def repeat_stmt(node):
 
 #########################################################################
 def for_stmt(node):
+    notify_debugger()
 
     (FOR, (IN_EXP, in_exp), (STMT_LIST, stmt_list)) = node
     assert_match(FOR, 'for')
@@ -1185,6 +1426,7 @@ def for_stmt(node):
     #             print y.
     #      end for
     try:
+        stepping = debugger_has_stepped()
         for term in list_val:
             try:
                 unifiers = unify(term,pattern)
@@ -1192,12 +1434,14 @@ def for_stmt(node):
                 pass
             else:
                 declare_unifiers(unifiers)
-                walk(stmt_list)
+                walk_stmt_list(stmt_list, step_state=stepping)
+                
     except Break:
         pass
 
 #########################################################################
 def if_stmt(node):
+    notify_debugger()
 
     (IF, (LIST, if_list)) = node
     assert_match(IF, 'if')
@@ -1215,11 +1459,11 @@ def if_stmt(node):
         (BOOLEAN, cond_val) = map2boolean(walk(cond))
 
         if cond_val:
-            walk(stmts)
+            walk_stmt_list(stmts)
             break
-
 #########################################################################
 def struct_def_stmt(node):
+    notify_debugger()
 
     (STRUCT_DEF, (ID, struct_id), (MEMBER_LIST, (LIST, member_list))) = node
     assert_match(STRUCT_DEF, 'struct-def')
@@ -1239,12 +1483,14 @@ def struct_def_stmt(node):
             (DATA, (ID, member_id)) = member
             struct_memory.append(('none', None))
             member_names.append(member_id)
+
         elif member[0] == 'unify':
             (UNIFY, (ID, member_id), function_exp) = member
             # Note: we have to bind a function VALUE into the structure memory
             function_val = walk(function_exp)
             struct_memory.append(function_val)
             member_names.append(member_id)
+
         elif member[0] == 'noop':
             pass
         else:
@@ -1256,6 +1502,34 @@ def struct_def_stmt(node):
 
     state.symbol_table.enter_sym(struct_id, struct_type)
 
+#########################################################################
+def import_stmt(node):
+    notify_debugger()
+
+    global debugging
+    old_debugging = debugging
+    debugging = False
+
+    (LIST, inlist) = node
+    assert_match(LIST, 'import_stmt')
+
+    for e in inlist:
+        walk(e)
+
+    debugging = old_debugging
+
+    return
+
+#########################################################################
+def top_level_exp_stmt(node):
+
+    (TOP_LEVEL_EXP, exp) = node
+    assert_match(TOP_LEVEL_EXP, 'top-level-exp')
+
+    notify_debugger()
+
+    return walk(exp)
+
 #########################################################################
 def eval_exp(node):
 
@@ -1285,7 +1559,6 @@ def eval_exp(node):
 
 #########################################################################
 def apply_exp(node):
-
     (APPLY, f, arg) = node
     assert_match(APPLY, 'apply')
 
@@ -1346,6 +1619,7 @@ def apply_exp(node):
                    ('object-memory', ('list', object_memory)))
         # if the struct has an __init__ function call it on the object
         # NOTE: constructor functions do not have return values.
+
         if '__init__' in member_names:
             slot_ix = member_names.index('__init__')
             init_fval = struct_memory[slot_ix]
@@ -1501,54 +1775,39 @@ def to_list_exp(node):
     (TOLIST,
      (START, start),
      (STOP, stop),
-     (STRIDE, stride)) = node
+     (STEP, step)) = node
 
     assert_match(TOLIST, 'to-list')
     assert_match(START, 'start')
     assert_match(STOP, 'stop')
-    assert_match(STRIDE, 'stride')
+    assert_match(STEP, 'step')
 
     (START_TYPE, start_val, *_) = walk(start)
     (STOP_TYPE, stop_val, *_) = walk(stop)
-    (STRIDE_TYPE, stride_val, *_) = walk(stride)
+    (STEP_TYPE, step_val, *_) = walk(step)
 
-    if START_TYPE != 'integer' or STOP_TYPE != 'integer' or STRIDE_TYPE != 'integer':
-        raise ValueError("only integer values allowed in start, stop, or stride")
+    if START_TYPE != 'integer' or STOP_TYPE != 'integer' or STEP_TYPE != 'integer':
+        raise ValueError("only integer values allowed in start, stop, or step")
 
     out_list_val = []
 
-    # If our stride val is > 0
-    if int(stride_val) > 0: # generate the list
-        # Get the [i]nitial inde[x] and [e]nd inde[x]
+    if int(step_val) > 0: # generate the list
         ix = int(start_val)
-        ex = int(stop_val)
-
-        # Get the stride_val
-        stride_val = int(stride_val)
-
-        # Change the direction of the stride value based on the
-        # ends of the range. I.e. 5->1 has an implicit direction
-        # of -1, 1->5 has a direction of +1
-        direction = (1 if ix < ex else -1)
-        stride_val *= direction
+        while ix <= int(stop_val):
+            out_list_val.append(('integer', ix))
+            ix += int(step_val)
 
-        # We need to modify the ending index to acccount for python
-        # ranges. For example, for 1->10 we want range(1, 10 + 1).
-        # Or, for the opposite, we want range(10, 1 - 1) to give
-        # us the full inclusive range. Thus, we can just add our
-        # direction
-        new_ex = ex + direction
-        for i in range(ix, new_ex, stride_val):
-            out_list_val.append( ('integer', i) )
+    elif int(step_val) == 0: # error
+        raise ValueError("step size of 0 not supported")
 
-    elif int(stride_val) == 0: # error
-        raise ValueError("stride size of 0 not supported")
-
-    elif int(stride_val) < 0: # generate the list
-        raise ValueError("negative stride sizes are not supported")
+    elif int(step_val) < 0: # generate the list
+        ix = int(start_val)
+        while ix >= int(stop_val):
+            out_list_val.append(('integer', ix))
+            ix += int(step_val)
 
     else:
-        raise ValueError("'{}' not a valid stride value".format(stride_val))
+        raise ValueError("{} not a valid step value".format(step_val))
 
     return ('list', out_list_val)
 
@@ -1597,6 +1856,9 @@ def process_lineinfo(node):
     (LINEINFO, lineinfo_val) = node
     assert_match(LINEINFO, 'lineinfo')
 
+    if debugging:
+        debugger.set_lineinfo(lineinfo_val)
+
     #lhh
     #print("lineinfo: {}".format(lineinfo_val))
 
@@ -1628,7 +1890,6 @@ def constraint_exp(node):
 
 #########################################################################
 def set_ret_val(node):
-
     (SET_RET_VAL, exp) = node
     assert_match(SET_RET_VAL,'set-ret-val')
 
@@ -1637,8 +1898,63 @@ def set_ret_val(node):
     function_return_value.pop()
     function_return_value.append(val)
 
+    # Debugger keeps track of the most recent
+    # return value
+    if debugging and val:
+        debugger.retval = term2string(val)
+
     return
 
+#########################################################################
+def walk_stmt_list(stmts, step_state=None):
+    # step_stae is a flag to tell us if we actually
+    # want to step through to the next line of the stmt
+    # list while debugging
+    
+    # We only want to do this if we're debugging
+    # and we've stepped through to this point or
+    # have continued to this point. Basically, this
+    # flag tells us if we've stepped or continued 
+    # into this function call, effectively telling
+    # the debugger to treat it as the top level
+
+    # This also allows us to keep loop execution
+    # control working with the debugger. Meaning
+    # we can do a "next" on the last line in a 
+    # loop and go back to the top as opposed to the
+    # next line after the loop
+    if step_state:
+        stepping = step_state
+    else:
+        stepping = debugger_has_stepped()
+
+    for s in stmts[1]:
+
+        if debugging:
+            # Set the debugger's info reflect new "top level"
+            if stepping: debugger.set_top_level(True)
+
+            # If we've hit a return and we're in continue-until-return
+            # mode, notify the debugger
+
+            # TODO: (OWM) get this working for implicit returns as well.
+            # This currently doesn't work because you can have an implicit
+            # return before a return statement. In this case, the return
+            # statement consumes the return behavior because it is the
+            # final statement in the stmt_list
+
+            # OWM -- This is still true but I'm not sure the fix for it
+            # nor if it should even be fixed
+
+            # The statement is a return the debugger is executing until return
+            # then notify the debugger
+            if debugging and \
+                debugger.exc['RETURN'] and s[0] == 'return':
+                
+                notify_debugger(at_return=True)
+            
+        walk(s)
+
 #########################################################################
 # walk
 #########################################################################
@@ -1648,8 +1964,12 @@ def walk(node):
 
     if type == 'clear-ret-val':
         # implemented here instead of dictionary for efficiency reasons
-        global function_return_value
-        function_return_value.pop()
+        global function_return_value    
+
+        # If we have no function return value, then append None    
+        if function_return_value != []:
+            function_return_value.pop()
+        
         function_return_value.append(None)
         return
     elif type in dispatch_dict:
@@ -1658,6 +1978,43 @@ def walk(node):
     else:
         raise ValueError("feature '{}' not yet implemented".format(type))
 
+#########################################################################
+# walk_program
+#########################################################################
+def walk_program(node):
+    """
+    This function exists mostly as a performance
+    boost to unify. Running the program through
+    this function overrides the debugger wrapping
+    on unify.
+    """
+    global unify
+    unify = __unify
+
+    walk(node)
+
+#########################################################################
+# debug_walk
+#########################################################################
+def debug_walk(node, dbg):
+    """
+    This function allows us to keep the top-level distinction
+    at the program level.
+    """
+    global debugging, debugger, unify
+    debugging, debugger = (True, dbg)
+
+    unify = debug_unify
+
+    (LIST, inlist) = node
+
+    for e in inlist:
+        # We want to differentiate between top level and nested
+        # statements. So, we run the list of statements outright
+        # so we can control when we know we are at the top level
+        debugger.set_top_level(True)
+        walk(e)
+
 # a dictionary to associate tree nodes with node functions
 dispatch_dict = {
     # statements - statements do not produce return values
@@ -1677,8 +2034,10 @@ def walk(node):
     'throw'         : throw_stmt,
     'try'           : try_stmt,
     'struct-def'    : struct_def_stmt,
+    'top-level-exp' : top_level_exp_stmt,
     # expressions - expressions do produce return values
     'list'          : list_exp,
+    'import_stmt'   : import_stmt,
     'tuple'         : tuple_exp,
     'to-list'       : to_list_exp,
     'head-tail'     : head_tail_exp,
@@ -1791,4 +2150,183 @@ def check_redundancy( body_list, f_name ):
                 pass
             else:                               #CONFLICTION
                 raise RedundantPatternFound( ptrn_h , ptrn_l , f_name, location_h, location_l )
+
 #######################################################################################
+# *** Asteroid Debugger (ADB) helper functions ***
+# 
+# message_explicit:
+#   The main messaging function for explicit mode. Operates in a similar way the
+#   string.format function does. Read the function's internal docstring for more
+#   information.
+# 
+# notify_debugger:
+#   Notifies the debugger of the current program position. If the debugger is
+#   accepting notifications at the time of call, the debugger will pause and
+#   run the interactive prompt.
+# 
+# notify_explicit:
+#   Similar to notify_debugger but only runs in explicit mode.
+# 
+# explicit_enabled:
+#   A simple helper function. Equivalent to `debugging and debugger.explicit_enabled`
+# 
+# decrease_tab_level:
+#   A simple helper function to manage debugger tab level decrementing.
+#
+# debugger_has_stepped::
+#   Helper function that returns if the debugger has stepped into a function
+#   body or other compound statement
+#
+# gen_t2s:
+#   A generator function used in place of term2string in message_explicit calls.
+#   This allows the computation to be deferred or completely ignored. Much like
+#   a function returning a lambda function
+#
+# debug_unify:
+#   A wrapper for the normal unify function that automatically decreases
+#   the tab level of failed pattern matches.
+#
+#######################################################################################
+def message_explicit(fmt_message, terms=None, level="primary", 
+    increase=False, decrease=False, notify=False):
+
+    """
+    Message explicit expects 1-3 main arguments
+
+    1. A string, which can be a format string or a normal string.
+    2. (Optional) A list of terms to express for a given format string
+    3. The message level (Defaults to primary)
+
+    Format terms can be either plain strings or generators. If a gener-
+    ator is supplied, this function will express it
+
+    fmt_message is a list of terms to be applied in the same
+    way as .format
+
+    --------------------------------------------------------------
+    This function also supports three other options that shorten
+    common development patterns.
+
+    increase: Increases the tab level after the message is written
+    decrease: Decreases the tab level before the message is written
+    notify: Makes a notify_explicit call to stop the debugger within
+            a pattern
+    """
+    from types import GeneratorType
+
+    if explicit_enabled():
+        if decrease: decrease_tab_level()
+
+        if not terms:
+            debugger.message_explicit(fmt_message, level)
+        else:
+            expressed_terms = []
+            for t in terms:
+                if isinstance(t, GeneratorType):
+                    expressed_terms.append(next(t))
+                else:
+                    expressed_terms.append(t)
+            
+            expressed_string = fmt_message.format(*expressed_terms)
+            debugger.message_explicit(expressed_string, level)
+        
+        if increase:    debugger.tab_level += 1
+        if notify:      notify_explicit()
+
+#########################################################################
+def notify_debugger(at_return=False):
+    """
+    If the debugger is accepting notifications at the time of call,
+    the debugger will pause and run the interactive prompt.
+    """
+    if debugging:
+        # We need to save the old lineinfo in case we go into a REPL
+        old_lineinfo = state.lineinfo
+
+        debugger.notify(at_return)
+
+        # Reset our lineinfo
+        state.lineinfo = old_lineinfo
+        debugger.set_lineinfo(state.lineinfo)
+
+#########################################################################
+def notify_explicit():
+    """
+    If the debugger is accepting notifications at the time of call and
+    the debugger is in explicit mode, the debugger will pause and run
+    the interactive prompt.
+    """
+    if debugging and explicit_enabled:
+        old_lineinfo = state.lineinfo
+        
+        debugger.notify_explicit()
+
+        # Reset our lineinfo
+        state.lineinfo = old_lineinfo
+        debugger.set_lineinfo(state.lineinfo)
+
+#########################################################################
+def explicit_enabled():
+    """
+    Helper function that returns the state
+    of explicit mode if debugging is enableds
+    """
+    return debugging and debugger.explicit_enabled
+
+#########################################################################
+def gen_t2s(node):
+    """
+    Generator function for term2string. This cuts down on
+    runtime as it defers term2string computations to when
+    they're actually needed.
+    """
+    yield term2string(node)
+
+#########################################################################
+def debugger_has_stepped():
+    """
+    Returns the step/continue state of the debugger
+    """
+    return debugging and \
+            (debugger.exc['STEP'] or debugger.exc['CONTINUE'])
+
+#########################################################################
+def decrease_tab_level():
+    """
+    Decreases/handles the debugger's tab level
+    """
+    if debugging:
+        debugger.tab_level = debugger.tab_level - 1
+
+        if debugger.tab_level < 0:
+            debugger.tab_level = 0
+
+#########################################################################
+def debug_unify(term, pattern, unifying = True):
+    '''
+    Proxy function for unify to make the debugger's formatting
+    work properly
+
+    OWM - This proxy function allows us to wrap unification
+    in an except block and catch when PatternMatchFailed exceptions
+    occur. In this situation, we need to decrease the tab level
+    and then re-raise the exception. Being able to "unravel"
+    tab levels is essential to making things look good.
+
+    In normal, non-debugging execution, this function is overridden
+    and not used.
+    '''
+
+    # Try to call the actual unify function
+    try:
+        return __unify(term, pattern, unifying)
+
+    # If there's a pattern match failed, decrease
+    # the tab level and reraise the exception
+    except PatternMatchFailed as r:
+        decrease_tab_level()
+        raise r
+
+    # If there's a normal exception, reraise it
+    except Exception as e:
+        raise e
\ No newline at end of file
diff --git a/docs/ADB.rst b/docs/ADB.rst
new file mode 100644
index 00000000..50abc4d2
--- /dev/null
+++ b/docs/ADB.rst
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+..
+   *** DO NOT EDIT; MACHINE GENERATED ***
+
+
+===================
+ADB Reference Guide
+===================
+The Asteroid Debugger (ADB) 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
+* Stack frame inspection
+* Evaluation of arbitrary Asteroid code
+* Macros
+* REPL Instances
+
+ADB features a unique "explicit" mode which details much of the pattern matching and underlying
+mechanics of Asteroid allow developers to debug pattern matches. Explicit mode
+details many of the steps of pattern matching, function calling, statement execution, and
+return values. Explicit mode is very experimental and is under current research. As a result,
+explicit mode is currently incomplete.
+
+Usage
+=======
+``asteroid --adb <FILENAME>``
+
+``asteroid -g <FILENAME>``
+
+Debugging sessions
+==================
+The debugger's prompt ``(ADB)`` allows the user to enter commands to effect the source environment
+and debugger behavior.
+
+Each debugging session will have a few pieces of information:
+
+**Filename and line number**
+::
+
+	[/home/user/test.ast (1)]
+
+**The current line which will be executed**
+::
+
+    -->> let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+
+**The command prompt**
+::
+
+  (ADB)
+
+ADB runs like any other debugger, here's a small example session where
+we see running commands, listing the program, and setting and continuing to breakpoints.
+::
+
+  [/home/user/test.ast (1)]
+  -->> let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+  (ADB) ll
+  ----- Program Listing -----
+  >  1 let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+     2
+     3 let d = pattern %[(x:*p, y:*p, z:*p)]%.
+     4 let x:*p = 99.
+     5
+     6 let t:*d = (1,2,3).
+     7 [EOF]
+  (ADB) break 6
+  (ADB) continue
+  ----- Breakpoint -----
+  [/home/user/test.ast (6)]
+  -->> let t:\*d = (1,2,3).
+  (ADB)
+
+Commands
+========
+Below is a list of the commands available to the debugger. Most of which can be shortened.
+These shortenings are shown in parenthesis (`c(ommand)`).
+
+List
+----
+``l(ist)`` lists the lines around the currently executing line
+
+Longlist
+---------
+``ll (longlist)`` lists the entire program
+
+Step
+---------
+``s(tep)`` through to the next statement or through function call.
+
+Continue
+---------
+``c(ontinue)`` ``cont(inue)`` continue execution until the next breakpoint.
+
+Next
+---------
+``n(ext)`` continue onto the next executing line at the current scope.
+
+Until
+---------
+``u(ntil) ?lineno`` By default, continue execution until a line with a greater number
+than the current one is reached.
+
+Given an optional line number, continue execution until a line number greater than
+or equal to that number is reached
+
+Return
+---------
+``(r(et))urn`` Continue execution until the return of the current function is reached
+
+Breakpoints
+------------
+``b(reak) number\*`` set a breakpoint at one or more lines. Running without any arguments
+lists your breakpoints.
+
+Example: ``b 1 2 3``, ``break``.
+
+Conditional breakpoints can be set in the same way, just attach ``if eval("condition")``
+after each breakpoint number.
+
+Example:
+::
+
+    -- Set a conditional breakpoint on 10 and normal breakpoints on 11, 15, and 23.
+    b 10 if eval("x == 123") 11 15 23
+
+Delete
+---------
+``d(elete) (number)+`` ``del(ete) (number)+`` delete a list of breakpoints.
+
+Example:
+::
+
+    del 1 5 8 9
+
+Macro
+---------
+``macro (name) (command list)``. Define a macro. Running just ``macro`` lists your macros.
+
+Example macro that continues to a breakpoint and prints the value of ``x``:
+::
+
+    macro gox = c; eval("io@println(x)");
+
+Eval
+---------
+`eval("asteroid code")` Evaluate the asteroid code between quotes. Works exactly like a single-line
+repl.
+
+Example, print out the value of ``x``:
+::
+
+    eval("x")
+
+!
+---------
+``!`` Open up a repl in the current context
+
+\_\_retval\_\_
+--------------
+``(_)_retval`` Print the most recent return value
+
+Help
+---------
+``h(elp) (command)?`` gives help for a given command. Running just ``help`` shows all available commands.
+Example:
+::
+
+    h macro
+    help break
+
+Retval
+---------
+``((r)et)val`` Prints the most recent return value
+
+<
+---------
+``<`` move up one stack frame
+
+>
+---------
+``>`` move down one stack frame
+
+Where
+---------
+``w(here)`` displays the frame stack and the currently active frame.
+
+Explicit
+---------
+``e(xplicit) (on|off)?`` By default, this command run without an argument toggles
+explicit mode. If given a literal ``on`` or ``off``, explicit mode will be switched
+to the corresponding state.
+
+Explicit
+::
+
+    -- Toggle Explicit mode
+    explicit
+    e
+
+    -- Turn on/off
+    explicit on
+    e on
+    explicit off
+    e off
+
+Quit
+---------
+``q(uit)`` Quits the current ADB session
diff --git a/docs/ADB.txt b/docs/ADB.txt
new file mode 100644
index 00000000..5318b9e4
--- /dev/null
+++ b/docs/ADB.txt
@@ -0,0 +1,218 @@
+/******************************************************************
+  This is the source file from which the action doc is generated.
+  We use cpp to insert live code snippets into the document.
+  In order to generate the action doc run the following command
+  on a Unix-like system:
+
+  cpp -w -P "ADB.txt" > "ADB.rst"
+
+ ******************************************************************/
+#include "header.txt"
+
+===================
+ADB Reference Guide
+===================
+The Asteroid Debugger (ADB) 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
+* Stack frame inspection
+* Evaluation of arbitrary Asteroid code
+* Macros
+* REPL Instances
+
+ADB features a unique "explicit" mode which details much of the pattern matching and underlying
+mechanics of Asteroid allow developers to debug pattern matches. Explicit mode
+details many of the steps of pattern matching, function calling, statement execution, and
+return values. Explicit mode is very experimental and is under current research. As a result,
+explicit mode is currently incomplete.
+
+Usage
+=======
+``asteroid --adb <FILENAME>``
+
+``asteroid -g <FILENAME>``
+
+Debugging sessions
+==================
+The debugger's prompt ``(ADB)`` allows the user to enter commands to effect the source environment
+and debugger behavior.
+
+Each debugging session will have a few pieces of information:
+
+**Filename and line number**
+::
+
+	[/home/user/test.ast (1)]
+
+**The current line which will be executed**
+::
+
+    -->> let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+
+**The command prompt**
+::
+
+  (ADB)
+
+ADB runs like any other debugger, here's a small example session where
+we see running commands, listing the program, and setting and continuing to breakpoints.
+::
+
+  [/home/user/test.ast (1)]
+  -->> let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+  (ADB) ll
+  ----- Program Listing -----
+  >  1 let p = pattern %[(x:%integer) if x > 0 and x < 100]%.
+     2
+     3 let d = pattern %[(x:*p, y:*p, z:*p)]%.
+     4 let x:*p = 99.
+     5
+     6 let t:*d = (1,2,3).
+     7 [EOF]
+  (ADB) break 6
+  (ADB) continue
+  ----- Breakpoint -----
+  [/home/user/test.ast (6)]
+  -->> let t:\*d = (1,2,3).
+  (ADB)
+
+Commands
+========
+Below is a list of the commands available to the debugger. Most of which can be shortened.
+These shortenings are shown in parenthesis (`c(ommand)`).
+
+List
+----
+``l(ist)`` lists the lines around the currently executing line
+
+Longlist
+---------
+``ll (longlist)`` lists the entire program
+
+Step
+---------
+``s(tep)`` through to the next statement or through function call.
+
+Continue
+---------
+``c(ontinue)`` ``cont(inue)`` continue execution until the next breakpoint.
+
+Next
+---------
+``n(ext)`` continue onto the next executing line at the current scope.
+
+Until
+---------
+``u(ntil) ?lineno`` By default, continue execution until a line with a greater number
+than the current one is reached.
+
+Given an optional line number, continue execution until a line number greater than
+or equal to that number is reached
+
+Return
+---------
+``(r(et))urn`` Continue execution until the return of the current function is reached
+
+Breakpoints
+------------
+``b(reak) number\*`` set a breakpoint at one or more lines. Running without any arguments
+lists your breakpoints.
+
+Example: ``b 1 2 3``, ``break``.
+
+Conditional breakpoints can be set in the same way, just attach ``if eval("condition")``
+after each breakpoint number.
+
+Example:
+::
+
+    -- Set a conditional breakpoint on 10 and normal breakpoints on 11, 15, and 23.
+    b 10 if eval("x == 123") 11 15 23
+
+Delete
+---------
+``d(elete) (number)+`` ``del(ete) (number)+`` delete a list of breakpoints.
+
+Example:
+::
+
+    del 1 5 8 9
+
+Macro
+---------
+``macro (name) (command list)``. Define a macro. Running just ``macro`` lists your macros.
+
+Example macro that continues to a breakpoint and prints the value of ``x``:
+::
+
+    macro gox = c; eval("io@println(x)");
+
+Eval
+---------
+`eval("asteroid code")` Evaluate the asteroid code between quotes. Works exactly like a single-line
+repl.
+
+Example, print out the value of ``x``:
+::
+
+    eval("x")
+
+!
+---------
+``!`` Open up a repl in the current context
+
+\_\_retval\_\_
+--------------
+``(_)_retval`` Print the most recent return value
+
+Help
+---------
+``h(elp) (command)?`` gives help for a given command. Running just ``help`` shows all available commands.
+Example:
+::
+
+    h macro
+    help break
+
+Retval
+---------
+``((r)et)val`` Prints the most recent return value
+
+<
+---------
+``<`` move up one stack frame
+
+>
+---------
+``>`` move down one stack frame
+
+Where
+---------
+``w(here)`` displays the frame stack and the currently active frame.
+
+Explicit
+---------
+``e(xplicit) (on|off)?`` By default, this command run without an argument toggles
+explicit mode. If given a literal ``on`` or ``off``, explicit mode will be switched
+to the corresponding state.
+
+Explicit
+::
+
+    -- Toggle Explicit mode
+    explicit
+    e
+
+    -- Turn on/off
+    explicit on
+    e on
+    explicit off
+    e off
+
+Quit
+---------
+``q(uit)`` Quits the current ADB session
diff --git a/docs/Asteroid in Action.rst b/docs/Asteroid in Action.rst
index 6f668fa9..915c28ed 100644
--- a/docs/Asteroid in Action.rst
+++ b/docs/Asteroid in Action.rst
@@ -15,6 +15,7 @@
 ..
    *** DO NOT EDIT; MACHINE GENERATED ***
 
+
 .. highlight:: none
 
 Asteroid in Action
@@ -2141,7 +2142,7 @@ We can use an index vector to accomplish  the same thing,
   load system math.
 
   let a = [20 to 30].
-  let array = a @[1 to a @length()-1 stride 2] .
+  let array = a @[1 to a @length()-1 step 2] .
   io @println array.
 
   assert (array == [21,23,25,27,29]).
diff --git a/docs/Installing and Running.rst b/docs/Installing and Running.rst
index 929e12a4..e7bdcb36 100644
--- a/docs/Installing and Running.rst
+++ b/docs/Installing and Running.rst
@@ -62,11 +62,10 @@ Asteroid program by typing::
 at the command prompt.  Asteroid also supports an interactive mode::
 
     $ asteroid
-    Asteroid Version 1.0.0
+    Asteroid Version 1.1.0
     Run "asteroid -h" for help
     Press CTRL+D to exit
     > load system io.
     > io @println "Hello, World!".
     Hello, World!
     >
-  
diff --git a/docs/Quickstart Tutorial.rst b/docs/Quickstart Tutorial.rst
index 7d861bdc..7018840a 100644
--- a/docs/Quickstart Tutorial.rst
+++ b/docs/Quickstart Tutorial.rst
@@ -1,3 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+..
+   *** DO NOT EDIT; MACHINE GENERATED ***
+
+
+.. highlight:: none
+
 .. role:: raw-html(raw)
    :format: html
 
@@ -18,8 +38,6 @@ and a variable as a pattern.  In that case the variable is simply instantiated w
 A very interesting aspect of pattern matching is that it provides a powerful way to inspect the values passed to a function.
 Consider the C++ function ``f`` defined here,
 ::
-  #include <iostream>
-
   void f(int x) {
     if (x == 0)
       std::cout << "zero\n";
@@ -113,8 +131,6 @@ Object-Oriented Programming in Asteroid
 ---------------------------------------
 The term object-oriented in programming means that code is broken up into classes and objects. Think of classes as **user defined data types**. While this may sound intimidating, there are many uses of object-oriented programming that can be used to help write efficient, clean code. For instance, there may be a time where you have to write code for software that pertains to families. While you could use tuples or arrays to represent this data, objects and classes are an even better way to achieve this feat. Take a look at this code in C++ that has the class for a family:
 ::
-     #include <string>
-
      class Family {
      public:
          std::string parent;
diff --git a/docs/Quickstart Tutorial.txt b/docs/Quickstart Tutorial.txt
new file mode 100644
index 00000000..1f689d41
--- /dev/null
+++ b/docs/Quickstart Tutorial.txt
@@ -0,0 +1,228 @@
+/******************************************************************
+  This is the source file from which the reference guide is
+  generated.  We use cpp to insert live code snippets into the
+  document. In order to generate the reference guide run the
+  following command on a Unix-like system in the directory of
+  this doc:
+
+  bash generate_docs
+
+ ******************************************************************/
+#include "header.txt"
+
+.. highlight:: none
+
+.. role:: raw-html(raw)
+   :format: html
+
+Welcome To Asteroid!
+====================
+Thank you for visiting our page about Asteroid! If you have gone to the `Asteroid Documentation <https://asteroid-lang.readthedocs.io/en/latest/>`_ then you have seen that Asteroid is a pattern-matching oriented language.
+If you have not heard of that programming paradigm (type of programming language) before, Asteroid is one of the first of its kind. Here we provide a brief introduction to Asteroid geared towards C++ programmers.
+
+Pattern Matching at the Core of Things
+--------------------------------------
+Pattern-matching is the idea of extracting values from a structure by applying a pattern to that structure.  If the pattern
+contains variables then these variables will be instantiated with values from the structure during a successful match.  Consider the structure ``(1,2)``.
+If we apply the pattern ``(1,x)`` to that structure then the ``1`` will be matched and the variable ``x`` will be instantiated
+with the value ``2``.  The interesting part about pattern matching is that they can also fail to match.  Consider again
+our structure ``(1,2)`` but now we want to apply the pattern ``(y,1)``.  This pattern match will fail because the second component of our structure and the pattern do not match.  Because of this mismatch the variable ``y`` will also not be instantiated.  Perhaps the simplest pattern match is between a value
+and a variable as a pattern.  In that case the variable is simply instantiated with the value.
+
+A very interesting aspect of pattern matching is that it provides a powerful way to inspect the values passed to a function.
+Consider the C++ function ``f`` defined here,
+::
+  void f(int x) {
+    if (x == 0)
+      std::cout << "zero\n";
+    else
+      std::cout << "not zero\n";
+  }
+
+If the integer value passed to the function is equal to zero then it prints out the word ``zero``, otherwise it prints out
+the words ``not zero``.
+Here the programmer has to use conditionals in order to decide which output to produce.  Using conditionals is not a bad practice, however,
+functions that use pattern tend to look less cluttered.
+
+Here is the same function written in Asteroid using pattern matching,
+::
+  load system io.
+
+  function f
+     with 0 do
+        io @println("zero").
+     with x do
+        io @println("not zero").
+     end
+
+Here the value ``0`` and the variable ``x`` appearing right after the ``with`` keywords are patterns that are applied to the function value.
+If the function is called with the value ``0`` then the value will be matched by the first pattern and the associated print statement is executed.
+If the value with which the function was called is anything but ``0`` then the second pattern will match and that associated print statement
+will be executed. A variable can be considered a pattern
+that will match any value (unless other conditions have been placed on that variable).
+
+The interesting part about patterns is that they can include all kinds of additional information like, for example, that the value a pattern is to
+match is a positive integer. Consider the following example of the recursive factorial implementation.  Let's look at that function in C++,
+::
+     int fact(int x) {
+       if (x == 0)
+          return 1;
+       else if (x > 0)
+          return x * fact(x-1);
+       else
+         throw "undefined for negative values";
+     }
+
+Factorials are only defined over positive integers.  So notice that in this case we have to declare the argument as an integer argument and then
+later in the function we need to make sure that the integer value being passed in is in the correct range of values in order for the computation to be successful.
+With pattern matching in Asteroid we can accomplish all this right in the patterns that we apply to the input argument,
+::
+   -- define patterns matching positive or negative integer values
+   let POS_INT = pattern %[(k:%integer) if k > 0]%.
+   let NEG_INT = pattern %[(k:%integer) if k < 0]%.
+
+   -- define our factorial function
+   function fact
+       with 0 do
+           return 1
+       with x:*POS_INT do            -- use first pattern
+           return x * fact(x-1).
+       with x:*NEG_INT do            -- use second pattern
+           throw Error("undefined for "+x).
+       end
+
+The first two lines of the program create the patterns that define what it means to be a positive or negative integer. For example, the first pattern will only match a value that is an integer whose value is larger than zero. Later in the program, these patterns get dereferenced (which means retrieved from where they are stored in memory) using the ``*`` operator. Notice that we have a similar setup here as with the ``f`` function we looked at earlier.  If the ``0`` pattern matches then we will just return the value ``1``. The line after that is saying "with the argument x and the pattern POS_INT (or in other words, if the argument is positive), recursively find the factorial of the number" and the last ``wth`` line is saying "with the argument x and the pattern NEG_INT (if the argument is negative), throw an error".
+Notice that patterns allow us to precisely define what we mean by positive or negative integers in one place and then use these patterns in our function.
+
+Pattern matching can be applied in a lot of places in Asteroid.  But one other place is perhaps more prevalent than any other, which is pattern
+matching in Asteroid's ``let`` statement.
+The ``let`` statement is Asteroid's version of the assignment statement with a twist though:  the left side of the ``=`` sign is not just a variable
+but is considered a pattern.  For simple assignments there is no discernible difference between assignments in Asteroid and assignments in other
+languages,
+::
+  let x = val.
+
+Here, the variable ``x`` will match the value stored in ``val``.  However, because the left side of the ``=`` sign is a pattern we
+can write something like this,
+::
+  let x:%[(k:%integer) if mod(k,2)==0]% = val.
+
+where ``x`` will only match the value of ``val`` if that value is an even integer value.  The fact that the left side of the ``=`` is a pattern allows
+us to write things like this,
+::
+   let 1 = 1.
+
+which simply states that the value ``1`` on the right can be matched by the pattern ``1`` on the left.  Having the ability to pattern match
+on literals is convenient for statements like these,
+::
+  let (1,x) = p.
+
+This ``let`` statement is only successful for values of ``p`` which are pairs where the first component of the pair is the value ``1``.
+**The thing to remember is that the let statement is not entirely equivalent to the assignment operator in other languages, even though it may look like that.**
+
+
+Object-Oriented Programming in Asteroid
+---------------------------------------
+The term object-oriented in programming means that code is broken up into classes and objects. Think of classes as **user defined data types**. While this may sound intimidating, there are many uses of object-oriented programming that can be used to help write efficient, clean code. For instance, there may be a time where you have to write code for software that pertains to families. While you could use tuples or arrays to represent this data, objects and classes are an even better way to achieve this feat. Take a look at this code in C++ that has the class for a family:
+::
+     class Family {
+     public:
+         std::string parent;
+         std::string child1;
+         std::string child2;
+
+         // constructor
+         Family(std::string p, std::string c1, std::string c2) {
+             this->parent = p;
+             this->child1 = c1;
+             this->child2 = c2;
+         }
+     };
+
+Now if you want to create an instance or object of the Family class, you could write this line to do so:
+::
+   Family *myfamily = new Family("Jim", "Bob", "Ann");
+
+where the properties parent is "Jim", child1 is "Bob" and child2 is "Ann". Now if you wanted to access one of these properties, you could do,
+::
+   std::cout << myfamily->child1; // while this looks intimidating, all this is doing is dereferencing child1
+
+Classes and objects are an easier way to store data that may not fit with any data structure that a language currently has.
+Asteroid implements object-orientation via structures, an approach it shares with the programming language Rust.
+In Asteroid the above example would be written as,
+::
+   structure Family with
+       data parent.
+       data child1.
+       data child2.
+
+       -- constructor
+       function __init__ with (p:%string, c1:%string, c2:%string) do
+          let this @parent = p.
+          let this @child1 = c1.
+          let this @child2 = c2.
+       end
+   end
+
+And you can create an object from that structure by doing,
+::
+   let myfamily = Family("Jim", "Bob", "Ann").
+
+Notice how similar the construction of objects are in both languages.
+**Think of structures in Asteroid as classes in C++, and in both languages these allow you to instantiate objects** (that means if you have programmed with classes and objects in C++, creating structures in Asteroid should be trivial). Something else to note is that similar to Rust and Go, **Asteroid does not have inheritance for classes**.  That is why programming in Asteroid is sometimes referred to as object-based programming rather than object-oriented programming.
+
+We can access substructures of objects with the access operator ``@``,
+::
+  io @println (myfamily @child1).
+
+which will print out the name of the first child.
+
+The name of the class above can now be considered a user defined data type and can appear wherever built-in data type names can appear.
+For instance it can appear in a pattern restricting the values a particular variable can take on,
+::
+  let f:%Family = myfamily.
+
+Since we are talking about the ``let`` statement in conjunction with objects, Asteroid allows pattern matching on objects!  This allows for
+easy access to substructures of objects,
+::
+     let Family(parent,first,second) = myfamily.
+
+     assert(parent is "Jim").
+     assert(first is "Bob").
+     assert(second is "Ann").
+
+Here we are matching the object stored in ``myfamily`` again the pattern ``Family(parent,first,second)`` and the variables will be instantiated
+with appropriate values from the ``data`` members of the object.
+
+Now that you understand the two different paradigms that Asteroid is made out of, you can start writing your programs in it and explore the versatility of patterns, pattern-matching and object-oriented programming.
+
+
+How to Get Started in Asteroid
+-------------------------------
+Now that you know what principles Asteroid is made of, you can now get started writing programs in it. Directions to install Asteroid can be found `here <https://asteroid-lang.readthedocs.io/en/latest/Installing%20and%20Running.html>`_.
+After you installed Asteroid correctly, you can write your first program. The first one you can write is a simple hello world program, which looks something like:
+::
+   load system io. -- header that allows the programmer to print things out to the screen and to accept input
+
+   io @println "Hello, World!".
+
+After you have written your first program, you can run the program by typing in the following line in your terminal:
+::
+   asteroid <name of program>
+
+where the name of the program is the name of the file that you want to run.
+
+**Make sure that you are in the same folder in your terminal of the file that you are trying to run!**
+
+Notice how the ``@`` symbol is used in two different places (this is common in programming languages, where one operator can be used multiple ways). In Asteroid, modules (which was the ``load system io.`` line at the top of our files) are actually objects, so to access a method in a module, you use the ``@`` symbol. So in this example, the module is the ``io`` module and we want to use the ``println`` method in that module, which is why you see the ``@`` symbol in there. **A module is a group of code that has already been written (typically by the developers of the language) which can be used again in other people's programs.**
+
+`Here <https://asteroid-lang.readthedocs.io/en/latest/Reference%20Guide.html#asteroid-modules>`_ is the complete list of modules in Asteroid.
+
+Some important things to note in Asteroid:
+
+* Most statements must end with a period (this is equivalent to using a semicolon in C++)
+* In order to print things, you must include the ``load system io.`` in your program before you attempt any output.
+* lines that start with ``--`` are comment lines
+* If you see a line that looks like (``x:%integer``), that is used to match any value of a given type. (The ``%integer`` pattern matches any integer value and can be used with any other type in Asteroid.)
+
+If you would like more information about Asteroid, please see the Asteroid `reference guide <https://asteroid-lang.readthedocs.io/en/latest/Reference%20Guide.html#>`_  and `user guide <https://asteroid-lang.readthedocs.io/en/latest/Asteroid%20User%20Guide.html>`_.
diff --git a/docs/Reference Guide.rst b/docs/Reference Guide.rst
index 46aab065..dfb8fd38 100644
--- a/docs/Reference Guide.rst
+++ b/docs/Reference Guide.rst
@@ -15,6 +15,7 @@
 ..
    *** DO NOT EDIT; MACHINE GENERATED ***
 
+
 .. highlight:: none
 
 Asteroid Reference Guide
@@ -464,18 +465,17 @@ Example,
 List Comprehensions
 %%%%%%%%%%%%%%%%%%%
 
-Syntax: ``start_exp TO end_exp (STRIDE exp)?``
+Syntax: ``start_exp TO end_exp (STEP exp)?``
 
 This expression constructs a list starting with an element given by the start expression
-up to the value of the end expression with a given stride.  If the stride expression
-is not given then a stride value of 1 is assumed.  The stride value is always a
-positive value.  The comprehension figures out how to apply the stride value to reach the
-end value from the start value.  The comprehension can be placed between optional square brackets.
+up to the value of the end expression with a given step.  If the step expression
+is not given then a step value of 1 is assumed. The comprehension can be placed between
+optional square brackets.
 
 Examples,
 ::
       let [0,1,2,3,4] = 0 to 4.
-      let [0,-2,-4,-6] = [0 to -6 stride 2].
+      let [0,-2,-4,-6] = [0 to -6 step -2].
 
 Function Calls
 %%%%%%%%%%%%%%
@@ -689,7 +689,7 @@ are written in quotes.
         (
            (IS pattern) |
            (IN exp) |               
-           (TO exp (STRIDE exp)?) |   
+           (TO exp (STEP exp)?) |   
         )?
 
   logic_exp0
@@ -762,7 +762,7 @@ Builtin Functions
 * Function ``range`` will compute a list of values depending on the input values:
 
   1. ``(start:%integer,stop:%integer)`` returns list ``[start to stop-1]``.
-  2. ``(start:%integer,stop:%integer,inc:%integer)`` returns list ``[start to stop-1 stride inc]``.
+  2. ``(start:%integer,stop:%integer,inc:%integer)`` returns list ``[start to stop-1 step inc]``.
   3. ``(stop:%integer)`` returns list ``[0 to stop-1]``.
 
 * Function ``getid`` returns the id (physical memory address) of any Asteroid object as an Asteroid integer.
@@ -1187,3 +1187,285 @@ Here is a simple example program for the ``vector`` module,
    
 
 which prints the value ``0``.
+
+Interfacing Asteroid with Python
+--------------------------------
+
+Asteroid allows integration with Python in one of two ways.  First, we can call the
+Asteroid interpreter from within a Python program and second, we can embed
+Python code directly within an Asteroid program. We start with looking at
+calling the Asteroid interpreter from Python.
+
+Calling Asteroid from Python
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Calling Asteroid from within a Python program is nothing more than calling Asteroid's ``interp``
+function with a string representing an Asteroid program as its argument.  In order to make this work you
+will have to make sure that the Python interpreter can find the Asteroid modules.
+Here we assume that you have installed Asteroid with the ``pip`` installer.
+Once you have installed Asteroid you will have to point the ``PYTHONPATH``
+environment variable to the directory where ``pip`` installed the Asteroid modules.
+You can easily find out where the modules are installed by issuing the ``show`` command,
+::
+    ubuntu$ pip3 show asteroid-lang
+    Name: asteroid-lang
+    Version: 1.1.0
+    Summary: A pattern-matching oriented programming language.
+    Home-page: https://asteroid-lang.org
+    Author: University of Rhode Island
+    Author-email: lutzhamel@uri.edu
+    License: None
+    Location: /home/ubuntu/.local/lib/python3.8/site-packages
+    Requires: numpy, pandas, matplotlib
+    Required-by:
+    ubuntu$
+
+The ``Location`` field tells us where the Asteroid modules have been installed.
+Under Ubuntu we can now create an environment variable that points to that directory as follows,
+::
+    ubuntu$ export PYTHONPATH=/home/ubuntu/.local/lib/python3.8/site-packages
+    ubuntu$
+
+Now that Python knows how to find the Asteroid modules we can import the
+Asteroid interpreter into any Python program using,
+::
+   from asteroid.interp import interp
+
+where the ``interp`` function takes a string representing of an Asteroid program
+as an argument.  Let's test drive this in the Python interactive shell,
+::
+    ubuntu$ python3
+    Python 3.8.10 (default, Nov 26 2021, 20:14:08)
+    [GCC 9.3.0] on 1
+    Type "help", "copyright", "credits" or "license" for more information.
+    >>> from asteroid.interp import interp
+    >>> interp('load system io. io @println "Hello, World!".')
+    Hello, World!
+    >>>
+
+For more detailed information on the ``interp`` function do a ``help(interp)``
+at the interactive Python prompt.
+Even though we have shown this example under Linux, analogous approaches
+should work on both Windows and macOS.
+
+Not only can we execute the Asteroid interpreter
+from Python but we can also access its state to look up the results of a
+computation for example.  Here is a slight variation of the program above
+where the Asteroid program computes the string value containing the greeting but
+we are actually printing the value from Python,
+::
+      # import Asteroid modules
+      from asteroid.interp import interp
+      from asteroid.state import state
+
+      # run the interpreter to compute the greeting string
+      interp('let s = "Hello World!".')
+
+      # retrieve the greeting string from the interpreter state
+      # notice the pair of values a symbol table lookup produces:
+      # one for the type of the value and one for the actual value
+      (type,val) = state.symbol_table.lookup_sym('s')
+      print(type)
+      print(val)
+
+The program prints out,
+::
+      string
+      Hello World!
+
+
+
+Embedding Python into an Asteroid Program
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Using Asteroid's ``escape`` expression allows us to embed arbitray Python
+code into an Asteroid program,
+::
+      -- Printing hello once from each environment
+
+      -- print hello from Asteroid
+      load system io.
+      io @println "Hello World from Asteroid!".
+
+      -- print hello from Python
+      escape
+      "
+      print('Hello World from Python!')
+      ".
+
+Please note that the format of the Python code in the escaped string should follow the
+same guidelines as the Python code embedded in strings handed to the Python `exec
+function <https://docs.python.org/3/library/functions.html#exec>`_.
+
+Not only does the ``escape`` expression give you access to the Python environment but
+it also gives you access to the current Asteroid interpreter state including its
+symbol table.  That means we can access any variable defined in the Asteroid
+environment from Python,
+::
+      let s = "Hello World!".
+
+      escape
+      "
+      (type, val) = state.symbol_table.lookup_sym('s')
+      print(type)
+      print(val)
+      ".
+
+Notice that a symbol table lookup produces a pair of values where the first value
+represents the type of the value stored in the symbol table and the second value
+is the actual value stored.  In this case our program prints out,
+::
+      string
+      Hello World!
+
+That is the type of the value is a string and the value is the actual string ``Hello World!``.
+
+Since ``escape`` represents an expression we can also return values from the
+Python code using a special ``__retval__`` variable.  The only trick is that
+we have to remember that values in Asteroid are pairs consisting of type information
+and values.  Here is a very simple program that exercises that part of the Python API,
+::
+      load system io.
+
+      let i = escape
+      "
+      global __retval__  # access the return value register
+
+      __retval__ = ('integer', 101)
+      ".
+
+      io @println i.
+
+This program will print out the value ``101`` from Asteroid even though that value
+was created within the Python environment.  Notice that we have to access the
+return value register ``__retval__`` with the ``global`` statement in the Python code.
+
+We can pull all of this together and write an Asteroid function that performs its
+computations in Python,
+::
+      function inc with i do return escape
+      "
+      # access return value register
+      global __retval__
+      # lookup the value of the formal argument
+      (type, val) = state.symbol_table.lookup_sym('i')
+
+      # only perform the increment if the value is an integer
+      if type != 'integer':
+         raise ValueError('not an integer')
+      else:
+         __retval__ = (type, val+1)
+      ".
+      end
+
+      -- call inc and make sure the result is correct
+      let k = inc(1)
+      assert(k == 2).
+
+Of course the function is just an illustration of how to use the Python API.  This
+type of computation is much easier to express in Asteroid directly,
+::
+      function inc
+         with i:%integer do
+            i+1
+         end
+
+      let k = inc(1)
+      assert(k == 2).
+
+The Foreign Type Tag
+^^^^^^^^^^^^^^^^^^^^
+
+When working in the hybrid Asteroid-Python environment it is sometimes useful to be able to embed values
+in an Asteroid program that have no direct representation in Asteroid.  This is where the ``foreign``
+type tage comes into play.  Consider the following program that uses Pandas dataframes within an
+Asteroid program,
+::
+
+      ------------------------------------------------------------------------
+      function pack
+      ------------------------------------------------------------------------
+      -- this function packs four real values into a Pandas dataframe
+      with (a:%real,b:%real,c:%real,d:%real) do return escape
+      "
+      global __retval__
+      # we can ignore type info here because we checked it above
+      (_, aval) = state.symbol_table.lookup_sym('a')
+      (_, bval) = state.symbol_table.lookup_sym('b')
+      (_, cval) = state.symbol_table.lookup_sym('c')
+      (_, dval) = state.symbol_table.lookup_sym('d')
+
+      import pandas as pd
+      df = pd.DataFrame({'x':[aval,bval], 'y':[cval,dval]})
+      __retval__ = ('foreign', df)
+      "
+      end
+
+      ------------------------------------------------------------------------
+      function dump
+      ------------------------------------------------------------------------
+      -- dump the Pandas dataframe to stdout
+      with df do escape
+      "
+      (dftype, dfval) = state.symbol_table.lookup_sym('df')
+      if dftype != 'foreign':
+         raise ValueError('expected data frame')
+      print(dfval)
+      "
+      end
+
+      ------------------------------------------------------------------------
+      function access
+      ------------------------------------------------------------------------
+      -- access an element of the Pandas dataframe at row r and column c
+      with (df,r:%integer,c:%integer) do return escape
+      "
+      global __retval__
+      (dftype, dfval) = state.symbol_table.lookup_sym('df')
+      if dftype != 'foreign':
+         raise ValueError('expected data frame')
+      # we can ignore type info here because we checked it above
+      (_, rval) = state.symbol_table.lookup_sym('r')
+      (_, cval) = state.symbol_table.lookup_sym('c')
+      # make sure the ret value conforms to the Asteroid value structure
+      __retval__ = ('real', dfval.iloc[rval,cval])
+      "
+      end
+
+      ------------------------------------------------------------------------
+      function sum
+      ------------------------------------------------------------------------
+      -- sum down the columns of the dataframe and return a pair of values,
+      -- one component for each column
+      with (df) do return escape
+      "
+      global __retval__
+      (dftype, dfval) = state.symbol_table.lookup_sym('df')
+      if dftype != 'foreign':
+         raise ValueError('expected data frame')
+      # sum the value down the columns
+      sum = list(dfval.sum(axis=0))
+      # construct our tuple, note the type information
+      __retval__ = ('tuple', [('real',sum[0]),('real',sum[1])])
+      "
+      end
+
+      ------------------------------------------------------------------------
+      -- exercise our machinery
+      let df = pack(1.0,2.0,3.0,4.0).
+      dump(df).
+      assert(access(df,1,1) == 4).
+      assert(sum(df) == (3.0,7.0)).
+
+The ``dump`` function generates the following output,
+::
+           x    y
+      0  1.0  3.0
+      1  2.0  4.0
+
+Pandas dataframes are not directly usable in Asteroid but by writing thin Python
+wrappers and taking advantage of the ``escape`` expression the ``foreign`` type
+tag we can embed Pandas functionality into Asteroid.  As an additional step we could
+wrap these individual functions into a ``structure`` with the dataframe as
+a data member and the functions as member functions of that structure.  As an
+example of this approach see the `dataframe.ast <https://github.com/asteroid-lang/asteroid/blob/master/asteroid/modules/dataframe.ast>`_ system module.
diff --git a/docs/Reference Guide.txt b/docs/Reference Guide.txt
index 9875fbb2..0c57d296 100644
--- a/docs/Reference Guide.txt
+++ b/docs/Reference Guide.txt
@@ -459,18 +459,17 @@ Example,
 List Comprehensions
 %%%%%%%%%%%%%%%%%%%
 
-Syntax: ``start_exp TO end_exp (STRIDE exp)?``
+Syntax: ``start_exp TO end_exp (STEP exp)?``
 
 This expression constructs a list starting with an element given by the start expression
-up to the value of the end expression with a given stride.  If the stride expression
-is not given then a stride value of 1 is assumed.  The stride value is always a
-positive value.  The comprehension figures out how to apply the stride value to reach the
-end value from the start value.  The comprehension can be placed between optional square brackets.
+up to the value of the end expression with a given step.  If the step expression
+is not given then a step value of 1 is assumed. The comprehension can be placed between
+optional square brackets.
 
 Examples,
 ::
       let [0,1,2,3,4] = 0 to 4.
-      let [0,-2,-4,-6] = [0 to -6 stride 2].
+      let [0,-2,-4,-6] = [0 to -6 step -2].
 
 Function Calls
 %%%%%%%%%%%%%%
@@ -630,7 +629,7 @@ Builtin Functions
 * Function ``range`` will compute a list of values depending on the input values:
 
   1. ``(start:%integer,stop:%integer)`` returns list ``[start to stop-1]``.
-  2. ``(start:%integer,stop:%integer,inc:%integer)`` returns list ``[start to stop-1 stride inc]``.
+  2. ``(start:%integer,stop:%integer,inc:%integer)`` returns list ``[start to stop-1 step inc]``.
   3. ``(stop:%integer)`` returns list ``[0 to stop-1]``.
 
 * Function ``getid`` returns the id (physical memory address) of any Asteroid object as an Asteroid integer.
@@ -1003,3 +1002,285 @@ Here is a simple example program for the ``vector`` module,
 #include "../asteroid/test-suites/ref-programs/vector.ast"
 
 which prints the value ``0``.
+
+Interfacing Asteroid with Python
+--------------------------------
+
+Asteroid allows integration with Python in one of two ways.  First, we can call the
+Asteroid interpreter from within a Python program and second, we can embed
+Python code directly within an Asteroid program. We start with looking at
+calling the Asteroid interpreter from Python.
+
+Calling Asteroid from Python
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Calling Asteroid from within a Python program is nothing more than calling Asteroid's ``interp``
+function with a string representing an Asteroid program as its argument.  In order to make this work you
+will have to make sure that the Python interpreter can find the Asteroid modules.
+Here we assume that you have installed Asteroid with the ``pip`` installer.
+Once you have installed Asteroid you will have to point the ``PYTHONPATH``
+environment variable to the directory where ``pip`` installed the Asteroid modules.
+You can easily find out where the modules are installed by issuing the ``show`` command,
+::
+    ubuntu$ pip3 show asteroid-lang
+    Name: asteroid-lang
+    Version: 1.1.0
+    Summary: A pattern-matching oriented programming language.
+    Home-page: https://asteroid-lang.org
+    Author: University of Rhode Island
+    Author-email: lutzhamel@uri.edu
+    License: None
+    Location: /home/ubuntu/.local/lib/python3.8/site-packages
+    Requires: numpy, pandas, matplotlib
+    Required-by:
+    ubuntu$
+
+The ``Location`` field tells us where the Asteroid modules have been installed.
+Under Ubuntu we can now create an environment variable that points to that directory as follows,
+::
+    ubuntu$ export PYTHONPATH=/home/ubuntu/.local/lib/python3.8/site-packages
+    ubuntu$
+
+Now that Python knows how to find the Asteroid modules we can import the
+Asteroid interpreter into any Python program using,
+::
+   from asteroid.interp import interp
+
+where the ``interp`` function takes a string representing of an Asteroid program
+as an argument.  Let's test drive this in the Python interactive shell,
+::
+    ubuntu$ python3
+    Python 3.8.10 (default, Nov 26 2021, 20:14:08)
+    [GCC 9.3.0] on linux
+    Type "help", "copyright", "credits" or "license" for more information.
+    >>> from asteroid.interp import interp
+    >>> interp('load system io. io @println "Hello, World!".')
+    Hello, World!
+    >>>
+
+For more detailed information on the ``interp`` function do a ``help(interp)``
+at the interactive Python prompt.
+Even though we have shown this example under Linux, analogous approaches
+should work on both Windows and macOS.
+
+Not only can we execute the Asteroid interpreter
+from Python but we can also access its state to look up the results of a
+computation for example.  Here is a slight variation of the program above
+where the Asteroid program computes the string value containing the greeting but
+we are actually printing the value from Python,
+::
+      hash_comment import Asteroid modules
+      from asteroid.interp import interp
+      from asteroid.state import state
+
+      hash_comment run the interpreter to compute the greeting string
+      interp('let s = "Hello World!".')
+
+      hash_comment retrieve the greeting string from the interpreter state
+      hash_comment notice the pair of values a symbol table lookup produces:
+      hash_comment one for the type of the value and one for the actual value
+      (type,val) = state.symbol_table.lookup_sym('s')
+      print(type)
+      print(val)
+
+The program prints out,
+::
+      string
+      Hello World!
+
+
+
+Embedding Python into an Asteroid Program
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Using Asteroid's ``escape`` expression allows us to embed arbitray Python
+code into an Asteroid program,
+::
+      -- Printing hello once from each environment
+
+      -- print hello from Asteroid
+      load system io.
+      io @println "Hello World from Asteroid!".
+
+      -- print hello from Python
+      escape
+      "
+      print('Hello World from Python!')
+      ".
+
+Please note that the format of the Python code in the escaped string should follow the
+same guidelines as the Python code embedded in strings handed to the Python `exec
+function <https://docs.python.org/3/library/functions.html#exec>`_.
+
+Not only does the ``escape`` expression give you access to the Python environment but
+it also gives you access to the current Asteroid interpreter state including its
+symbol table.  That means we can access any variable defined in the Asteroid
+environment from Python,
+::
+      let s = "Hello World!".
+
+      escape
+      "
+      (type, val) = state.symbol_table.lookup_sym('s')
+      print(type)
+      print(val)
+      ".
+
+Notice that a symbol table lookup produces a pair of values where the first value
+represents the type of the value stored in the symbol table and the second value
+is the actual value stored.  In this case our program prints out,
+::
+      string
+      Hello World!
+
+That is the type of the value is a string and the value is the actual string ``Hello World!``.
+
+Since ``escape`` represents an expression we can also return values from the
+Python code using a special ``__retval__`` variable.  The only trick is that
+we have to remember that values in Asteroid are pairs consisting of type information
+and values.  Here is a very simple program that exercises that part of the Python API,
+::
+      load system io.
+
+      let i = escape
+      "
+      global __retval__  # access the return value register
+
+      __retval__ = ('integer', 101)
+      ".
+
+      io @println i.
+
+This program will print out the value ``101`` from Asteroid even though that value
+was created within the Python environment.  Notice that we have to access the
+return value register ``__retval__`` with the ``global`` statement in the Python code.
+
+We can pull all of this together and write an Asteroid function that performs its
+computations in Python,
+::
+      function inc with i do return escape
+      "
+      hash_comment access return value register
+      global __retval__
+      hash_comment lookup the value of the formal argument
+      (type, val) = state.symbol_table.lookup_sym('i')
+
+      hash_comment only perform the increment if the value is an integer
+      if type != 'integer':
+         raise ValueError('not an integer')
+      else:
+         __retval__ = (type, val+1)
+      ".
+      end
+
+      -- call inc and make sure the result is correct
+      let k = inc(1)
+      assert(k == 2).
+
+Of course the function is just an illustration of how to use the Python API.  This
+type of computation is much easier to express in Asteroid directly,
+::
+      function inc
+         with i:%integer do
+            i+1
+         end
+
+      let k = inc(1)
+      assert(k == 2).
+
+The Foreign Type Tag
+^^^^^^^^^^^^^^^^^^^^
+
+When working in the hybrid Asteroid-Python environment it is sometimes useful to be able to embed values
+in an Asteroid program that have no direct representation in Asteroid.  This is where the ``foreign``
+type tage comes into play.  Consider the following program that uses Pandas dataframes within an
+Asteroid program,
+::
+
+      ------------------------------------------------------------------------
+      function pack
+      ------------------------------------------------------------------------
+      -- this function packs four real values into a Pandas dataframe
+      with (a:%real,b:%real,c:%real,d:%real) do return escape
+      "
+      global __retval__
+      hash_comment we can ignore type info here because we checked it above
+      (_, aval) = state.symbol_table.lookup_sym('a')
+      (_, bval) = state.symbol_table.lookup_sym('b')
+      (_, cval) = state.symbol_table.lookup_sym('c')
+      (_, dval) = state.symbol_table.lookup_sym('d')
+
+      import pandas as pd
+      df = pd.DataFrame({'x':[aval,bval], 'y':[cval,dval]})
+      __retval__ = ('foreign', df)
+      "
+      end
+
+      ------------------------------------------------------------------------
+      function dump
+      ------------------------------------------------------------------------
+      -- dump the Pandas dataframe to stdout
+      with df do escape
+      "
+      (dftype, dfval) = state.symbol_table.lookup_sym('df')
+      if dftype != 'foreign':
+         raise ValueError('expected data frame')
+      print(dfval)
+      "
+      end
+
+      ------------------------------------------------------------------------
+      function access
+      ------------------------------------------------------------------------
+      -- access an element of the Pandas dataframe at row r and column c
+      with (df,r:%integer,c:%integer) do return escape
+      "
+      global __retval__
+      (dftype, dfval) = state.symbol_table.lookup_sym('df')
+      if dftype != 'foreign':
+         raise ValueError('expected data frame')
+      hash_comment we can ignore type info here because we checked it above
+      (_, rval) = state.symbol_table.lookup_sym('r')
+      (_, cval) = state.symbol_table.lookup_sym('c')
+      hash_comment make sure the ret value conforms to the Asteroid value structure
+      __retval__ = ('real', dfval.iloc[rval,cval])
+      "
+      end
+
+      ------------------------------------------------------------------------
+      function sum
+      ------------------------------------------------------------------------
+      -- sum down the columns of the dataframe and return a pair of values,
+      -- one component for each column
+      with (df) do return escape
+      "
+      global __retval__
+      (dftype, dfval) = state.symbol_table.lookup_sym('df')
+      if dftype != 'foreign':
+         raise ValueError('expected data frame')
+      hash_comment sum the value down the columns
+      sum = list(dfval.sum(axis=0))
+      hash_comment construct our tuple, note the type information
+      __retval__ = ('tuple', [('real',sum[0]),('real',sum[1])])
+      "
+      end
+
+      ------------------------------------------------------------------------
+      -- exercise our machinery
+      let df = pack(1.0,2.0,3.0,4.0).
+      dump(df).
+      assert(access(df,1,1) == 4).
+      assert(sum(df) == (3.0,7.0)).
+
+The ``dump`` function generates the following output,
+::
+           x    y
+      0  1.0  3.0
+      1  2.0  4.0
+
+Pandas dataframes are not directly usable in Asteroid but by writing thin Python
+wrappers and taking advantage of the ``escape`` expression the ``foreign`` type
+tag we can embed Pandas functionality into Asteroid.  As an additional step we could
+wrap these individual functions into a ``structure`` with the dataframe as
+a data member and the functions as member functions of that structure.  As an
+example of this approach see the `dataframe.ast <https://github.com/asteroid-lang/asteroid/blob/master/asteroid/modules/dataframe.ast>`_ system module.
diff --git a/docs/User Guide.rst b/docs/User Guide.rst
index 66157b84..6c82f3d8 100644
--- a/docs/User Guide.rst
+++ b/docs/User Guide.rst
@@ -15,6 +15,7 @@
 ..
    *** DO NOT EDIT; MACHINE GENERATED ***
 
+
 .. highlight:: none
 
 Asteroid User Guide
@@ -175,16 +176,16 @@ For a comprehensive treatment of available member functions for Asteroid lists p
 
 Besides using the default constructor for lists which consists of the
 square brackets enclosing a list of elements we can use **list comprehensions** to construct lists.  In Asteroid a list comprehension consist of a range specifier together with
-a stride specifier allowing you to generate integer values within that range,
+a step specifier allowing you to generate integer values within that range,
 ::
     load system io.
 
     -- build a list of odd values
-    let a = [1 to 10 stride 2].  -- list comprehension
+    let a = [1 to 10 step 2].  -- list comprehension
     io @println ("list: " + a).
 
     -- reverse the list using a slice computed as comprehension
-    let slice = [4 to 0 stride 1]. -- list comprehension
+    let slice = [4 to 0 step -1]. -- list comprehension
     let b = a @slice.
     io @println ("reversed list: " + b).
 
@@ -408,7 +409,7 @@ As we said, in terms of flow of control statements there are really not a lot of
 ::
     load system io.
 
-    for i in 0 to 10 stride 2 do
+    for i in 0 to 10 step 2 do
         io @println i.
     end
 
diff --git a/docs/User Guide.txt b/docs/User Guide.txt
index f2a50323..8d3ce99b 100644
--- a/docs/User Guide.txt
+++ b/docs/User Guide.txt
@@ -142,7 +142,7 @@ For a comprehensive treatment of available member functions for Asteroid lists p
 
 Besides using the default constructor for lists which consists of the
 square brackets enclosing a list of elements we can use **list comprehensions** to construct lists.  In Asteroid a list comprehension consist of a range specifier together with
-a stride specifier allowing you to generate integer values within that range,
+a step specifier allowing you to generate integer values within that range,
 ::
 #include "../asteroid/test-suites/ug-programs/prgm-011.ast"
 
diff --git a/docs/conf.py b/docs/conf.py
index c857f380..c9c655bb 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -18,11 +18,11 @@
 # -- Project information -----------------------------------------------------
 
 project = 'Asteroid'
-copyright = '2021- University of Rhode Island'
-author = 'Lutz Hamel, Tim Colaneri, Oliver McLaughlin, Ariel Finkel, Theodore Henson, Calvin Higgins'
+copyright = '2022- University of Rhode Island'
+author = 'Lutz Hamel, Tim Colaneri, Oliver McLaughlin, Ariel Finkel, Theodore Henson, Calvin Higgins, Christian Tropeano'
 
 # The full version, including alpha/beta/rc tags
-release = '1.0.0'
+release = '1.1.0'
 
 
 # -- General configuration ---------------------------------------------------
diff --git a/docs/generate_docs b/docs/generate_docs
index 691b1cd2..3b798d16 100755
--- a/docs/generate_docs
+++ b/docs/generate_docs
@@ -11,3 +11,9 @@ cpp -w -P -traditional "Reference Guide.txt" > "Reference Guide.rst"
 
 echo "generating Asteroid in Action.rst"
 cpp -w -P -traditional "Asteroid in Action.txt" > "Asteroid in Action.rst"
+
+echo "generating Quickstart Tutorial.rst"
+cpp -w -P -traditional "Quickstart Tutorial.txt" > "Quickstart Tutorial.rst"
+
+echo "generating ADB.rst"
+cpp -w -P -traditional "ADB.txt" > "ADB.rst"
diff --git a/docs/header.txt b/docs/header.txt
index c7cf5888..eb548f7b 100644
--- a/docs/header.txt
+++ b/docs/header.txt
@@ -2,3 +2,5 @@
 
 ..
    *** DO NOT EDIT; MACHINE GENERATED ***
+
+#define hash_comment #
diff --git a/docs/index.rst b/docs/index.rst
index d76aa355..d327a3e9 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -21,3 +21,4 @@ Contents
    User Guide
    Reference Guide
    Asteroid in Action
+   ADB
\ No newline at end of file
diff --git a/asteroid.vim b/editor-support/asteroid.vim
similarity index 93%
rename from asteroid.vim
rename to editor-support/asteroid.vim
index 74a1244f..49a4bc83 100644
--- a/asteroid.vim
+++ b/editor-support/asteroid.vim
@@ -12,7 +12,7 @@ if exists("b:current_syntax")
 endif
 
 syn keyword basic with end do load let for function structure in is pattern 
-syn keyword basic throw this system data global return to stride if not
+syn keyword basic throw this system data global return to step if not
 syn keyword basic else or and
 
 syn keyword delimeter escape
diff --git a/editor-support/atom.md b/editor-support/atom.md
new file mode 100644
index 00000000..898ad7d0
--- /dev/null
+++ b/editor-support/atom.md
@@ -0,0 +1,7 @@
+## Asteroid Syntax Highlighting for Atom
+
+Syntax highlighting for Asteroid in the Atom editor is provided via
+the `language-asteroid` Atom package.  You can install this package directly from
+within Atom by searching for this package in the Atom package manager or you can
+install it manually by copying the relevant files from the
+[repository](https://github.com/asteroid-lang/language-asteroid).
diff --git a/editor-support/vscode/README.md b/editor-support/vscode/README.md
new file mode 100644
index 00000000..26897226
--- /dev/null
+++ b/editor-support/vscode/README.md
@@ -0,0 +1,17 @@
+# Asteroid Syntax Highlighter for Visual Studio Code
+
+A limited Asteroid syntax highlighter.
+
+## Features
+
+The Asteroid Syntax Highlighter extension provides syntactic highlighting for keywords, constants, functions and structures. It does not provide invalid syntax highlighting or semantic highlighting.
+
+## Installation
+
+To use the extension with Visual Studio Code, copy the extension into the `<user home>/.vscode/extensions` folder and restart Visual Studio Code.
+
+## Release Notes
+
+### 1.1.0
+
+Initial release of the Asteriod syntax highlighter. Provides highlighting for Asteroid 1.1.0 keywords, constants, functions and structures.
diff --git a/editor-support/vscode/language-configuration.json b/editor-support/vscode/language-configuration.json
new file mode 100644
index 00000000..ba110b92
--- /dev/null
+++ b/editor-support/vscode/language-configuration.json
@@ -0,0 +1,28 @@
+{
+    "comments": {
+        // symbol used for single line comment. Remove this entry if your language does not support line comments
+        "lineComment": "--",
+    },
+    // symbols used as brackets
+    "brackets": [
+        ["[", "]"],
+        ["%[", "]%"],
+        ["(", ")"]
+    ],
+    // symbols that are auto closed when typing
+    "autoClosingPairs": [
+        ["[", "]"],
+        ["%[", "]%"],
+        ["(", ")"],
+        ["\"", "\""],
+        ["'", "'"]
+    ],
+    // symbols that can be used to surround a selection
+    "surroundingPairs": [
+        ["[", "]"],
+        ["%[", "]%"],
+        ["(", ")"],
+        ["\"", "\""],
+        ["'", "'"]
+    ]
+}
\ No newline at end of file
diff --git a/editor-support/vscode/package.json b/editor-support/vscode/package.json
new file mode 100644
index 00000000..26c092af
--- /dev/null
+++ b/editor-support/vscode/package.json
@@ -0,0 +1,33 @@
+{
+    "name": "asteroid-syntax-highlighter",
+    "displayName": "Asteroid Syntax Highlighter",
+    "description": "A limited Asteroid syntax highlighter.",
+    "version": "1.0.0",
+    "engines": {
+        "vscode": "^1.70.0"
+    },
+    "categories": [
+        "Programming Languages"
+    ],
+    "contributes": {
+        "languages": [
+            {
+                "id": "asteroid",
+                "aliases": [
+                    "asteroid"
+                ],
+                "extensions": [
+                    "ast"
+                ],
+                "configuration": "./language-configuration.json"
+            }
+        ],
+        "grammars": [
+            {
+                "language": "asteroid",
+                "scopeName": "source.ast",
+                "path": "./syntaxes/asteroid.tmLanguage.json"
+            }
+        ]
+    }
+}
\ No newline at end of file
diff --git a/editor-support/vscode/syntaxes/asteroid.tmLanguage.json b/editor-support/vscode/syntaxes/asteroid.tmLanguage.json
new file mode 100644
index 00000000..8f68d8f9
--- /dev/null
+++ b/editor-support/vscode/syntaxes/asteroid.tmLanguage.json
@@ -0,0 +1,179 @@
+{
+	"$schema": "https://raw.githubusercontent.com/martinring/tmlanguage/master/tmlanguage.json",
+	"name": "asteroid",
+	"patterns": [
+		{
+			"name": "comment.line.double-dash.asteroid",
+			"begin": "--",
+			"beginCaptures": {
+				"0": {
+					"name": "comment.line.asteroid"
+				}
+			},
+			"end": "\\n"
+		},
+		{
+			"name": "comment.line.number-sign.asteroid",
+			"begin": "#",
+			"beginCaptures": {
+				"0": {
+					"name": "comment.line.asteroid"
+				}
+			},
+			"end": "\\n"
+		},
+		{
+			"name": "constant.numeric.asteroid",
+			"match": "\\b([0-9]+([.][0-9]+)?((e|E)(\\+|\\-)?[0-9]+)?)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "storage.modifier.global.asteroid",
+			"match": "\\b(global)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.load.asteroid",
+			"match": "\\b(load|system)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.conditional.asteroid",
+			"match": "\\b(if|elif|else)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.exception.asteroid",
+			"match": "\\b(try|catch)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.exception.asteroid",
+			"match": "\\b(throw)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.repeat.asteroid",
+			"match": "\\b(for|while|loop|repeat|until)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.statement.asteroid",
+			"match": "\\b(break|return)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.dispatch.asteroid",
+			"match": "\\b(with|orwith)\\b"
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.control.block.asteroid",
+			"match": "\\b(do|escape|end)\\b"
+		},
+		{
+			"name": "keyword",
+			"comment": "keyword.declaration.asteroid",
+			"match": "\\b(let|data)\\b"
+		},
+		{
+			"name": "keyword",
+			"comment": "keyword.operator.logical.asteroid",
+			"match": "\\b(and|is|not|or)\\b"
+
+		},
+		{
+			"name": "keyword.control",
+			"comment": "keyword.operator.comprehension.asteroid",
+			"match": "\\b(in|to|from|step)\\b"
+		},
+		{
+			"name": "keyword",
+			"comment": "keyword.other.asteroid",
+			"match": "\\b(assert|eval)\\b"
+		},
+		{
+			"name": "keyword.operator.comparison.asteroid",
+			"match": "<\\=|>\\=|\\=\\=|<|>|\\=/\\="
+		},
+		{
+			"name": "keyword.operator.arithmetic.asteroid",
+			"match": "\\+|\\-|\\*|/"
+		},
+		{
+			"name": "keyword.operator.assignment.asteroid",
+			"match": "\\="
+		},
+		{
+			"begin": "^\\s*(structure)\\s",
+			"beginCaptures": {
+				"1": {
+					"name": "storage.type.structure.asteroid"
+				}
+			},
+			"contentName": "entity.name.type.structure.asteroid",
+			"end": "\\s+",
+			"name": "meta.structure.asteroid",
+			"patterns": [
+				{
+					"match": "[A-Za-z_][A-Za-z0-9_]*"
+				}
+			]
+		},
+		{
+			"begin": "^\\s*(function)\\s",
+			"beginCaptures": {
+				"1": {
+					"name": "storage.type.function.asteroid"
+				}
+			},
+			"contentName": "entity.name.function.asteroid",
+			"end": "\\s+",
+			"name": "meta.function.asteroid",
+			"patterns": [
+				{
+					"match": "[A-Za-z_][A-Za-z0-9_]*"
+				}
+			]
+		},
+		{
+			"match": "\\b(lambda)\\b",
+			"name": "storage.type.function.inline.asteroid"
+		},
+		{
+			"match": "\\b(pattern)\\b",
+			"name": "storage.type.pattern.inline.asteroid"
+		},
+		{
+			"match": "\\b(none|true|false)\\b",
+			"name": "constant.language.asteroid"
+		},
+		{
+			"name": "string.quoted.double.asteroid",
+			"begin": "\"",
+			"end": "\"",
+			"patterns": [
+				{
+					"name": "constant.character.escape.asteroid",
+					"match": "\\\\."
+				}
+			]
+		},
+		{
+			"name": "string.quoted.single.asteroid",
+			"begin": "'",
+			"end": "'",
+			"patterns": [
+				{
+					"name": "constant.character.escape.asteroid",
+					"match": "\\\\."
+				}
+			]
+		},
+		{
+			"name": "variable.name.asteroid",
+			"match": "[A-Za-z_][A-Za-z0-9_]*"
+		}
+	],
+	"scopeName": "source.ast"
+}
\ No newline at end of file
diff --git a/setup.py b/setup.py
index eb63bc90..ea7d7fbc 100644
--- a/setup.py
+++ b/setup.py
@@ -5,7 +5,7 @@
 
 setup(
     name="asteroid-lang",
-    version="1.0.0",
+    version="1.1.0",
     author="University of Rhode Island",
     author_email="lutzhamel@uri.edu",
     description="A pattern-matching oriented programming language.",


Test patch:
diff --git a/asteroid/test-suites/action-tests/test073.ast b/asteroid/test-suites/action-tests/test073.ast
index 245bef34..9e01271a 100644
--- a/asteroid/test-suites/action-tests/test073.ast
+++ b/asteroid/test-suites/action-tests/test073.ast
@@ -2,7 +2,7 @@
   load system math.
 
   let a = [20 to 30].
-  let array = a @[1 to a @length()-1 stride 2] .
+  let array = a @[1 to a @length()-1 step 2] .
   io @println array.
 
   assert (array == [21,23,25,27,29]).
diff --git a/asteroid/test-suites/action-tests/test075.ast b/asteroid/test-suites/action-tests/test075.ast
index 198f25bc..7aeaec13 100644
--- a/asteroid/test-suites/action-tests/test075.ast
+++ b/asteroid/test-suites/action-tests/test075.ast
@@ -9,4 +9,4 @@
 
   io @println a.
 
-  assert (a == [2,3,4,5,6,7,10])
+  assert (a == [2,3,4,5,6,7,10])
\ No newline at end of file
diff --git a/asteroid/test-suites/regression-tests/test008.ast b/asteroid/test-suites/regression-tests/test008.ast
index 91de08d5..71b82127 100644
--- a/asteroid/test-suites/regression-tests/test008.ast
+++ b/asteroid/test-suites/regression-tests/test008.ast
@@ -1,4 +1,3 @@
-
 load system io.
 
 structure Family with
@@ -24,4 +23,3 @@ let p1 = "Harry".
 let family = eval(FAMILY).
 --__raw_print__(family).
 io @println (family @members()).
-
diff --git a/asteroid/test-suites/regression-tests/test044.ast b/asteroid/test-suites/regression-tests/test044.ast
index c488a5ec..ed508812 100644
--- a/asteroid/test-suites/regression-tests/test044.ast
+++ b/asteroid/test-suites/regression-tests/test044.ast
@@ -1,6 +1,6 @@
 
 load system io.      
 load "util".
-let slice = [4 to 0 stride 1].
+let slice = [4 to 0 step -1].
 io @println slice.
 assert(slice is [4,3,2,1,0]).
diff --git a/asteroid/test-suites/regression-tests/test090.ast b/asteroid/test-suites/regression-tests/test090.ast
index e3bbee67..8e2d5aeb 100644
--- a/asteroid/test-suites/regression-tests/test090.ast
+++ b/asteroid/test-suites/regression-tests/test090.ast
@@ -3,6 +3,6 @@ load system io.
 load "util".
 
 let a = [0,1,2,3].
-let b = a@[0 to 3 stride 2].
+let b = a@[0 to 3 step 2].
 io @println b.
 assert (b is [0,2]).
diff --git a/asteroid/test-suites/regression-tests/test091.ast b/asteroid/test-suites/regression-tests/test091.ast
index 81d22b2c..1791cbdc 100644
--- a/asteroid/test-suites/regression-tests/test091.ast
+++ b/asteroid/test-suites/regression-tests/test091.ast
@@ -2,7 +2,7 @@
 load system io.
 load "util".
 
-let y = [0 to 10 stride 2].
+let y = [0 to 10 step 2].
 let z = 4 in y.
 io @println z.
 assert z.
diff --git a/asteroid/test-suites/ug-programs/prgm-011.ast b/asteroid/test-suites/ug-programs/prgm-011.ast
index ecf613d5..77d47063 100644
--- a/asteroid/test-suites/ug-programs/prgm-011.ast
+++ b/asteroid/test-suites/ug-programs/prgm-011.ast
@@ -1,10 +1,10 @@
     load system io.
 
     -- build a list of odd values
-    let a = [1 to 10 stride 2].  -- list comprehension
+    let a = [1 to 10 step 2].  -- list comprehension
     io @println ("list: " + a).
 
     -- reverse the list using a slice computed as comprehension
-    let slice = [4 to 0 stride 1]. -- list comprehension
+    let slice = [4 to 0 step -1]. -- list comprehension
     let b = a @slice.
     io @println ("reversed list: " + b).
diff --git a/asteroid/test-suites/ug-programs/prgm-022.ast b/asteroid/test-suites/ug-programs/prgm-022.ast
index b85264fb..fbbe4b02 100644
--- a/asteroid/test-suites/ug-programs/prgm-022.ast
+++ b/asteroid/test-suites/ug-programs/prgm-022.ast
@@ -1,5 +1,5 @@
     load system io.
 
-    for i in 0 to 10 stride 2 do
+    for i in 0 to 10 step 2 do
         io @println i.
     end