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

Issue:
Module statements
We need to proper module system.  Perhaps something along the lines,
```
module SomeModule with

   structure SomeStructure with
      data a.
      data b.
   end

   function some_function with a do
      a
   end

end -- module

let a:%SomeModule@SomeStructure = SomeModule @SomeStructure(1,2).
let b = SomeModule @some_function a.
```
Of course, if the module is written in a separate file, say `somemodule.ast` then we could load the module and use it,
```
load somemodule.
let a:%SomeModule@SomeStructure = SomeModule @SomeStructure(1,2).
let b = SomeModule @some_function a.
```




Discussion / hints:

Fix patch:
diff --git a/asteroid/frontend.py b/asteroid/frontend.py
index dde2d25d..c8d5df88 100644
--- a/asteroid/frontend.py
+++ b/asteroid/frontend.py
@@ -63,6 +63,7 @@ def dbg_print(string):
     'LET',
     'LOAD',
     'LOOP',
+    'MODULE',
     'REPEAT',
     'RETURN',
     'STRUCTURE',
@@ -118,29 +119,10 @@ def stmt_list(self):
         dbg_print("parsing STMT_LIST")
 
         sl = []
-        while self.stmt_coming_up():
+        while self.lexer.peek().type in stmt_lookahead:
             sl += [('lineinfo', state.lineinfo)]
-
-            # 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)]             
-
+            sl += [('clear-ret-val',)]
+            sl += [self.stmt()]
         return ('list', sl)
 
     ###########################################################################################
@@ -243,10 +225,10 @@ def stmt(self):
 
                 # Give the absolute path to the parser
                 fparser = Parser(str(ast_module_path))
-                (STMT_LIST, fstmts) = fparser.parse(data)
+                fstmts = fparser.parse(data)
             
             state.lineinfo = old_lineinfo
-            return ('import_stmt', fstmts)
+            return ('load-stmt', fstmts)
 
         elif tt == 'GLOBAL':
             dbg_print("parsing GLOBAL")
@@ -276,6 +258,17 @@ def stmt(self):
                     ('id', id_tok.value),
                     ('member-list', stmts))
 
+        elif tt == 'MODULE':
+            dbg_print("parsing MODULE")
+            self.lexer.match('MODULE')
+            id_tok = self.lexer.match('ID')
+            self.lexer.match('WITH')
+            stmts = self.stmt_list()
+            self.lexer.match('END')
+            return ('module-def',
+                    ('id', id_tok.value),
+                    ('stmt-list', stmts))
+
         elif tt == 'LET':
             dbg_print("parsing LET")
             self.lexer.match('LET')
@@ -440,7 +433,7 @@ def stmt(self):
         elif tt in exp_lookahead:
             v = self.exp()
             self.lexer.match_optional('DOT')
-            return ('top-level-exp', v)
+            return ('set-ret-val', v)
 
         else:
             raise SyntaxError("syntax error at '{}'"
@@ -1040,8 +1033,3 @@ 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/globals.py b/asteroid/globals.py
index beed9c0d..c5c824d8 100644
--- a/asteroid/globals.py
+++ b/asteroid/globals.py
@@ -166,4 +166,12 @@ def __str__(self):
 ###########################################################################################
 # list of structures that a pattern type-pattern will subsume/overlap
 
-pattern_subsumes = patterns - {'id'}
\ No newline at end of file
+pattern_subsumes = patterns - {'id'}
+
+###########################################################################################
+# the debugger object set by the interpreter at start time
+debugger = None
+
+def set_debugger(dbg):
+    debugger = dbg
+
diff --git a/asteroid/grammar.txt b/asteroid/grammar.txt
index 0c540c7b..89ae450a 100644
--- a/asteroid/grammar.txt
+++ b/asteroid/grammar.txt
@@ -26,6 +26,7 @@
     | GLOBAL id_list '.'?
     | ASSERT exp '.'?
     | STRUCTURE ID WITH struct_stmts END
+    | MODULE ID WITH stmt_list END
     | LET pattern '=' exp '.'?
     | LOOP DO? stmt_list END
     | FOR pattern IN exp DO stmt_list END
diff --git a/asteroid/interp.py b/asteroid/interp.py
index 95209434..1dbda789 100644
--- a/asteroid/interp.py
+++ b/asteroid/interp.py
@@ -11,7 +11,7 @@
 from asteroid.support import *
 from asteroid.frontend import Parser
 from asteroid.state import state, dump_trace
-from asteroid.walk import walk, debug_walk
+from asteroid.walk import walk
 
 # the prologue file is expected to be in the 'modules' folder
 prologue_name = 'prologue.ast'
@@ -95,11 +95,10 @@ def interp(program,
         # walk the AST
         if tree_dump:
             dump_AST(state.AST)
+        if debugger:
+            set_debugger(debugger)
         if do_walk:
-            if debugger:
-                debug_walk(state.AST, debugger)
-            else:
-                walk(state.AST)
+            walk(state.AST)
         if symtab_dump:
             state.symbol_table.dump()
 
diff --git a/asteroid/lex.py b/asteroid/lex.py
index f5c0d293..7d5dbc55 100644
--- a/asteroid/lex.py
+++ b/asteroid/lex.py
@@ -36,13 +36,14 @@
     'let'           : 'LET',
     'load'          : 'LOAD',
     'loop'          : 'LOOP',
+    'module'        : 'MODULE',
     'not'           : 'NOT',
     'or'            : 'OR',
     'orwith'        : 'ORWITH',
     'pattern'       : 'PATTERN',
     'repeat'        : 'REPEAT',
     'return'        : 'RETURN',
-    'step'        : 'STEP',
+    'step'          : 'STEP',
     'structure'     : 'STRUCTURE',
     'system'        : 'SYSTEM',
     'throw'         : 'THROW',
diff --git a/asteroid/modules/bitwise.ast b/asteroid/modules/bitwise.ast
index 8ef19dc0..439e99e3 100644
--- a/asteroid/modules/bitwise.ast
+++ b/asteroid/modules/bitwise.ast
@@ -1,3 +1,4 @@
+module bitwise with
 ------------------------------------------------------------------
 -- bitwise.ast
 --
@@ -6,13 +7,6 @@
 -- (c) University of Rhode Island
 ------------------------------------------------------------------
 
--- define the bitwise module object
-
-------------------------------------------------------------------
-structure __BITWISE_MODULE__
-------------------------------------------------------------------
-with
-
 ------------------------------------------------------------------
 function band
 ------------------------------------------------------------------
@@ -180,8 +174,4 @@ __retval__ = ('integer', x_val.bit_length())
 "
 end
 
-end -- __BITWISE__
-
-------------------------------------------------------------------
--- instantiate the bitwise module object
-let bitwise = __BITWISE_MODULE__().
+end -- bitwise
diff --git a/asteroid/modules/dataframe.ast b/asteroid/modules/dataframe.ast
index 2b341551..3f91d0ec 100644
--- a/asteroid/modules/dataframe.ast
+++ b/asteroid/modules/dataframe.ast
@@ -1,3 +1,4 @@
+module dataframe with
 ------------------------------------------------------------------
 -- Defines the Asteroid DataFrames structure.
 --
@@ -7,9 +8,8 @@
 ------------------------------------------------------------------
 
 ------------------------------------------------------------------
-structure __DATAFRAME__
+structure DataFrame with
 ------------------------------------------------------------------
-with
 
 ------------------------------------------------------------------
 data df.
@@ -30,6 +30,7 @@ header_row_val = state.symbol_table.lookup_sym('header_row')
 # destructure object
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 # read as Pandas dataframe
@@ -56,6 +57,7 @@ ix_val = state.symbol_table.lookup_sym('ix')
 # destructure object
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
  # the dataframe is in the first slot in the object memory
@@ -95,6 +97,7 @@ ix_val = state.symbol_table.lookup_sym('ix')
 # destructure object
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
  # the dataframe is in the first slot in the object memory
@@ -132,6 +135,7 @@ this_val = state.symbol_table.lookup_sym('this')
 # destructure object
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
  # the dataframe is in the first slot in the object memory
@@ -152,6 +156,7 @@ this_val = state.symbol_table.lookup_sym('this')
 # destructure object
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
  # the dataframe is in the first slot in the object memory
@@ -159,18 +164,12 @@ this_val = state.symbol_table.lookup_sym('this')
 __retval__ = ('integer', df_val.shape[1])
 "
 end
-end -- __DATAFRAME__
+end -- DataFrame structure
 
-------------------------------------------------------------------
-structure __DATAFRAME_MODULE__
-------------------------------------------------------------------
-with
 
 function dataframe with none do
-  return __DATAFRAME__().
+  return DataFrame().
 end
 
-end -- __DATAFRAME_MODULE__
+end -- dataframe module
 
-------------------------------------------------------------------
-let dataframe = __DATAFRAME_MODULE__().
diff --git a/asteroid/modules/hash.ast b/asteroid/modules/hash.ast
index a119fe2c..f6466a92 100644
--- a/asteroid/modules/hash.ast
+++ b/asteroid/modules/hash.ast
@@ -1,3 +1,4 @@
+module hash with 
 ------------------------------------------------------------------
 -- hash.ast
 --
@@ -6,8 +7,9 @@
 -- (c) Lutz Hamel, University of Rhode Island
 ------------------------------------------------------------------
 
+
 ------------------------------------------------------------------
-structure __HASH__ with
+structure Hash with
 ------------------------------------------------------------------
 -- Asteroid hash table implementation using Python dictionaries
 
@@ -25,6 +27,7 @@ this_val = state.symbol_table.lookup_sym('this')
 # destructure object
 (OBJECT,
  (STRUCT_ID, (ID, struct_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 # the table is in the first slot in the object memory
@@ -45,6 +48,7 @@ value_val = state.symbol_table.lookup_sym('value')
 # destructure object
 (OBJECT,
  (STRUCT_ID, (ID, struct_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 # the table is in the first slot in the object memory
@@ -71,6 +75,7 @@ name_val = state.symbol_table.lookup_sym('name')
 # destructure object
 (OBJECT,
  (STRUCT_ID, (ID, struct_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 # the table is in the first slot in the object memory
@@ -95,6 +100,7 @@ this_val = state.symbol_table.lookup_sym('this')
 # destructure object
 (OBJECT,
  (STRUCT_ID, (ID, struct_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 # the table is in the first slot in the object memory
@@ -123,18 +129,12 @@ with none do
   return tostring (this @aslist ()).
 end -- __str__
 
-end -- __HASH__
-
-------------------------------------------------------------------
-structure __HASH_MODULE__
-------------------------------------------------------------------
-with
+end -- Hash structure
 
+-- here for backwards compatibility
 function hash with none do
-   return __HASH__().
+   return Hash().
 end
 
-end -- __HASH_MODULE__
+end -- hash module
 
-------------------------------------------------------------------
-let hash = __HASH_MODULE__().
diff --git a/asteroid/modules/io.ast b/asteroid/modules/io.ast
index de0e27c0..4e76b2d9 100644
--- a/asteroid/modules/io.ast
+++ b/asteroid/modules/io.ast
@@ -1,3 +1,4 @@
+module io with
 ------------------------------------------------------------------
 -- io.ast
 --
@@ -5,12 +6,14 @@
 --
 -- (c) University of Rhode Island
 
+-- define the io module.
 
 ------------------------------------------------------------------
--- Basic file i/o
+
 ------------------------------------------------------------------
 structure __FILE__ with
 ------------------------------------------------------------------
+-- Basic file i/o
 
 data fd.
 
@@ -30,6 +33,7 @@ mode_val = state.symbol_table.lookup_sym('mode')
 # destructure object
 (OBJECT,
 (CLASS_ID, (ID, class_id)),
+(MEMBER_NAMES, (LIST, member_names)),
 (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 # Open file
@@ -60,6 +64,7 @@ else:
 # destructure object
 (OBJECT,
 (CLASS_ID, (ID, class_id)),
+(MEMBER_NAMES, (LIST, member_names)),
 (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 memory[0] = ('foreign', stream)
@@ -78,6 +83,7 @@ this_val = state.symbol_table.lookup_sym('this')
 
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 (FOREIGN, f_val) = memory[0]
@@ -97,6 +103,7 @@ this_val = state.symbol_table.lookup_sym('this')
 
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 (FOREIGN, f_val) = memory[0]
@@ -116,6 +123,7 @@ this_val = state.symbol_table.lookup_sym('this')
 
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 (FOREIGN, f_val) = memory[0]
@@ -135,6 +143,7 @@ what_val = state.symbol_table.lookup_sym('what')
 
 (OBJECT,
  (CLASS_ID, (ID, class_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 (FOREIGN, f_val) = memory[0]
@@ -149,21 +158,11 @@ end -- __FILE__
 
 
 
--- define the io module object.
-------------------------------------------------------------------
-structure __IO_MODULE__ with
-------------------------------------------------------------------
 
 -- Predefined standard stream constants
-data __STDIN__.
-data __STDOUT__.
-data __STDERR__.
-
-function __init__ with none do
-   let this @__STDIN__ = __FILE__("__STDIN__").
-   let this @__STDOUT__ = __FILE__("__STDOUT__").
-   let this @__STDERR__ = __FILE__("__STDERR__").
-end
+let __STDIN__ = __FILE__("__STDIN__").
+let __STDOUT__ = __FILE__("__STDOUT__").
+let __STDERR__ = __FILE__("__STDERR__").
 
 -- File I/O library functions
 ------------------------------------------------------------------
@@ -207,7 +206,7 @@ function readln
 with(file:%__FILE__) do
     return file @readln().
 with none do
-    return this @__STDIN__ @readln().
+    return __STDIN__ @readln().
 end
 
 ------------------------------------------------------------------
@@ -215,13 +214,8 @@ function write
 -- Write to a given file
 ------------------------------------------------------------------
 with what:%string do
-this @__STDOUT__ @write(what).
-
-escape "
-import sys
-sys.stdout.flush()
-"
-
+    __STDOUT__ @write(what).
+    escape "import sys;sys.stdout.flush()".
 with(file:%__FILE__, what:%string) do
     file @write(what).
 end
@@ -231,7 +225,7 @@ function writeln
 -- Write a complete line to a given file
 ------------------------------------------------------------------
 with what:%string do
-    this @__STDOUT__ @write(what + "\n").
+    __STDOUT__ @write(what + "\n").
 
 with(file:%__FILE__, what:%string) do
     file @write(what + "\n").
@@ -244,14 +238,14 @@ end
 function println
 ------------------------------------------------------------------
 with item do
-    this @writeln(tostring item).
+    writeln(tostring item).
 end
 
 ------------------------------------------------------------------
 function print
 ------------------------------------------------------------------
 with item do
-    this @write(tostring item).
+    write(tostring item).
 end
 
 ------------------------------------------------------------------
@@ -259,10 +253,10 @@ function input
 ------------------------------------------------------------------
 -- input drops the trailing newline char
 with none do
-    return this @readln() @replace("\n","").
+    return readln() @replace("\n","").
 with prompt do
-    this @write (tostring prompt).
-    return this @readln() @replace("\n","").
+    write (tostring prompt).
+    return readln() @replace("\n","").
 end
 
 ------------------------------------------------------------------
@@ -278,9 +272,5 @@ pprint(item_val)
 return none
 end
 
-end -- __IO_MODULE__
-
-
 ------------------------------------------------------------------
--- instantiate the io module object
-let io = __IO_MODULE__().
+end -- io
diff --git a/asteroid/modules/math.ast b/asteroid/modules/math.ast
index f4fcb4a9..d8859f93 100644
--- a/asteroid/modules/math.ast
+++ b/asteroid/modules/math.ast
@@ -1,3 +1,4 @@
+module math with
 ------------------------------------------------------------------
 -- math.ast
 --
@@ -7,31 +8,13 @@
 -- (c) University of Rhode Island
 ------------------------------------------------------------------
 
-structure __MATH_MODULE__ with
 
 ------------------------------------------------------------------
 -- Constants
 ------------------------------------------------------------------
-data pi.
-data e.
 
-function __init__ with none do
-
-let this @pi = escape -- init pi constant
-"
-from math import pi
-global __retval__
-__retval__ = ('real',pi)
-"
-
-let this @e = escape -- init e constant
-"
-from math import e
-global __retval__
-__retval__ = ('real',e)
-"
-
-end -- __init__
+let pi = escape "from math import pi;global __retval__;__retval__=('real',pi)"
+let e = escape "from math import e;global __retval__;__retval__=('real',e)"
 
 ------------------------------------------------------------------
 -- Power and logarithmic functions
@@ -461,6 +444,5 @@ __retval__ = ('real', math.radians(x_val[1]))
 "
 end
 
-end -- __MATH_MODULE__
+end -- math module
 
-let math = __MATH_MODULE__()
diff --git a/asteroid/modules/os.ast b/asteroid/modules/os.ast
index 3e5c8106..2c4cb2ad 100644
--- a/asteroid/modules/os.ast
+++ b/asteroid/modules/os.ast
@@ -1,3 +1,4 @@
+module os with
 ------------------------------------------------------------------
 -- os.ast
 --
@@ -9,14 +10,12 @@
 ------------------------------------------------------------------
 load system hash.
 
-structure __OS_MODULE__ with
-
 ------------------------------------------------------------------
 -- The list of command line arguments passed to an Asteroid script. 
 -- argv[0] is the name of the Asteroid script (it is operating 
 -- system dependent whether this is a full pathname or not).
 -- In interactive mode argv[0] will be the empty string.
-data argv.
+let argv = __getargv().
 
 ------------------------------------------------------------------
 -- A hash table where keys and values are strings that represent 
@@ -24,24 +23,14 @@ data argv.
 --    os @env @get "HOME"
 --  is the pathname of your home directory (on some platforms), 
 -- and is equivalent to getenv("HOME") in C.
-data env.
+let _l = __getenv(). -- list of key/value pairs
+let env = hash @hash ().
+env @insert _l.
 
 ------------------------------------------------------------------
 -- This string contains a platform identifier.
-data platform.
+let platform = __getplatform().
 
-------------------------------------------------------------------
-function __init__ 
-------------------------------------------------------------------
--- constructor for the __OS_MODULE__
-with none do
-   let this @argv = this @__getargv().
-   let this @platform = this @__getplatform().
-   -- construct the env hashtable
-   let l = this @__getenv(). -- list of key/value pairs
-   let this @env = hash @hash ().
-   this @env @insert l.
-end
 
 ------------------------------------------------------------------
 function __getargv 
@@ -398,7 +387,4 @@ __retval__ = ('integer', status)
 "
 end -- syscmd
 
-end -- __OS_MODULE__
-
-------------------------------------------------------------------
-let os = __OS_MODULE__().
+end -- os modules
diff --git a/asteroid/modules/pick.ast b/asteroid/modules/pick.ast
index c1f2e79f..0249b170 100644
--- a/asteroid/modules/pick.ast
+++ b/asteroid/modules/pick.ast
@@ -1,3 +1,4 @@
+module pick with
 ------------------------------------------------------------------
 -- pick.ast
 --
@@ -9,7 +10,7 @@
 load system random.
 
 ------------------------------------------------------------------
-structure __PICK__
+structure Pick
 ------------------------------------------------------------------
 -- A pick object holds a list of items that
 -- we can randomly pick from with the 'pickitems'
@@ -37,18 +38,13 @@ structure __PICK__
       let this @items = l.
   end
 
-end -- __PICK__
+end -- Pick
 
 ------------------------------------------------------------------
-structure __PICK_MODULE__
+function pick 
 ------------------------------------------------------------------
-with
-
-function pick with l:%list do
-   return __PICK__(l).
+with l:%list do
+   return Pick(l).
 end
 
-end -- __PICK_MODULE__
-
-------------------------------------------------------------------
-let pick = __PICK_MODULE__().
+end -- Pick module
diff --git a/asteroid/modules/prologue.ast b/asteroid/modules/prologue.ast
index e2f88eda..78602f6d 100644
--- a/asteroid/modules/prologue.ast
+++ b/asteroid/modules/prologue.ast
@@ -5,6 +5,9 @@
 --
 -- (c) University of Rhode Island
 ------------------------------------------------------------------
+-- Note: this is just a file with definitions that need to wind 
+-- up in the global scope of the interpreter.
+
 
 ------------------------------------------------------------------
 structure Error
@@ -48,6 +51,7 @@ if item_val[0] in ['list','tuple','string']:
 elif item_val[0] == 'object':
     (OBJECT,
         (STRUCT_ID, (ID, name)),
+        (MEMBER_NAMES, (LIST, member_names)),
         (OBJECT_MEMORY, (LIST, object_memory))) = item_val
     l = len(data_only(object_memory))
     __retval__ = ('integer', l)
@@ -362,7 +366,7 @@ with x do return escape
 global __retval__
 val = state.symbol_table.lookup_sym('x')
 if val[0] == 'object':
-  (OBJECT, (STRUCT_ID, (ID, x_type)), OBJECT_MEMORY) = val
+  (OBJECT, (STRUCT_ID, (ID, x_type)), MEMBER_LIST, OBJECT_MEMORY) = val
 elif val[0] in ['function-val','member-function-val']:
   # internally we store functions as function values
   # but to the user these look like functions as the 
diff --git a/asteroid/modules/random.ast b/asteroid/modules/random.ast
index 6deaedbf..2df0e123 100644
--- a/asteroid/modules/random.ast
+++ b/asteroid/modules/random.ast
@@ -1,3 +1,4 @@
+module random with
 ------------------------------------------------------------------
 -- random.ast
 --
@@ -6,11 +7,6 @@
 -- (c) University of Rhode Island
 ------------------------------------------------------------------
 
-------------------------------------------------------------------
-structure __RANDOM_MODULE__
-------------------------------------------------------------------
-with
-
 ------------------------------------------------------------------
 function random
 ------------------------------------------------------------------
@@ -70,7 +66,4 @@ random.seed(sd_val[1])
 "
 end
 
-end -- __RANDOM_MODULE__
-
-------------------------------------------------------------------
-let random = __RANDOM_MODULE__().
+end -- random
diff --git a/asteroid/modules/set.ast b/asteroid/modules/set.ast
index af69f9a5..898c2501 100644
--- a/asteroid/modules/set.ast
+++ b/asteroid/modules/set.ast
@@ -1,3 +1,4 @@
+module set with
 ------------------------------------------------------------------
 -- set.ast
 --
@@ -8,11 +9,6 @@
 -- (c) Lutz Hamel, University of Rhode Island
 ------------------------------------------------------------------
 
-------------------------------------------------------------------
-structure __SET_MODULE__
-------------------------------------------------------------------
-with
-
 ------------------------------------------------------------------
 function diff
 ------------------------------------------------------------------
@@ -73,7 +69,4 @@ __retval__ = ('list', list(set(a_val[1]) ^ set(b_val[1])))
 "
 end
 
-end -- __SET_MODULE__
-
-------------------------------------------------------------------
-let set = __SET_MODULE__().
+end -- set
diff --git a/asteroid/modules/sort.ast b/asteroid/modules/sort.ast
index 33c04bd2..2857e74a 100644
--- a/asteroid/modules/sort.ast
+++ b/asteroid/modules/sort.ast
@@ -1,3 +1,4 @@
+module sort with
 ------------------------------------------------------------------
 -- sort.ast
 --
@@ -14,11 +15,8 @@
 ------------------------------------------------------------------
 
 ------------------------------------------------------------------
-structure __SORT_MODULE__
-------------------------------------------------------------------
-with
-
 function sort
+------------------------------------------------------------------
     with (_,[]) do
         return [].
     with (_,[a]) do
@@ -35,9 +33,7 @@ function sort
             end
         end
 
-        return this @sort(p,less) + [pivot] + this @sort(p,more).
-    end
-end -- __SORT_MODULE__
+        return sort(p,less) + [pivot] + sort(p,more).
+end
 
-------------------------------------------------------------------
-let sort = __SORT_MODULE__().
+end -- sort
diff --git a/asteroid/modules/stream.ast b/asteroid/modules/stream.ast
index 020c3f0e..ea9ea591 100644
--- a/asteroid/modules/stream.ast
+++ b/asteroid/modules/stream.ast
@@ -1,3 +1,4 @@
+module stream with
 ------------------------------------------------------------------
 -- stream.ast
 --
@@ -11,7 +12,7 @@
 load system util.
 
 ------------------------------------------------------------------
-structure __STREAM__
+structure Stream
 ------------------------------------------------------------------
 -- Asteroid stream implementation based on lists
 with
@@ -71,7 +72,7 @@ function rewind
     let this @curr_ix = 0.
   end
 
-function __string__
+function __str__
 with none do return
 escape
 "
@@ -83,25 +84,21 @@ this_val = state.symbol_table.lookup_sym('this')
 # destructure object
 (OBJECT,
  (STRUCT_ID, (ID, struct_id)),
+ (MEMBER_NAMES, (LIST, member_names)),
  (OBJECT_MEMORY, (LIST, memory))) = this_val
 
 __retval__ = ('string', term2string(memory[0]))
 "
 end
 
-end -- __STREAM__
+end -- Stream structure
 
 
 ------------------------------------------------------------------
-structure __STREAM_MODULE__
+function stream 
 ------------------------------------------------------------------
-with
-
-function stream with l:%list do
-   return __STREAM__(l).
+with l:%list do
+   return Stream(l).
 end
 
-end -- __STREAM_MODULE__
-
-------------------------------------------------------------------
-let stream = __STREAM_MODULE__().
+end -- stream module
diff --git a/asteroid/modules/util.ast b/asteroid/modules/util.ast
index a3c8d9e7..e726541a 100644
--- a/asteroid/modules/util.ast
+++ b/asteroid/modules/util.ast
@@ -1,3 +1,4 @@
+module util with
 ------------------------------------------------------------------
 -- util.ast
 --
@@ -6,11 +7,6 @@
 -- (c) University of Rhode Island
 ------------------------------------------------------------------
 
-------------------------------------------------------------------
-structure __UTIL_MODULE__ 
-------------------------------------------------------------------
-with
-
 ------------------------------------------------------------------
 function achar
 ------------------------------------------------------------------
@@ -148,7 +144,4 @@ __retval__ = ('list', out_list)
 "
 end
 
-end -- __UTIL_MODULE__
-
-------------------------------------------------------------------
-let util = __UTIL_MODULE__().
+end -- util
diff --git a/asteroid/modules/vector.ast b/asteroid/modules/vector.ast
index 6e2120c1..165b5cbe 100644
--- a/asteroid/modules/vector.ast
+++ b/asteroid/modules/vector.ast
@@ -1,3 +1,4 @@
+module vector with
 ------------------------------------------------------------------
 -- vector.ast
 --
@@ -8,14 +9,11 @@
 
 load system util.
 
-------------------------------------------------------------------
-structure __VECTOR_MODULE__ with
-
 ------------------------------------------------------------------
 function add
 ------------------------------------------------------------------
 with (a,b) do
-  return this @op(lambda with (x,y) do return x+y,a,b).
+  return op(lambda with (x,y) do x+y,a,b).
 end
 
 ------------------------------------------------------------------
@@ -23,14 +21,14 @@ function dot
 -- computes the dot product of two lists
 ------------------------------------------------------------------
 with (a:%list,b:%list) do
-  return this @mult(a,b) @reduce(lambda with (x,y) do return x+y).
+  return mult(a,b) @reduce(lambda with (x,y) do x+y).
 end
 
 ------------------------------------------------------------------
 function mult
 ------------------------------------------------------------------
 with (a,b) do
-  return this @op(lambda with (x,y) do return x*y,a,b).
+  return op(lambda with (x,y) do x*y,a,b).
 end
 
 ------------------------------------------------------------------
@@ -40,18 +38,18 @@ function op
 -- Note: it also implements vector/scalar arithmetic
 with (f:%function,a:%list,b:%list) do
   if a @length() =/= b @length() do
-      throw Error("vops only defined on lists of the same length").
+      throw Error("vector operations only defined on lists of the same length").
   end
   let v = util @zip(a,b).
   return v @map(f).
 with (f:%function,a:%list,b if isscalar b) do
   -- promote b to a vector
-  let bv = [1 to a @length()] @map(lambda with _ do return b).
+  let bv = [1 to a @length()] @map(lambda with _ do b).
   let v = util @zip(a,bv).
   return v @map(f).
 with (f:%function,a if isscalar a,b:%list) do
   -- promote a to a vector
-  let av = [1 to b @length()] @map(lambda with _ do return a).
+  let av = [1 to b @length()] @map(lambda with _ do a).
   let v = util @zip(av,b).
   return v @map(f).
 end
@@ -60,10 +58,8 @@ end
 function sub
 ------------------------------------------------------------------
 with (a,b) do
-  return this @op(lambda with (x,y) do return x-y,a,b).
+  return op(lambda with (x,y) do x-y,a,b).
 end
 
 
-end -- __VECTOR_MODULE__
-
-let vector = __VECTOR_MODULE__().
+end -- vector
diff --git a/asteroid/support.py b/asteroid/support.py
index bd2b4cbf..d6036b64 100644
--- a/asteroid/support.py
+++ b/asteroid/support.py
@@ -20,6 +20,11 @@ def gettemp():
     return new_name
 
 ###########################################################################################
+__dump_level = 4
+
+def set_AST_dump_level(n):
+    __dump_level = n
+
 def dump_AST(node):
     '''
     this function will print any AST that follows the
@@ -32,7 +37,8 @@ def dump_AST(node):
     print('')
 
 def _dump_AST(node, level=0):
-
+    if level > __dump_level:
+        return
     if isinstance(node, tuple):
         _indent(level)
         nchildren = len(node) - 1
@@ -248,14 +254,9 @@ def term2string(term):
     elif TYPE == 'object':
         (OBJECT,
          (STRUCT_ID, (ID, struct_id)),
+         (MEMBER_NAMES, (LIST, member_names)),
          (OBJECT_MEMORY, (LIST, object_memory))) = term
 
-        struct_val = state.symbol_table.lookup_sym(struct_id)
-
-        (STRUCT,
-            (MEMBER_NAMES, (LIST, member_names)),
-            (STRUCT_MEMORY, (LIST, struct_memory))) = struct_val
-
         # if __str__ function exists for this object use it
         if '__str__' in member_names:
             slot_ix = member_names.index('__str__')
diff --git a/asteroid/version.py b/asteroid/version.py
index c72066d3..041a169f 100644
--- a/asteroid/version.py
+++ b/asteroid/version.py
@@ -5,4 +5,4 @@
 # (c) University of Rhode Island
 ##########################################################################################
 
-VERSION = "2.0.1dev"
+VERSION = "2.0.1debugger"
diff --git a/asteroid/walk.py b/asteroid/walk.py
index 26e12b77..cc95f24c 100644
--- a/asteroid/walk.py
+++ b/asteroid/walk.py
@@ -12,15 +12,6 @@
 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.
@@ -227,6 +218,7 @@ def unify(term, pattern, unifying = True ):
                         .format(typematch_kind) )
             (OBJECT,
                 (STRUCT_ID, (ID, struct_id)),
+                (MEMBER_NAMES, LIST),
                 (OBJECT_MEMORY, LIST)) = term
             if struct_id == typematch_kind:
                 return []
@@ -299,8 +291,8 @@ def unify(term, pattern, unifying = True ):
         # to an object as a pattern, e.g.
         #    let o = A(1,2). -- A is a structure with 2 data members
         #    let *o = o.
-        (OBJECT, (STRUCT_ID, (ID, pid)), (OBJECT_MEMORY, (LIST, pl))) = pattern
-        (OBJECT, (STRUCT_ID, (ID, tid)), (OBJECT_MEMORY, (LIST, tl))) = term
+        (OBJECT, (STRUCT_ID, (ID, pid)), pml, (OBJECT_MEMORY, (LIST, pl))) = pattern
+        (OBJECT, (STRUCT_ID, (ID, tid)), tml, (OBJECT_MEMORY, (LIST, tl))) = term
         if pid != tid:
             raise PatternMatchFailed(
                 "pattern type '{}' and term type '{}' do not agree"
@@ -316,14 +308,29 @@ def unify(term, pattern, unifying = True ):
         # in patterns constructor functions match objects, e.g.
         #   let A(x,y) = A(1,2)
         # only constructors are allowed in patterns
-        (APPLY,
-         (ID, apply_id),
-         arg) = pattern
+        f = pattern[1]
+        if f[0] == 'index':
+            # scope qualified pattern name
+            (APPLY,
+              (INDEX,
+                (ID, modname),
+                (ID, apply_id)),
+              arg) = pattern
+        else:
+            modname = None
+            (APPLY,
+              (ID, apply_id),
+              arg) = pattern
         # unpack term
         (OBJECT,
          (STRUCT_ID, (ID, struct_id)),
+         (MEMBER_NAMES, (LIST, member_names)),
          (OBJECT_MEMORY, (LIST, obj_memory))) = term
+        if modname:
+            orig_config = set_module_env(modname)        
         type = state.symbol_table.lookup_sym(apply_id,strict=False)
+        if modname:
+            set_config(orig_config)
         if not type or type[0] != 'struct':
             raise ValueError("illegal pattern, '{}' is not a type".format(apply_id))
         if struct_id != apply_id:
@@ -340,6 +347,10 @@ def unify(term, pattern, unifying = True ):
         return unify(data_list, arg_list, unifying)
 
     elif (not unifying) and pattern[0] == 'apply' and term[0] == 'apply':
+        fp = pattern[1]
+        ft = term[1]
+        if fp[0] != 'id' or ft[0] != 'id':
+            raise ValueError("pattern subsumption not supported")
         # unpack the apply structures
         (APPLY, (ID, t_id), t_arg) = term
         (APPLY, (ID, p_id), p_arg) = pattern
@@ -498,20 +509,21 @@ def read_at_ix(structure_val, ix):
     elif structure_val[0] == 'object':
         (OBJECT,
          (STRUCT_ID, (ID, struct_id)),
-         (OBJECT_MEMORY, (LIST, memory))) = structure_val
-        # compute the index -- for objects this has to be done
-        # in the context of the struct scope
-        struct_val = state.symbol_table.lookup_sym(struct_id)
-        # unpack the struct value
-        (STRUCT,
          (MEMBER_NAMES, (LIST, member_names)),
-         (STRUCT_MEMORY, (LIST, struct_memory))) = struct_val
-
+         (OBJECT_MEMORY, (LIST, memory))) = structure_val
         if ix[0] == 'id' and ix[1] in member_names:
             ix_val = ('integer', member_names.index(ix[1]))
         else:
             ix_val = walk(ix)
 
+    elif structure_val[0] == 'module':
+        (MODULE, id, (CLOSURE, closure)) = structure_val
+        config = state.symbol_table.get_config()
+        state.symbol_table.set_config(closure)
+        val = walk(ix)
+        state.symbol_table.set_config(config)
+        return val
+
     elif structure_val[0] == 'pattern':
         # simple patterns are just structures - skip the pattern operator
         return read_at_ix(structure_val[1], ix)
@@ -569,23 +581,26 @@ def store_at_ix(structure_val, ix, value):
     elif structure_val[0] == 'object':
         (OBJECT,
          (STRUCT_ID, (ID, struct_id)),
-         (OBJECT_MEMORY, (LIST, memory))) = structure_val
-        # compute the index -- for objects this has to be done
-        # in the context of the struct scope
-        struct_val = state.symbol_table.lookup_sym(struct_id)
-        # unpack the struct value
-        (STRUCT,
          (MEMBER_NAMES, (LIST, member_names)),
-         (STRUCT_MEMORY, (LIST, struct_memory))) = struct_val
-
+         (OBJECT_MEMORY, (LIST, memory))) = structure_val
         if ix[0] == 'id' and ix[1] in member_names:
             ix_val = ('integer', member_names.index(ix[1]))
         else:
             ix_val = walk(ix)
 
+    elif structure_val[0] == 'module':
+        (MODULE, id, (CLOSURE, closure)) = structure_val
+        config = state.symbol_table.get_config()
+        state.symbol_table.set_config(closure)
+        # execute an assignment in the context of the module
+        walk(('unify', ix, value))
+        state.symbol_table.set_config(config)
+        return
+
     elif structure_val[0] == 'pattern':
         # simple patterns are just structures - skip the pattern operator
-        return store_at_ix(structure_val[1], ix, value)
+        store_at_ix(structure_val[1], ix, value)
+        return
 
     else:
         raise ValueError("term '{}' is not a mutable structure"
@@ -618,13 +633,27 @@ def store_at_ix(structure_val, ix, value):
             # corresponding rval value
             (INTEGER, location) = lval[i]
             memory[location] = rval[i]
-
         return
 
     else:
         raise ValueError("index op '{}' in patterns not supported"
                          .format(ix_val[0]))
 
+#########################################################################
+# set module environment
+def set_module_env(modname):
+    module_val = state.symbol_table.lookup_sym(modname)
+    if module_val[0] != 'module':
+        raise ValueError("{} is not a module".format(modname))
+    (MODULE, id, (CLOSURE, closure)) = module_val
+    config = state.symbol_table.get_config()
+    state.symbol_table.set_config(closure)
+    return config
+
+#########################################################################
+def set_config(config):
+    state.symbol_table.set_config(config)
+
 #########################################################################
 def handle_builtins(node):
     (APPLY, (ID, opname), args) = node
@@ -897,8 +926,6 @@ def pop_stackframe(error_trace=False):
 
 #########################################################################
 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
@@ -916,6 +943,9 @@ 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.
+    # Note: we are keeping a stack of configs so that
+    # the debugger can look at contents of 
+    # Asteroid stack frames
     state.symbol_table.saved_configs.append(
         state.symbol_table.get_config()
     )
@@ -939,8 +969,8 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
 
         # Deconstruct function body
         (BODY,
-        (PATTERN, p),
-        (STMT_LIST, stmts)) = body_list_val[ i + 1]
+          (PATTERN, p),
+          stmts) = body_list_val[ i + 1]
 
         try:
             # Attempt to unify the actual args and the pattern
@@ -965,37 +995,24 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
     # Check for useless patterns
     try:
         if state.eval_redundancy:
-            old_debugging = debugging
-            debugging = False
             check_redundancy(body_list, fname)
-            debugging = old_debugging
 
     # Reset settings
     except RedundantPatternFound as r:
-        debugging = old_debugging
         # restore caller's env
         state.lineinfo = old_lineinfo
-        # Keep debugger up to date
-        if debugging: debugger.set_lineinfo(state.lineinfo)
         pop_stackframe()
+        if debugger: debugger.set_lineinfo(state.lineinfo)
         raise r
 
     # execute the function
     global function_return_value
-
     try:
         function_return_value.append(None)
-
-        walk_stmt_list(stmts)
-
-        # Pop the return value
+        walk(stmts)
         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)
 
@@ -1015,10 +1032,8 @@ def handle_call(obj_ref, fval, actual_val_args, fname):
     # all done with function call -- clean up and exit
     # restore caller's env
     state.lineinfo = old_lineinfo
-    # Keep debugger up to date
-    if debugging: debugger.set_lineinfo(state.lineinfo)
+    if debugger: debugger.set_lineinfo(state.lineinfo)
     pop_stackframe()
-
     return return_value
 
 #########################################################################
@@ -1054,9 +1069,19 @@ def declare_unifiers(unifiers):
     
 #########################################################################
 # node functions
+#########################################################################
+def stmt_list(node):
+
+    (STMT_LIST, (LIST, stmts)) = node
+    assert_match(STMT_LIST, 'stmt-list')
+    assert_match(LIST, 'list')
+    
+    for s in stmts:            
+        walk(s)
+
 #########################################################################
 def global_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (GLOBAL, (LIST, id_list)) = node
     assert_match(GLOBAL, 'global')
@@ -1074,31 +1099,29 @@ def global_stmt(node):
  
 #########################################################################
 def assert_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (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()
+    if debugger: debugger.notify(state)
 
     (UNIFY, pattern, exp) = node
     assert_match(UNIFY, 'unify')
 
     term = walk(exp)
-
     unifiers = unify(term, pattern)
-
     declare_unifiers(unifiers)
 
 #########################################################################
 def return_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (RETURN, e) = node
     assert_match(RETURN, 'return')
@@ -1109,7 +1132,8 @@ def return_stmt(node):
 
 #########################################################################
 def break_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
+
 
     (BREAK,) = node
     assert_match(BREAK, 'break')
@@ -1118,7 +1142,7 @@ def break_stmt(node):
 
 #########################################################################
 def throw_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (THROW, object) = node
     assert_match(THROW, 'throw')
@@ -1129,15 +1153,15 @@ def throw_stmt(node):
 
 #########################################################################
 def try_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (TRY,
-     (STMT_LIST, try_stmts),
+     try_stmts,
      (CATCH_LIST, (LIST, catch_list))) = node
 
-    stepping = debugger_has_stepped()
+    if debugger: debugger.has_stepped(state)
     try:
-        walk_stmt_list(try_stmts)
+        walk(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
@@ -1153,6 +1177,7 @@ def try_stmt(node):
     except PatternMatchFailed as inst:
         except_val = ('object',
                          ('struct-id', ('id', 'Exception')),
+                         ('member-names', ('list',["kind","val","__init__"])),
                          ('object-memory',
                           ('list',
                            [('string', 'PatternMatchFailed'),
@@ -1162,6 +1187,7 @@ def try_stmt(node):
     except RedundantPatternFound as inst:
         except_val = ('object',
                          ('struct-id', ('id', 'Exception')),
+                         ('member-names', ('list',["kind","val","__init__"])),
                          ('object-memory',
                           ('list',
                            [('string', 'RedundantPatternFound'),
@@ -1171,6 +1197,7 @@ def try_stmt(node):
     except NonLinearPatternError as inst:
         except_val = ('object',
                          ('struct-id', ('id', 'Exception')),
+                         ('member-names', ('list',["kind","val","__init__"])),
                          ('object-memory',
                           ('list',
                            [('string', 'NonLinearPatternError'),
@@ -1180,6 +1207,7 @@ def try_stmt(node):
     except ArithmeticError as inst:
         except_val = ('object',
                          ('struct-id', ('id', 'Exception')),
+                         ('member-names', ('list',["kind","val","__init__"])),
                          ('object-memory',
                           ('list',
                            [('string', 'ArithmeticError'),
@@ -1189,6 +1217,7 @@ def try_stmt(node):
     except FileNotFoundError as inst:
         except_val = ('object',
                          ('struct-id', ('id', 'Exception')),
+                         ('member-names', ('list',["kind","val","__init__"])),
                          ('object-memory',
                           ('list',
                            [('string', 'FileNotFound'),
@@ -1199,6 +1228,7 @@ def try_stmt(node):
         # mapping general Python exceptions into Asteroid's SystemError
         except_val = ('object',
                          ('struct-id', ('id', 'Exception')),
+                         ('member-names', ('list',["kind","val","__init__"])),
                          ('object-memory',
                           ('list',
                            [('string', 'SystemError'),
@@ -1214,7 +1244,7 @@ def try_stmt(node):
     for catch_val in catch_list:
         (CATCH,
          (CATCH_PATTERN, catch_pattern),
-         (CATCH_STMTS, catch_stmts)) = catch_val
+         catch_stmts) = catch_val
         try:
             unifiers = unify(except_val, catch_pattern)
         except PatternMatchFailed:
@@ -1223,7 +1253,7 @@ def try_stmt(node):
             # handler found - null out error_trace
             state.error_trace = None
             declare_unifiers(unifiers)
-            walk_stmt_list(catch_stmts, step_state=stepping)
+            walk(catch_stmts)
             return
 
     # no exception handler found - rethrow the exception
@@ -1231,39 +1261,34 @@ def try_stmt(node):
 
 #########################################################################
 def loop_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (LOOP, body_stmts) = node
     assert_match(LOOP, 'loop')
 
-    (STMT_LIST, body) = body_stmts
-
     try:
-        stepping = debugger_has_stepped()
+        if debugger: debugger.has_stepped(state)
         while True:
-            walk_stmt_list(body, step_state=stepping)
+            walk(body_stmts)
     except Break:
         pass
 
 #########################################################################
 def while_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
-    (WHILE, cond_exp, body_stmts) = node
+    (WHILE, (COND_EXP, cond), body_stmts) = node
     assert_match(WHILE, 'while')
 
-    (COND_EXP, cond) = cond_exp
-    (STMT_LIST, body) = body_stmts
-
     try:
-        stepping = debugger_has_stepped()
+        if debugger: debugger.has_stepped(state)
 
         (cond_type, cond_val) = walk(cond)
         if cond_type != 'boolean':
             raise ValueError("found '{}' expected 'boolean' in while loop"
                              .format(cond_type))
         while cond_val:
-            walk_stmt_list(body, step_state=stepping)
+            walk(body_stmts)
             (cond_type, cond_val) = walk(cond)
             if cond_type != 'boolean':
                 raise ValueError("found '{}' expected 'boolean' in while loop"
@@ -1273,18 +1298,15 @@ def while_stmt(node):
 
 #########################################################################
 def repeat_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
-    (REPEAT, body_stmts, cond_exp) = node
+    (REPEAT, body_stmts, (COND_EXP, cond)) = node
     assert_match(REPEAT, 'repeat')
 
-    (COND_EXP, cond) = cond_exp
-    (STMT_LIST, body) = body_stmts
-
     try:
-        stepping = debugger_has_stepped()
+        if debugger: debugger.has_stepped(state)
         while True:
-            walk_stmt_list(body, step_state=stepping)
+            walk(body_stmts)
             (cond_type, cond_val) = walk(cond)
             if cond_type != 'boolean':
                 raise ValueError("found '{}' expected 'boolean' in repeat loop"
@@ -1297,9 +1319,10 @@ def repeat_stmt(node):
 
 #########################################################################
 def for_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
+
 
-    (FOR, (IN_EXP, in_exp), (STMT_LIST, stmt_list)) = node
+    (FOR, (IN_EXP, in_exp), stmt_list) = node
     assert_match(FOR, 'for')
 
     (IN, pattern, list_term) = in_exp
@@ -1325,7 +1348,7 @@ def for_stmt(node):
     #             print y.
     #      end for
     try:
-        stepping = debugger_has_stepped()
+        if debugger: debugger.has_stepped(state)
         for term in list_val:
             try:
                 unifiers = unify(term,pattern)
@@ -1333,14 +1356,14 @@ def for_stmt(node):
                 pass
             else:
                 declare_unifiers(unifiers)
-                walk_stmt_list(stmt_list, step_state=stepping)
+                walk(stmt_list)
                 
     except Break:
         pass
 
 #########################################################################
 def if_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (IF, (LIST, if_list)) = node
     assert_match(IF, 'if')
@@ -1353,7 +1376,7 @@ def if_stmt(node):
 
         (IF_CLAUSE,
          (COND, cond),
-         (STMT_LIST, stmts)) = if_list[ i + 1 ]
+         stmts) = if_list[ i + 1 ]
 
         (cond_type, cond_val) = walk(cond)
         if cond_type != 'boolean':
@@ -1361,11 +1384,12 @@ def if_stmt(node):
                             .format(cond_type))
 
         if cond_val:
-            walk_stmt_list(stmts)
+            walk(stmts)
             break
+
 #########################################################################
 def struct_def_stmt(node):
-    notify_debugger()
+    if debugger: debugger.notify(state)
 
     (STRUCT_DEF, (ID, struct_id), (MEMBER_LIST, (LIST, member_list))) = node
     assert_match(STRUCT_DEF, 'struct-def')
@@ -1405,32 +1429,28 @@ 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')
+def module_def_stmt(node):
+    (MODULE_DEF, (ID, modname), stmts) = node
+    assert_match(MODULE_DEF, 'module-def')
 
-    for e in inlist:
-        walk(e)
-
-    debugging = old_debugging
+    state.symbol_table.push_scope({})
+    walk(stmts)
+    closure = state.symbol_table.get_closure()
+    state.symbol_table.pop_scope()
 
-    return
+    module_type = ('module', ('id', modname), ('scope', closure))
+    state.symbol_table.enter_sym(modname, module_type)
 
 #########################################################################
-def top_level_exp_stmt(node):
+def load_stmt(node):
+    if debugger: debugger.notify(state)
 
-    (TOP_LEVEL_EXP, exp) = node
-    assert_match(TOP_LEVEL_EXP, 'top-level-exp')
+    (LOAD_STMT, inlist) = node
+    assert_match(LOAD_STMT, 'load-stmt')
 
-    notify_debugger()
+    walk(inlist)
 
-    return walk(exp)
+    return
 
 #########################################################################
 def eval_exp(node):
@@ -1486,11 +1506,11 @@ def apply_exp(node):
         # computed.
         f_name = 'lambda'
     elif f[0] == 'index':
-        # object member function
+        # member/module function
         (INDEX, ix, (ID, f_name)) = f
-        # 'str' is necessary in case we use an index value
-        # instead of a function name -- see regression test test085.ast
-        f_name = "member function " + str(f_name)
+        if not isinstance(f_name, str):
+            raise ValueError("function names have to be strings")
+        f_name = "member/module function " + f_name
     else:
         # just a regular function call
         (ID, f_name) = f
@@ -1516,7 +1536,13 @@ def apply_exp(node):
 
     # object constructor call
     elif f_val[0] == 'struct':
-        (ID, struct_id) = f
+        if f[0] == 'index':
+            # constructor name qualified with a module name
+            (INDEX, (ID, modname), (ID, struct_id)) = f
+        else: 
+            # inscope constructor call
+            modname = None
+            (ID, struct_id) = f
         (STRUCT,
          (MEMBER_NAMES, (LIST, member_names)),
          (STRUCT_MEMORY, (LIST, struct_memory))) = f_val
@@ -1527,6 +1553,7 @@ def apply_exp(node):
         # create our object
         obj_ref = ('object',
                    ('struct-id', ('id', struct_id)),
+                   ('member-names', ('list', member_names)),
                    ('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.
@@ -1534,11 +1561,15 @@ def apply_exp(node):
         if '__init__' in member_names:
             slot_ix = member_names.index('__init__')
             init_fval = struct_memory[slot_ix]
-            # calling a member function - push struct scope
+            # calling a member function
+            if modname:
+                orig_config = set_module_env(modname)
             handle_call(obj_ref,
                         init_fval,
                         arg_val,
                         f_name)
+            if modname:
+                set_config(orig_config)
         # the struct does not have an __init__ function but
         # we have a constructor call with args, e.g. Foo(1,2)
         # try to apply a default constructor by copying the
@@ -1759,8 +1790,7 @@ def process_lineinfo(node):
     (LINEINFO, lineinfo_val) = node
     assert_match(LINEINFO, 'lineinfo')
 
-    if debugging:
-        debugger.set_lineinfo(lineinfo_val)
+    if debugger: debugger.set_lineinfo(lineinfo_val)
 
     #lhh
     #print("lineinfo: {}".format(lineinfo_val))
@@ -1796,60 +1826,21 @@ def set_ret_val(node):
 
     # Debugger keeps track of the most recent
     # return value
-    if debugging and val:
-        debugger.retval = term2string(val)
+    if debugger 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)
+def clear_ret_val(node):
+    (CLEAR_RET_VAL,) = node
+    assert_match(CLEAR_RET_VAL, 'clear-ret-val')
+
+    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
 
 #########################################################################
 # walk
@@ -1858,50 +1849,20 @@ def walk(node):
     # node format: (TYPE, [child1[, child2[, ...]]])
     type = node[0]
 
-    if type == 'clear-ret-val':
-        # implemented here instead of dictionary for efficiency reasons
-        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:
+    if type in dispatch_dict:
         node_function = dispatch_dict[type]
         return node_function(node)
     else:
         raise ValueError("feature '{}' not yet implemented".format(type))
 
-#########################################################################
-# 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)
-
-    (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 = {
-    # Note: statement lists are now handled by separate functions outside
-    #       the walk function
     # statements - statements do not produce return values
+    'stmt-list'     : stmt_list,
     'lineinfo'      : process_lineinfo,
     'set-ret-val'   : set_ret_val,
+    'clear-ret-val' : clear_ret_val,
     'noop'          : lambda node : None,
     'assert'        : assert_stmt,
     'unify'         : unify_stmt,
@@ -1916,10 +1877,10 @@ def debug_walk(node, dbg):
     'throw'         : throw_stmt,
     'try'           : try_stmt,
     'struct-def'    : struct_def_stmt,
-    'top-level-exp' : top_level_exp_stmt,
+    'module-def'    : module_def_stmt,
     # expressions - expressions do produce return values
     'list'          : list_exp,
-    'import_stmt'   : import_stmt,
+    'load-stmt'     : load_stmt,
     'tuple'         : tuple_exp,
     'to-list'       : to_list_exp,
     'head-tail'     : head_tail_exp,
@@ -2032,54 +1993,3 @@ def check_redundancy( body_list, f_name ):
             else:                               #CONFLICTION
                 raise RedundantPatternFound( ptrn_h , ptrn_l , f_name, location_h, location_l )
 
-#######################################################################################
-# *** Asteroid Debugger (ADB) helper functions ***
-# 
-# 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.
-#  
-# 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
-#
-#######################################################################################
-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 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
-
diff --git a/docs/Reference Guide.rst b/docs/Reference Guide.rst
index 1ea8c117..ed51cd63 100644
--- a/docs/Reference Guide.rst
+++ b/docs/Reference Guide.rst
@@ -238,6 +238,20 @@ Syntax: ``LOOP DO? stmt_list END``
 The ``loop`` statement executes the statements in the loop body indefinitely
 unless a ``break`` statement is encountered.
 
+Module
+%%%%%%
+
+Syntax: ``MODULE module_name WITH stmt_list END``
+
+The ``module`` statement introduces a new name space which is accessible via
+the module name,
+::
+      module foo with
+         let x = 1.
+      end
+      assert(foo@x == 1).
+      assert(not isdefined "x"). -- x is not defined in the global scope
+
 Repeat-Until
 %%%%%%%%%%%%
 
@@ -688,6 +702,7 @@ are written in quotes.
     | GLOBAL id_list '.'?
     | ASSERT exp '.'?
     | STRUCTURE ID WITH struct_stmts END
+    | MODULE ID WITH stmt_list END
     | LET pattern '=' exp '.'?
     | LOOP DO? stmt_list END
     | FOR pattern IN exp DO stmt_list END
diff --git a/docs/Reference Guide.txt b/docs/Reference Guide.txt
index 865dec6c..67382b3a 100644
--- a/docs/Reference Guide.txt
+++ b/docs/Reference Guide.txt
@@ -235,6 +235,20 @@ Syntax: ``LOOP DO? stmt_list END``
 The ``loop`` statement executes the statements in the loop body indefinitely
 unless a ``break`` statement is encountered.
 
+Module
+%%%%%%
+
+Syntax: ``MODULE module_name WITH stmt_list END``
+
+The ``module`` statement introduces a new name space which is accessible via 
+the module name,
+::
+      module foo with
+         let x = 1.
+      end
+      assert(foo@x == 1).
+      assert(not isdefined "x"). -- x is not defined in the global scope
+
 Repeat-Until
 %%%%%%%%%%%%
 


Test patch:
diff --git a/asteroid/test-suites/regression-tests/test085.ast b/asteroid/test-suites/regression-tests/test085.ast
deleted file mode 100644
index 50bb0f4c..00000000
--- a/asteroid/test-suites/regression-tests/test085.ast
+++ /dev/null
@@ -1,13 +0,0 @@
-
-load system io.
-
-structure A with
-    data x.
-    data y.
-    end 
-
-let a = A(999, (lambda with none do return "Hello World!"+ tostring (this@x))).
-io @println (tostring (a@1 ())).
-
-assert(a@1() is "Hello World!999").
-
diff --git a/asteroid/test-suites/regression-tests/test148.ast b/asteroid/test-suites/regression-tests/test148.ast
new file mode 100644
index 00000000..7cbc16b0
--- /dev/null
+++ b/asteroid/test-suites/regression-tests/test148.ast
@@ -0,0 +1,19 @@
+load system io.
+
+let g = 1.
+module test with
+   let x  = 1.
+   let a = [1 to 10].
+end
+
+io @println (test@x).
+io @println (test@a@2).
+
+let test@x = 2.
+let test@a@2 = -10.
+
+io @println (test@x).
+io @println (test@a@2).
+
+io @println g.
+
diff --git a/asteroid/test-suites/regression-tests/test149.ast b/asteroid/test-suites/regression-tests/test149.ast
new file mode 100644
index 00000000..21ea881a
--- /dev/null
+++ b/asteroid/test-suites/regression-tests/test149.ast
@@ -0,0 +1,13 @@
+module test with
+   load system io.
+   let x = "hello"
+   function hello with () do
+       io @println x.
+   end
+end
+
+test @hello ().
+
+let f = test@hello.
+f().
+
diff --git a/asteroid/test-suites/regression-tests/test150.ast b/asteroid/test-suites/regression-tests/test150.ast
new file mode 100644
index 00000000..f64d73c9
--- /dev/null
+++ b/asteroid/test-suites/regression-tests/test150.ast
@@ -0,0 +1,5 @@
+module test with
+   let p = pattern %[(x:%integer) if x > 0]%.
+end
+
+let k:*test@p = 1.
diff --git a/asteroid/test-suites/regression-tests/test151.ast b/asteroid/test-suites/regression-tests/test151.ast
new file mode 100644
index 00000000..0604ff4b
--- /dev/null
+++ b/asteroid/test-suites/regression-tests/test151.ast
@@ -0,0 +1,12 @@
+module foo with
+   structure A with
+      data a.
+      function dump with none do
+         return this@a.
+      end
+   end
+end
+
+let a = foo@A(1).
+let foo@A(x) = a.
+assert(x ==1).