changeset 26661:cf9e10ce3351

move variable values from symbol_record objects to stack_frame objects Apologies for the massive commit. I see no way to untangle these changes into a set of smaller incremental changes in a way that would be more useful. Previously, handling data for recursive function calls was managed by a stack of values in the symbol_record class and an auxiliary integer variable was used for managing the recursion depth (context_id). Now, values for local variables are in the stack_frame class and recursion is handled naturally by the call_stack as a new stack frame is added to the call_stack object for any call to a function or a script. Values for internal function call information (nargin, nargout, etc.) are now stored specially in the stack_frame object. Values for global variables are now stored in a map in the call_stack object. Values for persistent variables are stored in the corresponding octave_user_function object. Access to non-local variables inside nested functions is managed through pointers to stack_frame objects for the parent function scopes. The new implementation more closely resembles the techniques described in standard compiler literature. These changes should make it easier to create proper closures and finally solve bug #39257 (handles to nested functions are not yet supported). They may also make it easier to implement JIT compiler, though that is probably still a long way off. The new stack-frame.h file has some details about the new implementation of stack frames that should help in understanding how things work now. Describing each change to each file and function will probably not provide much greater understanding of the changes and would be quite tedious to write so I am omitting them.
author John W. Eaton <jwe@octave.org>
date Mon, 28 Jan 2019 18:01:46 +0000
parents e4909f142491
children 05fc703b419a
files libgui/src/workspace-view.cc libinterp/corefcn/call-stack.cc libinterp/corefcn/call-stack.h libinterp/corefcn/debug.cc libinterp/corefcn/error.cc libinterp/corefcn/fcn-info.cc libinterp/corefcn/help.cc libinterp/corefcn/input.cc libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h libinterp/corefcn/load-save.cc libinterp/corefcn/ls-mat5.cc libinterp/corefcn/ls-oct-text.cc libinterp/corefcn/mex.cc libinterp/corefcn/module.mk libinterp/corefcn/octave-link.cc libinterp/corefcn/stack-frame-walker.h libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/corefcn/syminfo-accumulator.h libinterp/corefcn/symrec.cc libinterp/corefcn/symrec.h libinterp/corefcn/symscope.cc libinterp/corefcn/symscope.h libinterp/corefcn/symtab.cc libinterp/corefcn/symtab.h libinterp/corefcn/syscalls.cc libinterp/corefcn/variables.cc libinterp/octave-value/ov-classdef.cc libinterp/octave-value/ov-fcn-handle.cc libinterp/octave-value/ov-fcn.h libinterp/octave-value/ov-usr-fcn.cc libinterp/octave-value/ov-usr-fcn.h libinterp/parse-tree/lex.ll libinterp/parse-tree/oct-lvalue.cc libinterp/parse-tree/oct-lvalue.h libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/pt-anon-scopes.cc libinterp/parse-tree/pt-anon-scopes.h libinterp/parse-tree/pt-decl.cc libinterp/parse-tree/pt-decl.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h libinterp/parse-tree/pt-fcn-handle.cc libinterp/parse-tree/pt-id.cc libinterp/parse-tree/pt-id.h
diffstat 46 files changed, 5378 insertions(+), 3438 deletions(-) [+]
line wrap: on
line diff
--- a/libgui/src/workspace-view.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libgui/src/workspace-view.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -44,6 +44,7 @@
 #include "resource-manager.h"
 
 #include "interpreter-private.h"
+#include "interpreter.h"
 #include "syminfo.h"
 
 namespace octave
@@ -415,10 +416,13 @@
       {
         QString var_name = get_var_name (index);
 
-        symbol_scope scope
-          = __get_current_scope__ ("workspace_view::handle_contextmenu_copy_value");
+        // FIXME: this looks suspciously unsafe.
+        interpreter& interp
+          = __get_interpreter__ ("workspace_view::handle_contextmenu_copy_value");
 
-        octave_value val = scope ? scope.varval (var_name.toStdString ()) : 0;
+        octave_value val = interp.varval (var_name.toStdString ());
+        if (val.is_undefined ())
+          val = 0;
         std::ostringstream buf;
         val.print_raw (buf, true);
 
@@ -464,7 +468,6 @@
         QString var_name = get_var_name (index);
 
         symbol_info_list syminfo = m_model->get_symbol_info ();
-
         octave_value val = syminfo.varval (var_name.toStdString ());
 
         emit edit_variable_signal (var_name, val);
--- a/libinterp/corefcn/call-stack.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/call-stack.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -30,6 +30,7 @@
 #include "call-stack.h"
 #include "defun.h"
 #include "interpreter.h"
+#include "interpreter-private.h"
 #include "oct-map.h"
 #include "ov.h"
 #include "ov-fcn.h"
@@ -37,7 +38,10 @@
 #include "ov-usr-fcn.h"
 #include "pager.h"
 #include "parse.h"
+#include "stack-frame.h"
+#include "stack-frame-walker.h"
 #include "syminfo.h"
+#include "syminfo-accumulator.h"
 #include "symrec.h"
 #include "symscope.h"
 #include "variables.h"
@@ -45,180 +49,135 @@
 // Use static fields for the best efficiency.
 // NOTE: C++0x will allow these two to be merged into one.
 static const char *bt_fieldnames[] =
-  { "file", "name", "line", "column", "scope", "context", nullptr };
+  { "file", "name", "line", "column", "scope", nullptr };
 static const octave_fields bt_fields (bt_fieldnames);
 
 namespace octave
 {
-  std::string
-  call_stack::stack_frame::fcn_file_name (void) const
-  {
-    return m_fcn ? m_fcn->fcn_file_name () : "";
-  }
-
-  std::string
-  call_stack::stack_frame::fcn_name (bool print_subfn) const
+  class stack_trace_generator : public stack_frame_walker
   {
-    std::string retval;
+  public:
+
+    stack_trace_generator (size_t nskip = 0)
+      : stack_frame_walker (), m_frames (), m_nskip (nskip),
+        m_curr_frame (0)
+    { }
+
+    stack_trace_generator (const stack_trace_generator&) = delete;
+
+    stack_trace_generator& operator = (const stack_trace_generator&) = delete;
+
+    ~stack_trace_generator (void) = default;
+
+    std::list<stack_frame *> frames (void) const { return m_frames; }
+
+    size_t current_frame (void) const { return m_curr_frame; }
 
-    if (m_fcn)
-      {
-        std::string parent_fcn_name = m_fcn->parent_fcn_name ();
+    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
+    {
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+    void visit_script_stack_frame (script_stack_frame& frame)
+    {
+      maybe_add_frame (frame);
 
-        if (print_subfn && ! parent_fcn_name.empty ())
-          retval = parent_fcn_name + '>';
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
+    {
+      maybe_add_frame (frame);
+
+      symbol_scope scope = frame.get_scope ();
 
-        if (m_fcn->is_anonymous_function ())
-          retval += octave_fcn_handle::anonymous;
-        else
-          retval += m_fcn->name ();
-      }
-    else
-      retval = "<unknown>";
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
 
-    return retval;
-  }
+    void visit_scope_stack_frame (scope_stack_frame& frame)
+    {
+      symbol_scope scope = frame.get_scope ();
+
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+  private:
 
-  bool
-  call_stack::stack_frame::operator == (const call_stack::stack_frame& rhs) const
+    void maybe_add_frame (stack_frame& frame)
+    {
+      if (m_nskip > 0)
+        {
+          m_nskip--;
+          return;
+        }
+
+      m_frames.push_back (&frame);
+    }
+
+    std::list<stack_frame *> m_frames;
+
+    // Number of user code frames to skip.
+    size_t m_nskip;
+
+    size_t m_curr_frame;
+  };
+
+  call_stack::call_stack (tree_evaluator& evaluator)
+    : m_evaluator (evaluator), m_cs (), m_curr_frame (0),
+      m_max_stack_depth (1024), m_global_values ()
   {
-    if (this->line () != rhs.line ())
-      return false;
-    else if (this->column () != rhs.column ())
-      return false;
-    else if (this->fcn_file_name () != rhs.fcn_file_name ())
-      return false;
-    else if (this->fcn_name () != rhs.fcn_name ())
-      return false;
-    else
-      return true;
+    push (symbol_scope ("top scope"));
   }
 
-  symbol_info_list
-  call_stack::stack_frame::make_symbol_info_list
-    (const std::list<symbol_record>& symrec_list) const
-  {
-    symbol_info_list symbol_stats;
-
-    for (const auto& sr : symrec_list)
-      {
-        octave_value value = sr.varval (m_context);
-
-        if (value.is_defined ())
-          {
-            symbol_info syminf (sr.name (), value,
-                                sr.is_formal (),
-                                sr.is_global (), sr.is_persistent ());
-
-            symbol_stats.append (syminf);
-          }
-      }
-
-    return symbol_stats;
-  }
-
-  symbol_info_list
-  call_stack::stack_frame::glob_symbol_info (const std::string& pat) const
-  {
-    return make_symbol_info_list (m_scope.glob (pat, true));
-  }
-
-  symbol_info_list
-  call_stack::stack_frame::regexp_symbol_info (const std::string& pat) const
-  {
-    return make_symbol_info_list (m_scope.regexp (pat, true));
-  }
-
-  symbol_info_list call_stack::stack_frame::get_symbol_info (void) const
-  {
-    return make_symbol_info_list (m_scope.all_variables ());
-  }
-
-  call_stack::call_stack (interpreter& interp)
-    : cs (), curr_frame (0), m_max_stack_depth (1024), m_interpreter (interp)
-  {
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    push (nullptr, nullptr, symtab.top_scope (), 0);
-  }
-
-  int
-  call_stack::current_line (void) const
+  int call_stack::current_line (void) const
   {
     int retval = -1;
 
-    if (! cs.empty ())
+    if (! m_cs.empty ())
       {
-        const stack_frame& elt = cs[curr_frame];
-        retval = elt.m_line;
+        const stack_frame *elt = m_cs[m_curr_frame];
+        retval = elt->line ();
       }
 
     return retval;
   }
 
-  int
-  call_stack::current_column (void) const
+  int call_stack::current_column (void) const
   {
     int retval = -1;
 
-    if (! cs.empty ())
+    if (! m_cs.empty ())
       {
-        const stack_frame& elt = cs[curr_frame];
-        retval = elt.m_column;
+        const stack_frame *elt = m_cs[m_curr_frame];
+        retval = elt->column ();
       }
 
     return retval;
   }
 
-  size_t
-  call_stack::num_user_code_frames (octave_idx_type& curr_user_frame) const
-  {
-    size_t retval = 0;
-
-    curr_user_frame = 0;
-
-    // Look for the caller of dbstack.
-    size_t xframe = cs[curr_frame].m_prev;
-
-    bool found = false;
-
-    size_t k = cs.size ();
-
-    for (auto p = cs.crbegin (); p != cs.crend (); p++)
-      {
-        octave_function *f = (*p).m_fcn;
-
-        if (--k == xframe)
-          found = true;
-
-        if (f && f->is_user_code ())
-          {
-            if (! found)
-              curr_user_frame++;
-
-            retval++;
-          }
-      }
-
-    // We counted how many user frames were not the one, in reverse.
-    // Now set curr_user_frame to be the index in the other direction.
-    curr_user_frame = retval - curr_user_frame - 1;
-
-    return retval;
-  }
-
-  octave_user_code *
-  call_stack::caller_user_code (size_t nskip) const
+  octave_user_code * call_stack::caller_user_code (size_t nskip) const
   {
     octave_user_code *retval = nullptr;
 
-    auto p = cs.cend ();
+    size_t xframe = m_curr_frame;
 
-    while (p != cs.cbegin ())
+    while (xframe != 0)
       {
-        const stack_frame& elt = *(--p);
+        const stack_frame *elt = m_cs[xframe];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
           {
@@ -230,98 +189,101 @@
                 break;
               }
           }
+
+        xframe = m_cs[xframe]->previous ();
       }
 
     return retval;
   }
 
-  int
-  call_stack::caller_user_code_line (void) const
+  int call_stack::caller_user_code_line (void) const
   {
     int retval = -1;
 
-    auto p = cs.cend ();
+    size_t xframe = m_curr_frame;
 
-    while (p != cs.cbegin ())
+    while (xframe != 0)
       {
-        const stack_frame& elt = *(--p);
+        const stack_frame *elt = m_cs[xframe];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
           {
-            if (elt.m_line > 0)
+            if (elt->line () > 0)
               {
-                retval = elt.m_line;
+                retval = elt->line ();
                 break;
               }
           }
+
+        xframe = m_cs[xframe]->previous ();
       }
 
     return retval;
   }
 
-  unwind_protect *
-  call_stack::curr_fcn_unwind_protect_frame (void) const
+  unwind_protect * call_stack::curr_fcn_unwind_protect_frame (void) const
   {
-    auto p = cs.cend ();
+    size_t xframe = m_curr_frame;
 
-    while (p != cs.cbegin ())
+    while (xframe != 0)
       {
-        const stack_frame& elt = *(--p);
+        const stack_frame *elt = m_cs[xframe];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
-          return elt.m_unwind_protect_frame;
+          return elt->unwind_protect_frame ();
+
+        xframe = m_cs[xframe]->previous ();
       }
 
     return nullptr;
   }
 
-  int
-  call_stack::caller_user_code_column (void) const
+  int call_stack::caller_user_code_column (void) const
   {
     int retval = -1;
 
-    auto p = cs.cend ();
+    size_t xframe = m_curr_frame;
 
-    while (p != cs.cbegin ())
+    while (xframe != 0)
       {
-        const stack_frame& elt = *(--p);
+        const stack_frame *elt = m_cs[xframe];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
           {
-            if (elt.m_column)
+            if (elt->column ())
               {
-                retval = elt.m_column;
+                retval = elt->column ();
                 break;
               }
           }
+
+        xframe = m_cs[xframe]->previous ();
       }
 
     return retval;
   }
 
-  octave_user_code *
-  call_stack::debug_user_code (void) const
+  octave_user_code * call_stack::debug_user_code (void) const
   {
     octave_user_code *retval = nullptr;
 
     // This should never happen...
-    if (curr_frame == 0)
+    if (m_curr_frame == 0)
       return retval;
 
-    // Start looking with the caller of the calling debug function.
-    size_t i = cs[curr_frame].m_prev;
+    size_t i = m_curr_frame;
 
     while (i != 0)
       {
-        const stack_frame& elt = cs[i--];
+        const stack_frame *elt = m_cs[i--];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
           {
@@ -333,29 +295,27 @@
     return retval;
   }
 
-  int
-  call_stack::debug_user_code_line (void) const
+  int call_stack::debug_user_code_line (void) const
   {
     int retval = -1;
 
     // This should never happen...
-    if (curr_frame == 0)
+    if (m_curr_frame == 0)
       return retval;
 
-    // Start looking with the caller of the calling debug function.
-    size_t i = cs[curr_frame].m_prev;
+    size_t i = m_curr_frame;
 
     while (i != 0)
       {
-        const stack_frame& elt = cs[i--];
+        const stack_frame *elt = m_cs[i--];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
           {
-            if (elt.m_line)
+            if (elt->line ())
               {
-                retval = elt.m_line;
+                retval = elt->line ();
                 break;
               }
           }
@@ -364,29 +324,28 @@
     return retval;
   }
 
-  int
-  call_stack::debug_user_code_column (void) const
+  int call_stack::debug_user_code_column (void) const
   {
     int retval = -1;
 
     // This should never happen...
-    if (curr_frame == 0)
+    if (m_curr_frame == 0)
       return retval;
 
     // Start looking with the caller of the calling debug function.
-    size_t i = cs[curr_frame].m_prev;
+    size_t i = m_curr_frame;
 
     while (i != 0)
       {
-        const stack_frame& elt = cs[i--];
+        const stack_frame *elt = m_cs[i--];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && f->is_user_code ())
           {
-            if (elt.m_column)
+            if (elt->column ())
               {
-                retval = elt.m_column;
+                retval = elt->column ();
                 break;
               }
           }
@@ -395,18 +354,17 @@
     return retval;
   }
 
-  bool
-  call_stack::all_scripts (void) const
+  bool call_stack::all_scripts (void) const
   {
     bool retval = true;
 
-    auto p = cs.cend ();
+    auto p = m_cs.cend ();
 
-    while (p != cs.cbegin ())
+    while (p != m_cs.cbegin ())
       {
-        const stack_frame& elt = *(--p);
+        const stack_frame *elt = *(--p);
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (f && ! f->is_user_script ())
           {
@@ -418,64 +376,108 @@
     return retval;
   }
 
-  void
-  call_stack::push (octave_function *fcn, unwind_protect *up_frame)
+  stack_frame * call_stack::get_static_link (size_t prev_frame) const
   {
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
+    // FIXME: is there a better way?
+
+    stack_frame *slink = nullptr;
+
+    if (m_curr_frame > 0)
+      {
+        octave_function *t_fcn = m_cs[prev_frame]->function ();
+
+        slink = (t_fcn
+                 ? (t_fcn->is_user_code ()
+                    ? m_cs[prev_frame] : m_cs[prev_frame]->static_link ())
+                 : m_cs[prev_frame]);
+      }
 
-    push (fcn, up_frame, symtab.current_scope (), symtab.current_context ());
+    return slink;
+  }
+
+  void call_stack::push (const symbol_scope& scope)
+  {
+    size_t prev_frame = m_curr_frame;
+    m_curr_frame = m_cs.size ();
+
+    // m_max_stack_depth should never be less than zero.
+    if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
+      error ("max_stack_depth exceeded");
+
+    stack_frame *slink = get_static_link (prev_frame);
+
+    m_cs.push_back (new scope_stack_frame (*this, prev_frame, scope, slink));
   }
 
-  void
-  call_stack::push (octave_function *fcn, unwind_protect *up_frame,
-                    const symbol_scope& scope,
-                    symbol_record::context_id context)
+  void call_stack::push (octave_user_function *fcn, unwind_protect *up_frame)
   {
-    size_t prev_frame = curr_frame;
-    curr_frame = cs.size ();
+    size_t prev_frame = m_curr_frame;
+    m_curr_frame = m_cs.size ();
+
+    // m_max_stack_depth should never be less than zero.
+    if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
+      error ("max_stack_depth exceeded");
+
+    stack_frame *slink = get_static_link (prev_frame);
+
+    m_cs.push_back (new user_fcn_stack_frame (*this, fcn, up_frame,
+                                              prev_frame, slink));
+  }
+
+  void call_stack::push (octave_user_script *script, unwind_protect *up_frame)
+  {
+    size_t prev_frame = m_curr_frame;
+    m_curr_frame = m_cs.size ();
 
     // m_max_stack_depth should never be less than zero.
-    if (curr_frame > static_cast<size_t> (m_max_stack_depth))
+    if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
       error ("max_stack_depth exceeded");
 
-    cs.push_back (stack_frame (fcn, up_frame, scope, context, prev_frame));
+    stack_frame *slink = get_static_link (prev_frame);
 
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.set_scope_and_context (scope, context);
+    m_cs.push_back (new script_stack_frame (*this, script, up_frame,
+                                            prev_frame, slink));
   }
 
-  bool
-  call_stack::goto_frame (size_t n, bool verbose)
+  void call_stack::push (octave_function *fcn)
+  {
+    size_t prev_frame = m_curr_frame;
+    m_curr_frame = m_cs.size ();
+
+    // m_max_stack_depth should never be less than zero.
+    if (m_curr_frame > static_cast<size_t> (m_max_stack_depth))
+      error ("max_stack_depth exceeded");
+
+    stack_frame *slink = get_static_link (prev_frame);
+
+    m_cs.push_back (new compiled_fcn_stack_frame (*this, fcn, prev_frame,
+                                                  slink));
+  }
+
+  bool call_stack::goto_frame (size_t n, bool verbose)
   {
     bool retval = false;
 
-    if (n < cs.size ())
+    if (n < m_cs.size ())
       {
         retval = true;
 
-        curr_frame = n;
-
-        const stack_frame& elt = cs[n];
+        m_curr_frame = n;
 
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        symtab.set_scope_and_context (elt.m_scope, elt.m_context);
+        const stack_frame *elt = m_cs[n];
 
         if (verbose)
-          octave_stdout << "stopped in " << elt.fcn_name ()
-                        << " at line " << elt.m_line
-                        << " column " << elt.m_column
-                        << " [" << elt.fcn_file_name () << "] "
-                        << "[context = " << elt.m_context << "])"
+          octave_stdout << "stopped in " << elt->fcn_name ()
+                        << " at line " << elt->line ()
+                        << " column " << elt->column ()
+                        << " [" << elt->fcn_file_name () << "] "
                         << std::endl;
       }
 
     return retval;
   }
 
-  bool
-  call_stack::goto_frame_relative (int nskip, bool verbose)
+  bool call_stack::goto_frame_relative (int nskip, bool verbose)
   {
     bool retval = false;
 
@@ -486,19 +488,18 @@
     else if (nskip > 0)
       incr = 1;
 
-    // Start looking with the caller of dbup/dbdown/keyboard.
-    size_t xframe = cs[curr_frame].m_prev;
+    size_t xframe = m_curr_frame;
 
     while (true)
       {
-        if ((incr < 0 && xframe == 0) || (incr > 0 && xframe == cs.size () - 1))
+        if ((incr < 0 && xframe == 0) || (incr > 0 && xframe == m_cs.size () - 1))
           break;
 
         xframe += incr;
 
-        const stack_frame& elt = cs[xframe];
+        const stack_frame *elt = m_cs[xframe];
 
-        octave_function *f = elt.m_fcn;
+        octave_function *f = elt->function ();
 
         if (xframe == 0 || (f && f->is_user_code ()))
           {
@@ -509,21 +510,16 @@
 
             if (nskip == 0)
               {
-                curr_frame = xframe;
-                cs[cs.size () - 1].m_prev = curr_frame;
-
-                symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-                symtab.set_scope_and_context (elt.m_scope, elt.m_context);
+                m_curr_frame = xframe;
 
                 if (verbose)
                   {
                     std::ostringstream buf;
 
                     if (f)
-                      buf << "stopped in " << elt.fcn_name ()
-                          << " at line " << elt.m_line
-                          << " [" << elt.fcn_file_name () << "] "
+                      buf << "stopped in " << elt->fcn_name ()
+                          << " at line " << elt->line ()
+                          << " [" << elt->fcn_file_name () << "] "
                           << std::endl;
                     else
                       buf << "at top level" << std::endl;
@@ -537,110 +533,103 @@
           }
         else if (incr == 0)  // Break out of infinite loop by choosing an incr.
           incr = -1;
-
-        // There is no need to set scope and context here.  That will
-        // happen when the dbup/dbdown/keyboard frame is popped and we
-        // jump to the new "prev" frame set above.
       }
 
     return retval;
   }
 
-  void
-  call_stack::goto_caller_frame (void)
+  size_t call_stack::find_caller_frame (void)
   {
-    size_t xframe = curr_frame;
+    // Find the preceeding frame that corresponds to a script or
+    // function.  Expected to be called from a stack frame corresponding
+    // to a compiled function.
+
+    size_t xframe = m_curr_frame;
 
     bool skipped = false;
 
     while (xframe != 0)
       {
-        xframe = cs[xframe].m_prev;
-
-        const stack_frame& elt = cs[xframe];
+        xframe = m_cs[xframe]->previous ();
 
-        octave_function *f = elt.m_fcn;
-
-        if (elt.m_scope == cs[0].m_scope || (f && f->is_user_code ()))
+        stack_frame *frm = m_cs[xframe];
+        if (frm->is_user_fcn_frame () || frm->is_user_script_frame ())
           {
             if (! skipped)
               // We found the current user code frame, so skip it.
               skipped = true;
             else
-              {
-                // We found the caller user code frame.
-                stack_frame tmp (elt);
-                tmp.m_prev = curr_frame;
-
-                curr_frame = cs.size ();
-
-                cs.push_back (tmp);
-
-                symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-                symtab.set_scope_and_context (tmp.m_scope, tmp.m_context);
-
-                break;
-              }
+              return xframe;
           }
       }
+
+    return 0;
   }
 
-  void
-  call_stack::goto_base_frame (void)
+  void call_stack::goto_caller_frame (void)
   {
-    stack_frame tmp (cs[0]);
-    tmp.m_prev = curr_frame;
-
-    curr_frame = cs.size ();
-
-    cs.push_back (tmp);
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.set_scope_and_context (tmp.m_scope, tmp.m_context);
+    m_curr_frame = find_caller_frame ();
   }
 
-  std::list<call_stack::stack_frame>
+  void call_stack::goto_base_frame (void)
+  {
+    if (m_curr_frame > 0)
+      m_curr_frame = 0;
+  }
+
+  std::list<stack_frame *>
   call_stack::backtrace_frames (size_t nskip,
                                 octave_idx_type& curr_user_frame) const
   {
-    std::list<call_stack::stack_frame> retval;
+    stack_trace_generator stack_tracer (nskip);
+
+    // Start at the end of the stack, even if the current pointer is
+    // somewhere else.
+
+    size_t n = m_cs.size () - 1;
 
-    size_t user_code_frames = num_user_code_frames (curr_user_frame);
+    m_cs[n]->accept (stack_tracer);
 
-    size_t nframes = (nskip <= user_code_frames ? user_code_frames - nskip : 0);
+    std::list<stack_frame *> frame_list = stack_tracer.frames ();
+
+    if (frame_list.empty ())
+      return frame_list;
 
-    // Our list is reversed.
-    curr_user_frame = nframes - curr_user_frame - 1;
+    // Find the index into the list of frames where we are currently.
+    // We'll just search the list of frames for the one that matches
+    // where we are now.
+
+    stack_frame *frame = m_cs[m_curr_frame];
+
+    octave_function *fcn = frame->function ();
 
-    if (nframes > 0)
+    if (! (fcn && fcn->is_user_code ()))
+      frame = frame->static_link ();
+
+    curr_user_frame = 0;
+    bool found = false;
+    for (const auto *frm : frame_list)
       {
-        for (auto p = cs.crbegin (); p != cs.crend (); p++)
+        if (frm == frame)
           {
-            const stack_frame& elt = *p;
-
-            octave_function *f = elt.m_fcn;
+            found = true;
+            break;
+          }
 
-            if (f && f->is_user_code ())
-              {
-                if (nskip > 0)
-                  nskip--;
-                else
-                  retval.push_back (elt);
-              }
-          }
+        curr_user_frame++;
       }
 
-    return retval;
+    if (! found)
+      curr_user_frame = -1;
+
+    return frame_list;
   }
 
-  octave_map
-  call_stack::backtrace (size_t nskip, octave_idx_type& curr_user_frame,
-                         bool print_subfn) const
+  octave_map call_stack::backtrace (size_t nskip,
+                                    octave_idx_type& curr_user_frame,
+                                    bool print_subfn) const
   {
-    std::list<call_stack::stack_frame> frames
-      = backtrace_frames (nskip, curr_user_frame);
+    std::list<stack_frame *> frames = backtrace_frames (nskip, curr_user_frame);
 
     size_t nframes = frames.size ();
 
@@ -650,17 +639,15 @@
     Cell& name = retval.contents (1);
     Cell& line = retval.contents (2);
     Cell& column = retval.contents (3);
-    Cell& context = retval.contents (5);
 
     octave_idx_type k = 0;
 
-    for (const auto& frm : frames)
+    for (const auto *frm : frames)
       {
-        context(k) = frm.m_context;
-        file(k)    = frm.fcn_file_name ();
-        name(k)    = frm.fcn_name (print_subfn);
-        line(k)    = frm.m_line;
-        column(k)  = frm.m_column;
+        file(k) = frm->fcn_file_name ();
+        name(k) = frm->fcn_name (print_subfn);
+        line(k) = frm->line ();
+        column(k) = frm->column ();
 
         k++;
       }
@@ -668,64 +655,346 @@
     return retval;
   }
 
-  octave_map
-  call_stack::backtrace (size_t nskip)
+  octave_map call_stack::backtrace (size_t nskip)
   {
     octave_idx_type curr_user_frame = -1;
 
     return backtrace (nskip, curr_user_frame, true);
   }
 
-  octave_map
-  call_stack::empty_backtrace (void) const
+  octave_map call_stack::empty_backtrace (void) const
   {
     return octave_map (dim_vector (0, 1), bt_fields);
   }
 
-  void
-  call_stack::pop (void)
+  void call_stack::pop (void)
   {
-    if (cs.size () > 1)
+    // Never pop top scope.
+    // FIXME: is it possible for this case to happen?
+
+    if (m_cs.size () > 0)
       {
-        const stack_frame& elt = cs.back ();
-        curr_frame = elt.m_prev;
-        cs.pop_back ();
-        const stack_frame& new_elt = cs[curr_frame];
+        stack_frame *elt = m_cs.back ();
+
+        m_curr_frame = elt->previous ();
 
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
+        m_cs.pop_back ();
 
-        symtab.set_scope_and_context (new_elt.m_scope, new_elt.m_context);
+        delete elt;
       }
   }
 
-  symbol_info_list
-  call_stack::glob_symbol_info (const std::string& pat) const
+  void call_stack::clear (void)
+  {
+    while (! m_cs.empty ())
+      pop ();
+  }
+
+  symbol_info_list call_stack::all_variables (void)
+  {
+    return m_cs[m_curr_frame]->all_variables ();
+  }
+
+  std::list<symbol_record> call_stack::glob (const std::string& pattern) const
+  {
+    return m_cs[m_curr_frame]->glob (pattern);
+  }
+
+  std::list<symbol_record> call_stack::regexp (const std::string& pattern) const
+  {
+    return m_cs[m_curr_frame]->regexp (pattern);
+  }
+
+  std::list<std::string> call_stack::variable_names (void) const
+  {
+    return m_cs[m_curr_frame]->variable_names ();
+  }
+
+  std::list<std::string> call_stack::global_variable_names (void) const
   {
-    return cs[curr_frame].glob_symbol_info (pat);
+    std::list<std::string> retval;
+
+    for (const auto& nm_ov : m_global_values)
+      {
+        if (nm_ov.second.is_defined ())
+          retval.push_back (nm_ov.first);
+      }
+
+    retval.sort ();
+
+    return retval;
+  }
+
+  void call_stack::clear_global_variable (const std::string& name)
+  {
+    auto p = m_global_values.find (name);
+
+    if (p != m_global_values.end ())
+      p->second = octave_value ();
+  }
+
+  void call_stack::clear_global_variable_pattern (const std::string& pattern)
+  {
+    glob_match pat (pattern);
+
+    for (auto& nm_ov : m_global_values)
+      {
+        if (pat.match (nm_ov.first))
+          nm_ov.second = octave_value ();
+      }
+  }
+
+  void call_stack::clear_global_variable_regexp (const std::string& pattern)
+  {
+    octave::regexp pat (pattern);
+
+    for (auto& nm_ov : m_global_values)
+      {
+        if (pat.is_match (nm_ov.first))
+          nm_ov.second = octave_value ();
+      }
+  }
+
+  void call_stack::clear_global_variables (void)
+  {
+    for (auto& nm_ov : m_global_values)
+      nm_ov.second = octave_value ();
   }
 
   symbol_info_list
-  call_stack::regexp_symbol_info (const std::string& pat) const
+  call_stack::glob_symbol_info (const std::string& pattern) const
   {
-    return cs[curr_frame].glob_symbol_info (pat);
+    return m_cs[m_curr_frame]->glob_symbol_info (pattern);
   }
 
-  symbol_info_list call_stack::get_symbol_info (void) const
+  symbol_info_list
+  call_stack::regexp_symbol_info (const std::string& pattern) const
   {
-    return cs[curr_frame].get_symbol_info ();
+    return m_cs[m_curr_frame]->glob_symbol_info (pattern);
+  }
+
+  symbol_info_list call_stack::get_symbol_info (void)
+  {
+    return m_cs[m_curr_frame]->get_symbol_info ();
   }
 
   symbol_info_list call_stack::top_scope_symbol_info (void) const
   {
-    return cs[0].get_symbol_info ();
+    return m_cs[0]->get_symbol_info ();
   }
 
-  octave_value
-  call_stack::max_stack_depth (const octave_value_list& args, int nargout)
+  octave_value call_stack::max_stack_depth (const octave_value_list& args,
+                                            int nargout)
   {
     return set_internal_variable (m_max_stack_depth, args, nargout,
                                   "max_stack_depth", 0);
   }
+
+  void call_stack::make_persistent (const symbol_record& sym)
+  {
+    m_cs[m_curr_frame]->make_persistent (sym);
+  }
+
+  void call_stack::make_global (const symbol_record& sym)
+  {
+    m_cs[m_curr_frame]->make_global (sym);
+  }
+
+  octave_value call_stack::global_varval (const std::string& name) const
+  {
+    auto p = m_global_values.find (name);
+
+    return p == m_global_values.end () ? octave_value () : p->second;
+  }
+
+  octave_value& call_stack::global_varref (const std::string& name)
+  {
+    return m_global_values[name];
+  }
+
+  octave_value call_stack::get_top_level_value (const std::string& name) const
+  {
+    return m_cs[0]->varval (name);
+  }
+
+  void call_stack::set_top_level_value (const std::string& name,
+                                        const octave_value& value)
+  {
+    m_cs[0]->assign (name, value);
+  }
+
+  octave_value call_stack::do_who_two (const string_vector& patterns,
+                                       bool have_regexp, bool return_list,
+                                       bool verbose, const std::string& msg)
+  {
+    symbol_info_accumulator sym_inf_accum (patterns, have_regexp);
+
+    m_cs[m_curr_frame]->accept (sym_inf_accum);
+
+    if (return_list)
+      {
+        if (verbose)
+          return sym_inf_accum.map_value ();
+        else
+          return Cell (string_vector (sym_inf_accum.names ()));
+      }
+    else
+      {
+        if (msg.empty ())
+          octave_stdout << "Variables visible from the current scope:\n";
+        else
+          octave_stdout << msg;
+
+        if (verbose)
+          sym_inf_accum.display (octave_stdout,
+                                 m_evaluator.whos_line_format ());
+        else
+          {
+            octave_stdout << "\n";
+            string_vector names (sym_inf_accum.names ());
+            names.list_in_columns (octave_stdout);
+          }
+
+        octave_stdout << "\n";
+      }
+
+    return octave_value ();
+  }
+
+  octave_value call_stack::do_global_who_two (const string_vector& patterns,
+                                              bool have_regexp,
+                                              bool return_list, bool verbose,
+                                              const std::string& msg)
+  {
+    octave::symbol_info_list symbol_stats;
+    std::list<std::string> symbol_names;
+
+    octave_idx_type npatterns = patterns.numel ();
+
+    for (octave_idx_type j = 0; j < npatterns; j++)
+      {
+        std::string pattern = patterns[j];
+
+        std::list<std::string> tmp;
+
+        if (have_regexp)
+          {
+            octave::regexp pat (pattern);
+
+            for (auto& nm_ov : m_global_values)
+              {
+                if (pat.is_match (nm_ov.first))
+                  tmp.push_back (nm_ov.first);
+              }
+          }
+        else
+          {
+            glob_match pat (pattern);
+
+            for (auto& nm_ov : m_global_values)
+              {
+                if (pat.match (nm_ov.first))
+                  tmp.push_back (nm_ov.first);
+              }
+          }
+
+        for (const auto& nm : tmp)
+          {
+            octave_value value = m_global_values[nm];
+
+            if (value.is_defined ())
+              {
+                if (verbose)
+                  {
+                    bool is_formal = false;
+                    bool is_global = true;
+                    bool is_persistent = false;
+
+                    octave::symbol_info
+                      syminf (nm, value, is_formal,
+                              is_global, is_persistent);
+
+                    symbol_stats.append (syminf);
+                  }
+                else
+                  symbol_names.push_back (nm);
+              }
+          }
+      }
+
+    if (return_list)
+      {
+        if (verbose)
+          {
+            std::string caller_function_name;
+            octave_function *caller_function = caller ();
+            if (caller_function)
+              caller_function_name = caller_function->name ();
+
+            return symbol_stats.map_value (caller_function_name, 1);
+          }
+        else
+          return Cell (string_vector (symbol_names));
+      }
+    else if (! (symbol_stats.empty () && symbol_names.empty ()))
+      {
+        if (msg.empty ())
+          octave_stdout << "Global variables:\n\n";
+        else
+          octave_stdout << msg;
+
+        if (verbose)
+          symbol_stats.display (octave_stdout,
+                                m_evaluator.whos_line_format ());
+        else
+          {
+            string_vector names (symbol_names);
+
+            names.list_in_columns (octave_stdout);
+          }
+
+        octave_stdout << "\n";
+      }
+
+    return octave_value ();
+  }
+
+  void call_stack::clear_current_frame_values (void)
+  {
+    m_cs[m_curr_frame]->clear_values ();
+  }
+
+  void call_stack::display (void) const
+  {
+    std::ostream& os = octave_stdout;
+
+    size_t nframes = size ();
+
+    for (size_t i = 0; i < nframes; i++)
+      {
+        m_cs[i]->display (false);
+        if (i < nframes - 1)
+          os << std::endl;
+      }
+  }
+
+  void display_call_stack (void)
+  {
+    call_stack& cs = __get_call_stack__ ("display_call_stack");
+
+    cs.display ();
+  }
+
+  void call_stack::set_auto_fcn_var (stack_frame::auto_var_type avt,
+                                     const octave_value& val)
+  {
+    m_cs[m_curr_frame]->set_auto_fcn_var (avt, val);
+  }
+
+  octave_value call_stack::get_auto_fcn_var (stack_frame::auto_var_type avt) const
+  {
+    return m_cs[m_curr_frame]->get_auto_fcn_var (avt);
+  }
 }
 
 DEFMETHOD (max_stack_depth, interp, args, nargout,
@@ -764,106 +1033,11 @@
 */
 
 static octave_value
-do_who_two (octave::interpreter& interp, const string_vector& pats,
-            bool global_only, bool have_regexp, bool return_list,
-            bool verbose = false, std::string msg = "")
-{
-  octave::symbol_info_list symbol_stats;
-  std::list<std::string> symbol_names;
-
-  octave::tree_evaluator& tw = interp.get_evaluator ();
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
-  octave::symbol_scope scope = symtab.current_scope ();
-
-  octave::symbol_record::context_id context = scope.current_context ();
-
-  octave_idx_type npats = pats.numel ();
-
-  for (octave_idx_type j = 0; j < npats; j++)
-    {
-      std::string pat = pats[j];
-
-      std::list<octave::symbol_record> tmp
-        = (have_regexp
-           ? (global_only
-              ? symtab.regexp_global_variables (pat)
-              : symtab.regexp_variables (pat))
-           : (global_only
-              ? symtab.glob_global_variables (pat)
-              : symtab.glob_variables (pat)));
-
-      for (const auto& sr : tmp)
-        {
-          octave_value value = sr.varval (context);
-
-          if (value.is_defined ())
-            {
-              if (verbose)
-                {
-                  octave::symbol_info
-                    syminf (sr.name (), value,
-                            sr.is_formal (),
-                            sr.is_global (), sr.is_persistent ());
-
-                  symbol_stats.append (syminf);
-                }
-              else
-                symbol_names.push_back (sr.name ());
-            }
-        }
-    }
-
-  if (return_list)
-    {
-      if (verbose)
-        {
-          octave::call_stack& cs = interp.get_call_stack ();
-
-          std::string caller_function_name;
-          octave_function *caller = cs.caller ();
-          if (caller)
-            caller_function_name = caller->name ();
-
-          return symbol_stats.map_value (caller_function_name, 1);
-        }
-      else
-        return Cell (string_vector (symbol_names));
-    }
-  else if (! (symbol_stats.empty () && symbol_names.empty ()))
-    {
-      if (msg.empty ())
-        if (global_only)
-          octave_stdout << "Global variables:\n\n";
-        else
-          octave_stdout << "Variables in the current scope:\n\n";
-      else
-        octave_stdout << msg;
-
-      if (verbose)
-        symbol_stats.display (octave_stdout, tw.whos_line_format ());
-      else
-        {
-          string_vector names (symbol_names);
-
-          names.list_in_columns (octave_stdout);
-        }
-
-      octave_stdout << "\n";
-    }
-
-  return octave_value ();
-}
-
-static octave_value
 do_who (octave::interpreter& interp, int argc, const string_vector& argv,
         bool return_list, bool verbose = false)
 {
   octave_value retval;
 
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-  octave::call_stack& cs = interp.get_call_stack ();
-
   std::string my_name = argv[0];
 
   std::string file_name;
@@ -904,20 +1078,22 @@
       i++;
     }
 
-  int npats = argc - i;
-  string_vector pats;
-  if (npats > 0)
+  int npatterns = argc - i;
+  string_vector patterns;
+  if (npatterns > 0)
     {
-      pats.resize (npats);
-      for (int j = 0; j < npats; j++)
-        pats[j] = argv[i+j];
+      patterns.resize (npatterns);
+      for (int j = 0; j < npatterns; j++)
+        patterns[j] = argv[i+j];
     }
   else
     {
-      pats.resize (1);
-      pats[0] = "*";
+      patterns.resize (1);
+      patterns[0] = "*";
     }
 
+  octave::call_stack& cs = interp.get_call_stack ();
+
   if (from_file)
     {
       // FIXME: This is an inefficient manner to implement this as the
@@ -933,21 +1109,28 @@
 
       octave::symbol_scope tmp_scope ("$dummy_scope$");
 
-      symtab.set_scope (tmp_scope);
-
-      cs.push (tmp_scope, 0);
+      cs.push (tmp_scope);
       frame.add_method (cs, &octave::call_stack::pop);
 
       octave::feval ("load", octave_value (file_name), 0);
 
       std::string newmsg = "Variables in the file " + file_name + ":\n\n";
 
-      return do_who_two (interp, pats, global_only, have_regexp,
-                         return_list, verbose, newmsg);
+      if (global_only)
+        return cs.do_global_who_two (patterns, have_regexp, return_list,
+                                     verbose, newmsg);
+      else
+        return cs.do_who_two (patterns, have_regexp, return_list,
+                              verbose, newmsg);
     }
   else
-    return do_who_two (interp, pats, global_only, have_regexp,
-                       return_list, verbose);
+    {
+      if (global_only)
+        return cs.do_global_who_two (patterns, have_regexp, return_list,
+                                     verbose);
+      else
+        return cs.do_who_two (patterns, have_regexp, return_list, verbose);
+    }
 }
 
 DEFMETHOD (who, interp, args, nargout,
@@ -1032,10 +1215,6 @@
 @item blank
 Variable in local scope
 
-@item @code{a}
-Automatic variable.  An automatic variable is one created by the
-interpreter, for example @code{argn}.
-
 @item @code{c}
 Variable of complex type.
 
--- a/libinterp/corefcn/call-stack.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/call-stack.h	Mon Jan 28 18:01:46 2019 +0000
@@ -35,11 +35,12 @@
 class octave_value;
 class octave_value_list;
 
+#include "stack-frame.h"
 #include "symscope.h"
 
 namespace octave
 {
-  class interpreter;
+  class tree_evaluator;
   class symbol_info_list;
   class unwind_protect;
 
@@ -49,76 +50,25 @@
   {
   public:
 
-    class stack_frame
-    {
-    public:
-
-      friend class call_stack;
+    typedef std::deque<stack_frame *> stack_frames;
 
-      stack_frame (octave_function *fcn = nullptr,
-                   unwind_protect *up_frame = nullptr,
-                   const symbol_scope& scope = symbol_scope (),
-                   symbol_record::context_id context = 0, size_t prev = 0)
-        : m_fcn (fcn), m_unwind_protect_frame (up_frame),
-          m_line (-1), m_column (-1), m_scope (scope),
-          m_context (context), m_prev (prev)
-      { }
-
-      stack_frame (const stack_frame& elt)
-        : m_fcn (elt.m_fcn),
-          m_unwind_protect_frame (elt.m_unwind_protect_frame),
-          m_line (elt.m_line), m_column (elt.m_column),
-          m_scope (elt.m_scope), m_context (elt.m_context),
-          m_prev (elt.m_prev)
-      { }
-
-      int line (void) const { return m_line; }
-
-      int column (void) const { return m_column; }
-
-      std::string fcn_file_name (void) const;
+    typedef stack_frames::iterator iterator;
+    typedef stack_frames::const_iterator const_iterator;
 
-      std::string fcn_name (bool print_subfn = true) const;
-
-      bool operator == (const stack_frame& rhs) const;
-
-      symbol_info_list
-      make_symbol_info_list (const std::list<symbol_record>& srl) const;
-
-      symbol_info_list glob_symbol_info (const std::string& pat) const;
-
-      symbol_info_list regexp_symbol_info (const std::string& pat) const;
-
-      symbol_info_list get_symbol_info (void) const;
-
-    private:
+    typedef stack_frames::reverse_iterator reverse_iterator;
+    typedef stack_frames::const_reverse_iterator const_reverse_iterator;
 
-      octave_function *m_fcn;
-      unwind_protect *m_unwind_protect_frame;
-      int m_line;
-      int m_column;
-      symbol_scope m_scope;
-      symbol_record::context_id m_context;
-      size_t m_prev;
-    };
-
-    typedef std::deque<stack_frame>::iterator iterator;
-    typedef std::deque<stack_frame>::const_iterator const_iterator;
-
-    typedef std::deque<stack_frame>::reverse_iterator reverse_iterator;
-    typedef std::deque<stack_frame>::const_reverse_iterator const_reverse_iterator;
-
-    call_stack (interpreter& interp);
+    call_stack (tree_evaluator& evaluator);
 
     // Current function (top of stack).
     octave_function * current (void) const
     {
       octave_function *retval = nullptr;
 
-      if (! cs.empty ())
+      if (! m_cs.empty ())
         {
-          const stack_frame& elt = cs[curr_frame];
-          retval = elt.m_fcn;
+          const stack_frame *elt = m_cs[m_curr_frame];
+          retval = elt->function ();
         }
 
       return retval;
@@ -134,25 +84,39 @@
 
     octave_function * caller (void) const
     {
-      return curr_frame > 1 ? cs[curr_frame-1].m_fcn : cs[0].m_fcn;
+      return (m_curr_frame > 1
+              ? m_cs[m_curr_frame-1]->function () : m_cs[0]->function ());
     }
 
-    size_t current_frame (void) const { return curr_frame; }
+    size_t current_frame (void) const { return m_curr_frame; }
+
+    size_t size (void) const { return m_cs.size (); }
+
+    const stack_frame& get_current_stack_frame (void) const
+    {
+      return *(m_cs[m_curr_frame]);
+    }
 
-    size_t size (void) const { return cs.size (); }
+    stack_frame& get_current_stack_frame (void)
+    {
+      return *(m_cs[m_curr_frame]);
+    }
 
-    size_t num_user_code_frames (octave_idx_type& curr_user_frame) const;
+    symbol_scope top_scope (void) const
+    {
+      return m_cs[0]->get_scope ();
+    }
 
     symbol_scope current_scope (void) const
     {
-      return (curr_frame > 0 && curr_frame < cs.size ()
-              ? cs[curr_frame].m_scope : symbol_scope ());
+      // FIXME: Can m_curr_frame ever be invalid?
+      return (m_curr_frame < m_cs.size ()
+              ? m_cs[m_curr_frame]->get_scope () : symbol_scope ());
     }
 
-    symbol_record::context_id current_context (void) const
+    bool at_top_level (void) const
     {
-      return (curr_frame > 0 && curr_frame < cs.size ()
-              ? cs[curr_frame].m_context : 0);
+      return current_scope () == top_scope ();
     }
 
     // Function at location N on the call stack (N == 0 is current), may
@@ -161,10 +125,10 @@
     {
       octave_function *retval = nullptr;
 
-      if (cs.size () > n)
+      if (m_cs.size () > n)
         {
-          stack_frame& elt = cs[n];
-          retval = elt.m_fcn;
+          stack_frame *elt = m_cs[n];
+          retval = elt->function ();
         }
 
       return retval;
@@ -173,7 +137,7 @@
     // User code caller.
     octave_user_code * caller_user_code (size_t nskip = 0) const;
 
-    unwind_protect *curr_fcn_unwind_protect_frame (void) const;
+    unwind_protect * curr_fcn_unwind_protect_frame (void) const;
 
     // Line in user code caller.
     int caller_user_code_line (void) const;
@@ -193,45 +157,44 @@
     // Return TRUE if all elements on the call stack are scripts.
     bool all_scripts (void) const;
 
-    void push (octave_function *fcn = nullptr,
-               unwind_protect *up_frame = nullptr);
+    stack_frame * get_static_link (size_t prev_frame) const;
+
+    void push (const symbol_scope& scope);
 
-    void push (octave_function *fcn, unwind_protect *up_frame,
-               const symbol_scope& scope, symbol_record::context_id context);
+    void push (octave_user_function *fcn, unwind_protect *up_frame);
 
-    void push (const symbol_scope& scope, symbol_record::context_id context)
-    {
-      push (nullptr, nullptr, scope, context);
-    }
+    void push (octave_user_script *script, unwind_protect *up_frame);
+
+    void push (octave_function *fcn);
 
     void set_location (int l, int c)
     {
-      if (! cs.empty ())
+      if (! m_cs.empty ())
         {
-          stack_frame& elt = cs.back ();
+          stack_frame *elt = m_cs.back ();
 
-          elt.m_line = l;
-          elt.m_column = c;
+          elt->line (l);
+          elt->column (c);
         }
     }
 
     void set_line (int l)
     {
-      if (! cs.empty ())
+      if (! m_cs.empty ())
         {
-          stack_frame& elt = cs.back ();
+          stack_frame *elt = m_cs.back ();
 
-          elt.m_line = l;
+          elt->line (l);
         }
     }
 
     void set_column (int c)
     {
-      if (! cs.empty ())
+      if (! m_cs.empty ())
         {
-          stack_frame& elt = cs.back ();
+          stack_frame *elt = m_cs.back ();
 
-          elt.m_column = c;
+          elt->column (c);
         }
     }
 
@@ -244,15 +207,16 @@
 
     bool goto_frame_relative (int n, bool verbose = false);
 
+    size_t find_caller_frame (void);
+
     void goto_caller_frame (void);
 
     void goto_base_frame (void);
 
-    std::list<call_stack::stack_frame>
+    std::list<stack_frame *>
     backtrace_frames (size_t nskip, octave_idx_type& curr_user_frame) const;
 
-    std::list<call_stack::stack_frame>
-    backtrace_frames (size_t nskip = 0) const
+    std::list<stack_frame *> backtrace_frames (size_t nskip = 0) const
     {
       octave_idx_type curr_user_frame = -1;
 
@@ -268,28 +232,85 @@
 
     void pop (void);
 
-    void clear (void) { cs.clear (); }
+    void clear (void);
+
+    symbol_info_list all_variables (void);
+
+    std::list<symbol_record> glob (const std::string& pattern) const;
 
-    symbol_info_list glob_symbol_info (const std::string& pat) const;
+    std::list<symbol_record> regexp (const std::string& pattern) const;
+
+    std::list<std::string> variable_names (void) const;
+
+    std::list<std::string> global_variable_names (void) const;
 
-    symbol_info_list regexp_symbol_info (const std::string& pat) const;
+    void clear_global_variable (const std::string& name);
+
+    void clear_global_variable_pattern (const std::string& pattern);
+
+    void clear_global_variable_regexp(const std::string& pattern);
 
-    symbol_info_list get_symbol_info (void) const;
+    void clear_global_variables (void);
+
+    symbol_info_list glob_symbol_info (const std::string& pattern) const;
+
+    symbol_info_list regexp_symbol_info (const std::string& pattern) const;
+
+    symbol_info_list get_symbol_info (void);
 
     symbol_info_list top_scope_symbol_info (void) const;
 
     octave_value max_stack_depth (const octave_value_list& args, int nargout);
 
+    void make_persistent (const symbol_record& sym);
+
+    void make_global (const symbol_record& sym);
+
+    octave_value global_varval (const std::string& name) const;
+
+    octave_value& global_varref (const std::string& name);
+
+    octave_value get_top_level_value (const std::string& name) const;
+
+    void set_top_level_value (const std::string& name,
+                              const octave_value& value);
+
+    octave_value do_who_two (const string_vector& patterns, bool have_regexp,
+                             bool return_list, bool verbose,
+                             const std::string& msg = "");
+
+    octave_value do_global_who_two (const string_vector& patterns,
+                                    bool have_regexp, bool return_list,
+                                    bool verbose, const std::string& msg = "");
+
+    void clear_current_frame_values (void);
+
+    void display (void) const;
+
+    void set_auto_fcn_var (stack_frame::auto_var_type avt,
+                           const octave_value& val);
+
+    octave_value get_auto_fcn_var (stack_frame::auto_var_type avt) const;
+
   private:
 
+    tree_evaluator& m_evaluator;
+
     // The current call stack.
-    std::deque<stack_frame> cs;
+    // FIXME: maybe we should be using a std::shared_ptr to manage the
+    // individual stack frames?
+    stack_frames m_cs;
 
-    size_t curr_frame;
+    // FIXME: Could we eliminate this varaible and manage the current
+    // frame in the evaluator class instead?  The current frame might
+    // always be the top of the stack.  Restoring the previous/current
+    // frame would be managed by other means, such as an
+    // unwind_protect frame.
+    size_t m_curr_frame;
 
     int m_max_stack_depth;
 
-    interpreter& m_interpreter;
+    std::map<std::string, octave_value> m_global_values;
   };
 }
 
--- a/libinterp/corefcn/debug.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/debug.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -943,7 +943,7 @@
                  << std::endl;
             }
 
-          if (show_top_level)
+          if (cs.at_top_level () && show_top_level)
             os << "  --> top level" << std::endl;
         }
     }
@@ -951,7 +951,12 @@
     {
       octave_map stk = cs.backtrace (nskip, curr_frame, false);
 
-      retval = ovl (stk, curr_frame < 0 ? 1 : curr_frame + 1);
+      // If current stack frame is not in the list curr_frame will be
+      // -1 and either nskip caused us to skip it or we are at the top
+      // level, which is not included in the list of frames.  So in the
+      // interpreter, 0 will be our invalid frame index value.
+
+      retval = ovl (stk, curr_frame + 1);
     }
 
   return retval;
--- a/libinterp/corefcn/error.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/error.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -221,11 +221,7 @@
       octave_user_code *fcn = cs.caller_user_code ();
 
       if (fcn)
-        {
-          octave_idx_type curr_frame = -1;
-
-          Vlast_error_stack = cs.backtrace (0, curr_frame);
-        }
+        Vlast_error_stack = cs.backtrace ();
       else
         Vlast_error_stack = initialize_last_error_stack ();
     }
@@ -315,21 +311,20 @@
 {
   octave::call_stack& cs = octave::__get_call_stack__ ("pr_where");
 
-  std::list<octave::call_stack::stack_frame> call_stack_frames
-    = cs.backtrace_frames ();
+  std::list<octave::stack_frame *> call_stack_frames = cs.backtrace_frames ();
 
   // Print the error message only if it is different from the previous one;
   // Makes the output more concise and readable.
   call_stack_frames.unique ();
 
   std::list<error_stack_frame> frames;
-  for (const auto& frm : call_stack_frames)
+  for (const auto *frm : call_stack_frames)
     {
       error_stack_frame frame;
 
-      frame.name = frm.fcn_name ();
-      frame.line = frm.line ();
-      frame.column = frm.column ();
+      frame.name = frm->fcn_name ();
+      frame.line = frm->line ();
+      frame.column = frm->column ();
 
       frames.push_back (frame);
     }
@@ -1559,22 +1554,18 @@
           else
             old_warning_options = octave_map (warning_query (arg2));
 
-          octave::symbol_table& symtab = interp.get_symbol_table ();
-
-          if (nargin == 3 && argv[3] == "local"
-              && ! symtab.at_top_level ())
+          if (nargin == 3 && argv[3] == "local" && ! interp.at_top_level ())
             {
-              octave::symbol_scope scope
-                = symtab.require_current_scope ("warning");
-
               octave_scalar_map val = warning_query (arg2);
 
               octave_value curr_state = val.contents ("state");
 
               // FIXME: this might be better with a dictionary object.
 
+              octave::tree_evaluator& tw = interp.get_evaluator ();
+
               octave_value curr_warning_states
-                = scope.varval (".saved_warning_states.");
+                = tw.get_auto_fcn_var (octave::stack_frame::SAVED_WARNING_STATES);
 
               octave_map m;
 
@@ -1622,7 +1613,7 @@
               m.contents ("identifier") = ids;
               m.contents ("state") = states;
 
-              scope.force_assign (".saved_warning_states.", m);
+              tw.set_auto_fcn_var (octave::stack_frame::SAVED_WARNING_STATES, m);
 
               // Now ignore the "local" argument and continue to
               // handle the current setting.
@@ -2032,12 +2023,9 @@
             }
           else
             {
-              // No stack field.  Fill it in with backtrace info.
-              octave_idx_type curr_frame = -1;
-
               octave::call_stack& cs = interp.get_call_stack ();
 
-              Vlast_error_stack = cs.backtrace (0, curr_frame);
+              Vlast_error_stack = cs.backtrace ();
             }
         }
       else
--- a/libinterp/corefcn/fcn-info.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/fcn-info.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -384,6 +384,7 @@
         // they were defined within class methods and use local functions
         // (helper functions) we can still use those anonymous functions
 
+#if 0
         if (current_fcn->is_anonymous_function ())
           {
             if (fcn_file.empty ()
@@ -392,7 +393,7 @@
               fcn_file
                 = curr_scope.parent_scope ()->function ()->fcn_file_name();
           }
-
+#endif
         if (! fcn_file.empty ())
           {
             auto r = local_functions.find (fcn_file);
--- a/libinterp/corefcn/help.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/help.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -319,7 +319,7 @@
     const string_vector cfl = symtab.cmdline_function_names ();
     const int cfl_len = cfl.numel ();
 
-    const string_vector lcl = symtab.variable_names ();
+    const string_vector lcl = m_interpreter.variable_names ();
     const int lcl_len = lcl.numel ();
 
     load_path& lp = m_interpreter.get_load_path ();
--- a/libinterp/corefcn/input.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/input.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -150,23 +150,6 @@
     return std::string ();
 }
 
-static inline bool
-is_variable (octave::symbol_table& symtab, const std::string& name)
-{
-  bool retval = false;
-
-  if (! name.empty ())
-    {
-      octave::symbol_scope scope = symtab.current_scope ();
-
-      octave_value val = scope ? scope.varval (name) : octave_value ();
-
-      retval = val.is_defined ();
-    }
-
-  return retval;
-}
-
 static string_vector
 generate_struct_completions (const std::string& text,
                              std::string& prefix, std::string& hint)
@@ -201,9 +184,7 @@
       octave::interpreter& interp
         = octave::__get_interpreter__ ("generate_struct_completions");
 
-      octave::symbol_table& symtab = interp.get_symbol_table ();
-
-      if (is_variable (symtab, base_name))
+      if (interp.is_variable (base_name))
         {
           int parse_status;
 
@@ -1261,8 +1242,8 @@
   frame.add_method (cs, &octave::call_stack::restore_frame,
                     cs.current_frame ());
 
-  // Skip the frame assigned to the keyboard function.
-  cs.goto_frame_relative (0);
+  // Go up to the nearest user code frame.
+  cs.goto_frame_relative (-1);
 
   octave::tree_evaluator& tw = interp.get_evaluator ();
 
--- a/libinterp/corefcn/interpreter.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/interpreter.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -367,7 +367,7 @@
       m_load_path (),
       m_load_save_system (*this),
       m_type_info (),
-      m_symbol_table (),
+      m_symbol_table (*this),
       m_evaluator (*this),
       m_stream_list (*this),
       m_child_list (),
@@ -536,14 +536,7 @@
 
   void interpreter::intern_nargin (octave_idx_type nargs)
   {
-    // FIXME: should this explicitly be top_scope?
-    symbol_scope scope = m_symbol_table.current_scope ();
-
-    if (scope)
-      {
-        scope.assign (".nargin.", nargs);
-        scope.mark_hidden (".nargin.");
-      }
+    m_evaluator.set_auto_fcn_var (stack_frame::NARGIN, nargs);
   }
 
   // Read the history file unless a command-line option inhibits that.
@@ -1062,13 +1055,19 @@
   }
 
   symbol_scope
-  interpreter::get_current_scope (void)
+  interpreter::get_top_scope (void) const
   {
-    return m_symbol_table.current_scope ();
+    return m_evaluator.get_top_scope ();
   }
 
   symbol_scope
-  interpreter::require_current_scope (const std::string& who)
+  interpreter::get_current_scope (void) const
+  {
+    return m_evaluator.get_current_scope ();
+  }
+
+  symbol_scope
+  interpreter::require_current_scope (const std::string& who) const
   {
     symbol_scope scope = get_current_scope ();
 
@@ -1150,6 +1149,170 @@
     return m_evaluator.eval_string (arg, silent, parse_status, nargout);
   }
 
+  void interpreter::install_variable (const std::string& name,
+                                      const octave_value& value, bool global)
+  {
+    m_evaluator.install_variable (name, value, global);
+  }
+
+  octave_value interpreter::global_varval (const std::string& name) const
+  {
+    return m_evaluator.global_varval (name);
+  }
+
+  void interpreter::global_assign (const std::string& name,
+                                   const octave_value& val)
+  {
+    m_evaluator.global_assign (name, val);
+  }
+
+  octave_value interpreter::top_level_varval (const std::string& name) const
+  {
+    return m_evaluator.top_level_varval (name);
+  }
+
+  void interpreter::top_level_assign (const std::string& name,
+                                      const octave_value& val)
+  {
+    m_evaluator.top_level_assign (name, val);
+  }
+
+  bool interpreter::is_variable (const std::string& name) const
+  {
+    return m_evaluator.is_variable (name);
+  }
+
+  bool interpreter::is_local_variable (const std::string& name) const
+  {
+    return m_evaluator.is_local_variable (name);
+  }
+
+  octave_value interpreter::varval (const std::string& name) const
+  {
+    return m_evaluator.varval (name);
+  }
+
+  void interpreter::assign (const std::string& name,
+                            const octave_value& val)
+  {
+    m_evaluator.assign (name, val);
+  }
+
+  bool interpreter::at_top_level (void) const
+  {
+    return m_evaluator.at_top_level ();
+  }
+
+  bool interpreter::isglobal (const std::string& name) const
+  {
+    return m_evaluator.is_global (name);
+  }
+
+  octave_value interpreter::find (const std::string& name)
+  {
+    return m_evaluator.find (name);
+  }
+
+  void interpreter::clear_all (bool force)
+  {
+    m_evaluator.clear_all (force);
+  }
+
+  void interpreter::clear_objects (void)
+  {
+    m_evaluator.clear_objects ();
+  }
+
+  void interpreter::clear_variable (const std::string& name)
+  {
+    m_evaluator.clear_variable (name);
+  }
+
+  void interpreter::clear_variable_pattern (const std::string& pattern)
+  {
+    m_evaluator.clear_variable_pattern (pattern);
+  }
+
+  void interpreter::clear_variable_regexp (const std::string& pattern)
+  {
+    m_evaluator.clear_variable_regexp (pattern);
+  }
+
+  void interpreter::clear_variables (void)
+  {
+    m_evaluator.clear_variables ();
+  }
+
+  void interpreter::clear_global_variable (const std::string& name)
+  {
+    m_evaluator.clear_global_variable (name);
+  }
+
+  void interpreter::clear_global_variable_pattern (const std::string& pattern)
+  {
+    m_evaluator.clear_global_variable_pattern (pattern);
+  }
+
+  void interpreter::clear_global_variable_regexp (const std::string& pattern)
+  {
+    m_evaluator.clear_global_variable_regexp (pattern);
+  }
+
+  void interpreter::clear_global_variables (void)
+  {
+    m_evaluator.clear_global_variables ();
+  }
+
+  void interpreter::clear_functions (bool force)
+  {
+    m_symbol_table.clear_functions (force);
+  }
+
+  void interpreter::clear_function (const std::string& name)
+  {
+    m_symbol_table.clear_function (name);
+  }
+
+  void interpreter::clear_symbol (const std::string& name)
+  {
+    m_evaluator.clear_symbol (name);
+  }
+
+  void interpreter::clear_function_pattern (const std::string& pat)
+  {
+    m_symbol_table.clear_function_pattern (pat);
+  }
+
+  void interpreter::clear_function_regexp (const std::string& pat)
+  {
+    m_symbol_table.clear_function_regexp (pat);
+  }
+
+  void interpreter::clear_symbol_pattern (const std::string& pat)
+  {
+    return m_evaluator.clear_symbol_pattern (pat);
+  }
+
+  void interpreter::clear_symbol_regexp (const std::string& pat)
+  {
+    return m_evaluator.clear_symbol_regexp (pat);
+  }
+
+  std::list<std::string> interpreter::global_variable_names (void)
+  {
+    return m_evaluator.global_variable_names ();
+  }
+
+  std::list<std::string> interpreter::variable_names (void)
+  {
+    return m_evaluator.variable_names ();
+  }
+
+  std::list<std::string> interpreter::user_function_names (void)
+  {
+    return m_symbol_table.user_function_names ();
+  }
+
   void interpreter::recover_from_exception (void)
   {
     can_interrupt = true;
--- a/libinterp/corefcn/interpreter.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/interpreter.h	Mon Jan 28 18:01:46 2019 +0000
@@ -25,6 +25,7 @@
 
 #include "octave-config.h"
 
+#include <map>
 #include <string>
 
 #include "child-list.h"
@@ -194,24 +195,25 @@
       return m_load_save_system;
     }
 
+    type_info& get_type_info (void)
+    {
+      return m_type_info;
+    }
+
     symbol_table& get_symbol_table (void)
     {
       return m_symbol_table;
     }
 
-    type_info& get_type_info (void)
-    {
-      return m_type_info;
-    }
-
-    symbol_scope get_current_scope (void);
-    symbol_scope require_current_scope (const std::string& who);
+    tree_evaluator& get_evaluator (void);
 
     call_stack& get_call_stack (void);
 
-    profiler& get_profiler (void);
+    symbol_scope get_top_scope (void) const;
+    symbol_scope get_current_scope (void) const;
+    symbol_scope require_current_scope (const std::string& who) const;
 
-    tree_evaluator& get_evaluator (void);
+    profiler& get_profiler (void);
 
     stream_list& get_stream_list (void);
 
@@ -247,6 +249,76 @@
     octave_value_list eval_string (const octave_value& arg, bool silent,
                                    int& parse_status, int nargout);
 
+    void install_variable (const std::string& name, const octave_value& value,
+                           bool global);
+
+    void set_global_value (const std::string& name, const octave_value& value);
+
+    octave_value global_varval (const std::string& name) const;
+
+    void global_assign (const std::string& name,
+                        const octave_value& val = octave_value ());
+
+    octave_value top_level_varval (const std::string& name) const;
+
+    void top_level_assign (const std::string& name,
+                           const octave_value& val = octave_value ());
+
+    bool is_variable (const std::string& name) const;
+
+    bool is_local_variable (const std::string& name) const;
+
+    octave_value varval (const std::string& name) const;
+
+    void assign (const std::string& name,
+                 const octave_value& val = octave_value ());
+
+    bool at_top_level (void) const;
+
+    bool isglobal (const std::string& name) const;
+
+    octave_value find (const std::string& name);
+
+    void clear_all (bool force = false);
+
+    void clear_objects (void);
+
+    void clear_variable (const std::string& name);
+
+    void clear_variable_pattern (const std::string& pattern);
+
+    void clear_variable_regexp (const std::string& pattern);
+
+    void clear_variables (void);
+
+    void clear_global_variable (const std::string& name);
+
+    void clear_global_variable_pattern (const std::string& pattern);
+
+    void clear_global_variable_regexp (const std::string& pattern);
+
+    void clear_global_variables (void);
+
+    void clear_functions (bool force = false);
+
+    void clear_function (const std::string& name);
+
+    void clear_symbol (const std::string& name);
+
+    void clear_function_pattern (const std::string& pat);
+
+    void clear_function_regexp (const std::string& pat);
+
+    void clear_symbol_pattern (const std::string& pat);
+
+    void clear_symbol_regexp (const std::string& pat);
+
+    std::list<std::string> global_variable_names (void);
+
+    std::list<std::string> variable_names (void);
+
+    std::list<std::string> user_function_names (void);
+
     static void recover_from_exception (void);
 
     static void add_atexit_function (const std::string& fname);
--- a/libinterp/corefcn/load-save.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/load-save.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -66,7 +66,6 @@
 #include "ov-cell.h"
 #include "pager.h"
 #include "syminfo.h"
-#include "symtab.h"
 #include "sysdep.h"
 #include "unwind-prot.h"
 #include "utils.h"
@@ -685,17 +684,10 @@
 
         std::string struct_name = argv[argv_idx];
 
-        symbol_scope scope = m_interpreter.get_current_scope ();
-
-        octave_value struct_var;
+        if (! m_interpreter.is_variable (struct_name))
+          error ("save: no such variable: '%s'", struct_name.c_str ());
 
-        if (scope)
-          {
-            if (! scope.is_variable (struct_name))
-              error ("save: no such variable: '%s'", struct_name.c_str ());
-
-            struct_var = scope.varval (struct_name);
-          }
+        octave_value struct_var = m_interpreter.varval (struct_name);
 
         if (! struct_var.isstruct () || struct_var.numel () != 1)
           error ("save: '%s' is not a scalar structure", struct_name.c_str ());
@@ -1070,24 +1062,7 @@
                                                   bool global,
                                                   const std::string& /*doc*/)
   {
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symbol_scope scope = symtab.require_current_scope ("load_save_system::install_loaded_variable");
-
-    if (global)
-      {
-        symbol_record sym = scope.find_symbol (name);
-
-        if (! sym.is_global ())
-          {
-            symbol_scope global_scope = symtab.global_scope ();
-            symbol_record global_sym = global_scope.find_symbol (name);
-
-            sym.bind_fwd_rep (global_scope.get_rep (), global_sym);
-          }
-      }
-
-    scope.assign (name, val);
+    m_interpreter.install_variable (name, val, global);
   }
 
   std::string load_save_system::init_save_header_format (void)
--- a/libinterp/corefcn/ls-mat5.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/ls-mat5.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -976,14 +976,11 @@
             // Set up temporary scope to use for evaluating the text
             // that defines the anonymous function.
 
-            octave::symbol_table& symtab = interp.get_symbol_table ();
-
-            octave::symbol_scope local_scope;
-
-            symtab.set_scope (local_scope);
-
             octave::call_stack& cs = interp.get_call_stack ();
-            cs.push (local_scope, 0);
+
+            octave::symbol_scope local_scope ("read_mat5_binary_element$dummy_scope");
+
+            cs.push (local_scope);
             frame.add_method (cs, &octave::call_stack::pop);
 
             if (m2.nfields () > 0)
@@ -995,7 +992,7 @@
                     std::string key = m2.key (p0);
                     octave_value val = m2.contents (p0);
 
-                    local_scope.assign (key, val, 0);
+                    interp.assign (key, val);
                   }
               }
 
--- a/libinterp/corefcn/ls-oct-text.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/ls-oct-text.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -251,8 +251,7 @@
       return "";
     }
 
-  if (! (name == ".nargin." || name == ".nargout."
-         || name == CELL_ELT_TAG || octave::valid_identifier (name)))
+  if (! (name == CELL_ELT_TAG || octave::valid_identifier (name)))
     error ("load: invalid identifier '%s' found in file '%s'",
            name.c_str (), filename.c_str ());
 
--- a/libinterp/corefcn/mex.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/mex.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -3484,13 +3484,10 @@
 
   octave_value val;
 
+  octave::interpreter& interp = octave::__get_interpreter__ ("mexGetVariable");
+
   if (! strcmp (space, "global"))
-    {
-      octave::symbol_table& symtab
-        = octave::__get_symbol_table__ ("mexGetVariable");
-
-      val = symtab.global_varval (name);
-    }
+    val = interp.global_varval (name);
   else
     {
       // FIXME: should this be in variables.cc?
@@ -3507,18 +3504,14 @@
 
           if (base)
             {
-              octave::call_stack& cs
-                = octave::__get_call_stack__ ("mexGetVariable");
+              octave::call_stack& cs = interp.get_call_stack ();
 
               cs.goto_base_frame ();
 
               frame.add_method (cs, &octave::call_stack::pop);
             }
 
-          octave::symbol_scope scope
-            = octave::__require_current_scope__ ("mexGetVariable");
-
-          val = scope.varval (name);
+          val = interp.varval (name);
         }
       else
         mexErrMsgTxt ("mexGetVariable: symbol table does not exist");
@@ -3555,13 +3548,10 @@
   if (! name || name[0] == '\0')
     return 1;
 
+  octave::interpreter& interp = octave::__get_interpreter__ ("mexPutVariable");
+
   if (! strcmp (space, "global"))
-    {
-      octave::symbol_table& symtab
-        = octave::__get_symbol_table__ ("mexPutVariable");
-
-      symtab.global_assign (name, mxArray::as_octave_value (ptr));
-    }
+    interp.global_assign (name, mxArray::as_octave_value (ptr));
   else
     {
       // FIXME: should this be in variables.cc?
@@ -3578,18 +3568,14 @@
 
           if (base)
             {
-              octave::call_stack& cs
-                = octave::__get_call_stack__ ("mexPutVariable");
+              octave::call_stack& cs = interp.get_call_stack ();
 
               cs.goto_base_frame ();
 
               frame.add_method (cs, &octave::call_stack::pop);
             }
 
-          octave::symbol_scope scope
-            = octave::__require_current_scope__ ("mexPutVariable");
-
-          scope.assign (name, mxArray::as_octave_value (ptr));
+          interp.assign (name, mxArray::as_octave_value (ptr));
         }
       else
         mexErrMsgTxt ("mexPutVariable: symbol table does not exist");
--- a/libinterp/corefcn/module.mk	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/module.mk	Mon Jan 28 18:01:46 2019 +0000
@@ -82,7 +82,10 @@
   %reldir%/sighandlers.h \
   %reldir%/sparse-xdiv.h \
   %reldir%/sparse-xpow.h \
+  %reldir%/stack-frame.h \
+  %reldir%/stack-frame-walker.h \
   %reldir%/syminfo.h \
+  %reldir%/syminfo-accumulator.h \
   %reldir%/symrec.h \
   %reldir%/symscope.h \
   %reldir%/symtab.h \
@@ -232,6 +235,7 @@
   %reldir%/sparse.cc \
   %reldir%/spparms.cc \
   %reldir%/sqrtm.cc \
+  %reldir%/stack-frame.cc \
   %reldir%/strfind.cc \
   %reldir%/strfns.cc \
   %reldir%/sub2ind.cc \
--- a/libinterp/corefcn/octave-link.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/octave-link.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -37,7 +37,6 @@
 #include "ovl.h"
 #include "pager.h"
 #include "syminfo.h"
-#include "symtab.h"
 
 static int
 octave_readline_hook (void)
@@ -69,13 +68,10 @@
 {
   if (enabled ())
     {
-      octave::symbol_table& symtab
-         = octave::__get_symbol_table__ ("octave_link::set_workspace");
-
       octave::call_stack& cs
         = octave::__get_call_stack__ ("octave_link::set_workspace");
 
-      instance->do_set_workspace (symtab.at_top_level (),
+      instance->do_set_workspace (cs.at_top_level (),
                                   instance->debugging,
                                   cs.get_symbol_info (), true);
     }
@@ -465,9 +461,7 @@
     warning ("openvar: GUI is not running, can't start Variable Editor");
   else
     {
-      octave::symbol_scope scope = interp.require_current_scope ("openvar");
-
-      octave_value val = scope.varval (name);
+      octave_value val = interp.varval (name);
 
       if (val.is_undefined ())
         error ("openvar: '%s' is not a variable", name.c_str ());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/stack-frame-walker.h	Mon Jan 28 18:01:46 2019 +0000
@@ -0,0 +1,65 @@
+/*
+
+Copyright (C) 2018 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_stack_frame_walker_h)
+#define octave_stack_frame_walker_h 1
+
+#include "octave-config.h"
+
+namespace octave
+{
+  class compiled_fcn_stack_frame;
+  class script_stack_frame;
+  class user_fcn_stack_frame;
+  class scope_stack_frame;
+
+  class stack_frame_walker
+  {
+  protected:
+
+    stack_frame_walker (void) { }
+
+    virtual ~stack_frame_walker (void) = default;
+
+  public:
+
+    // No copying!
+
+    stack_frame_walker (const stack_frame_walker&) = delete;
+
+    stack_frame_walker& operator = (const stack_frame_walker&) = delete;
+
+    virtual void
+    visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0;
+
+    virtual void
+    visit_script_stack_frame (script_stack_frame&) = 0;
+
+    virtual void
+    visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0;
+
+    virtual void
+    visit_scope_stack_frame (scope_stack_frame&) = 0;
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/stack-frame.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -0,0 +1,1382 @@
+/*
+
+Copyright (C) 1995-2018 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "lo-regexp.h"
+#include "str-vec.h"
+
+#include "call-stack.h"
+#include "defun.h"
+#include "interpreter.h"
+#include "interpreter-private.h"
+#include "oct-map.h"
+#include "ov.h"
+#include "ov-fcn.h"
+#include "ov-fcn-handle.h"
+#include "ov-usr-fcn.h"
+#include "pager.h"
+#include "parse.h"
+#include "stack-frame.h"
+#include "stack-frame-walker.h"
+#include "syminfo.h"
+#include "syminfo-accumulator.h"
+#include "symrec.h"
+#include "symscope.h"
+#include "variables.h"
+
+namespace octave
+{
+  // FIXME: There should probably be a display method for the script,
+  // fcn, and scope objects and the script and function objects should
+  // be responsible for displaying the scopes they contain.
+
+  static void display_scope (std::ostream& os, const symbol_scope& scope)
+  {
+    if (scope)
+      {
+        os << "scope: " << scope.name () << std::endl;
+
+        if (scope.num_symbols () > 0)
+          {
+            os << "name (frame offset, data offset, storage class):"
+               << std::endl;
+
+            std::list<symbol_record> symbols = scope.symbol_list ();
+
+            for (auto& sym : symbols)
+              {
+                os << "  " << sym.name () << " (" << sym.frame_offset ()
+                   << ", " << sym.data_offset () << ", " << sym.storage_class ()
+                   << ")" << std::endl;
+              }
+          }
+      }
+  }
+
+  class symbol_cleaner : public stack_frame_walker
+  {
+  public:
+
+    symbol_cleaner (const std::string& pattern, bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (pattern),
+        m_clear_all_names (false), m_clear_objects (false),
+        m_have_regexp (have_regexp), m_cleared_names ()
+    { }
+
+    symbol_cleaner (const string_vector& patterns, bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (patterns),
+        m_clear_all_names (false), m_clear_objects (false),
+        m_have_regexp (have_regexp), m_cleared_names ()
+    { }
+
+    symbol_cleaner (bool clear_all_names = true, bool clear_objects = false)
+      : stack_frame_walker (), m_patterns (),
+        m_clear_all_names (clear_all_names), m_clear_objects (clear_objects),
+        m_have_regexp (false), m_cleared_names ()
+    { }
+
+    symbol_cleaner (const symbol_cleaner&) = delete;
+
+    symbol_cleaner& operator = (const symbol_cleaner&) = delete;
+
+    ~symbol_cleaner (void) = default;
+
+    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
+    {
+      // This one follows static link always.  Hmm, should the access
+      // link for a compiled_fcn_stack_frame be the same as the static
+      // link?
+
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+    void visit_script_stack_frame (script_stack_frame& frame)
+    {
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
+    {
+      clean_frame (frame);
+
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_scope_stack_frame (scope_stack_frame& frame)
+    {
+      clean_frame (frame);
+
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+  private:
+
+    void maybe_clear_symbol (stack_frame& frame, const symbol_record& sym)
+    {
+      std::string name = sym.name ();
+
+      if (m_cleared_names.find (name) == m_cleared_names.end ())
+        {
+          // FIXME: Should we check that the name is defined and skip if
+          // it is not?  Is it possible for another symbol with the same
+          // name to appear in a later stack frame?
+
+          // FIXME: If we are clearing objects and a symbol is found,
+          // should we add it to the list of cleared names (since
+          // we did find a symbol) but skip clearing the object?
+
+          if (m_clear_objects && ! frame.is_object (sym))
+            return;
+
+          m_cleared_names.insert (name);
+
+          frame.clear (sym);
+        }
+    }
+
+    // FIXME: It would be nice to avoid the duplication in the following
+    // function.
+
+    void clear_symbols (stack_frame& frame,
+                        const std::list<symbol_record>& symbols)
+    {
+      if (m_clear_all_names)
+        {
+          for (const auto& sym : symbols)
+            maybe_clear_symbol (frame, sym);
+        }
+      else if (m_have_regexp)
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              regexp pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  if (pat.is_match (sym.name ()))
+                    maybe_clear_symbol (frame, sym);
+                }
+            }
+        }
+      else
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              glob_match pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  if (pat.match (sym.name ()))
+                    maybe_clear_symbol (frame, sym);
+                }
+            }
+        }
+    }
+
+    void clean_frame (stack_frame& frame)
+    {
+      symbol_scope scope = frame.get_scope ();
+
+      std::list<symbol_record> symbols = scope.symbol_list ();
+
+      if (m_clear_all_names || ! m_patterns.empty ())
+        clear_symbols (frame, symbols);
+    }
+
+    string_vector m_patterns;
+
+    bool m_clear_all_names;
+    bool m_clear_objects;
+    bool m_have_regexp;
+
+    std::set<std::string> m_cleared_names;
+  };
+
+  // This function is only implemented for user_fcn stack frames and
+  // only called for those objects using unwind_protect and the
+  // call_stack::clear_current_frame_values function.  Anything else
+  // indicates an error in the implementation.
+
+  void stack_frame::clear_values (void)
+  {
+    panic_impossible ();
+  }
+
+  // Return first occurrence of variables in current stack frame and any
+  // parent frames reachable through access links.
+
+  symbol_info_list stack_frame::all_variables (void)
+  {
+    symbol_info_accumulator sia (true, true);
+
+    accept (sia);
+
+    return sia.symbol_info ();
+  }
+
+  std::list<symbol_record>
+  stack_frame::glob (const std::string& pattern) const
+  {
+    std::list<symbol_record> retval;
+
+    symbol_scope scope = get_scope ();
+
+    const std::map<std::string, symbol_record>& symbols = scope.symbols ();
+
+    glob_match pat (pattern);
+
+    for (const auto& nm_sr : symbols)
+      {
+        if (pat.match (nm_sr.first))
+          {
+            symbol_record sr = nm_sr.second;
+
+            if (! is_variable (sr))
+              continue;
+
+            retval.push_back (sr);
+          }
+      }
+
+    return retval;
+  }
+
+  std::list<symbol_record>
+  stack_frame::regexp (const std::string& pattern) const
+  {
+    std::list<symbol_record> retval;
+
+    symbol_scope scope = get_scope ();
+
+    const std::map<std::string, symbol_record>& symbols = scope.symbols ();
+
+    octave::regexp pat (pattern);
+
+    for (const auto& nm_sr : symbols)
+      {
+        if (pat.is_match (nm_sr.first))
+          {
+            symbol_record sr = nm_sr.second;
+
+            if (! is_variable (sr))
+              continue;
+
+            retval.push_back (sr);
+          }
+      }
+
+    return retval;
+  }
+
+  std::list<std::string> stack_frame::variable_names (void) const
+  {
+    std::list<std::string> retval;
+
+    symbol_scope scope = get_scope ();
+
+    const std::map<std::string, symbol_record>& symbols = scope.symbols ();
+
+    for (const auto& nm_sr : symbols)
+      {
+        if (is_variable (nm_sr.second))
+          retval.push_back (nm_sr.first);
+      }
+
+    retval.sort ();
+
+    return retval;
+  }
+
+  size_t stack_frame::size (void) const
+  {
+    // This function should only be called for user_fcn_stack_frame or
+    // scope_stack_frame objects.  Anything else indicates an error in
+    // the implementation.
+
+    panic_impossible ();
+  }
+
+  void stack_frame::resize (size_t)
+  {
+    // This function should only be called for user_fcn_stack_frame or
+    // scope_stack_frame objects.  Anything else indicates an error in
+    // the implementation.
+
+    panic_impossible ();
+  }
+
+  stack_frame::scope_flags stack_frame::get_scope_flag (size_t) const
+  {
+    // This function should only be called for user_fcn_stack_frame or
+    // scope_stack_frame objects.  Anything else indicates an error in
+    // the implementation.
+
+    panic_impossible ();
+  }
+
+  void stack_frame::set_scope_flag (size_t, scope_flags)
+  {
+    // This function should only be called for user_fcn_stack_frame or
+    // scope_stack_frame objects.  Anything else indicates an error in
+    // the implementation.
+
+    panic_impossible ();
+  }
+
+  void stack_frame::install_variable (const symbol_record& sym,
+                                      const octave_value& value, bool global)
+  {
+    if (global)
+      {
+        octave_value val = varval (sym);
+
+        if (val.is_defined ())
+          {
+            std::string nm = sym.name ();
+
+            warning_with_id ("Octave:global-local-conflict",
+                             "global: '%s' is defined in the current scope.\n",
+                             nm.c_str ());
+            warning_with_id ("Octave:global-local-conflict",
+                             "global: in a future version, global variables must be declared before use.\n");
+
+            // If the symbol is defined in the local but not the
+            // global scope, then use the local value as the
+            // initial value.  This value will also override any
+            // initializer in the global statement.
+            octave_value global_val = m_call_stack.global_varval (nm);
+
+            if (global_val.is_defined ())
+              {
+                warning_with_id ("Octave:global-local-conflict",
+                                 "global: global value overrides existing local value");
+
+                clear (sym);
+              }
+            else
+              {
+                warning_with_id ("Octave:global-local-conflict",
+                                 "global: existing local value used to initialize global variable");
+
+                m_call_stack.global_varref (nm) = val;
+              }
+          }
+
+        mark_global (sym);
+      }
+
+    if (value.is_defined ())
+      assign (sym, value);
+  }
+
+  octave_value stack_frame::varval (size_t) const
+  {
+    // This function should only be called for user_fcn_stack_frame or
+    // scope_stack_frame objects.  Anything else indicates an error in
+    // the implementation.
+
+    panic_impossible ();
+  }
+
+  octave_value& stack_frame::varref (size_t)
+  {
+    // This function should only be called for user_fcn_stack_frame or
+    // scope_stack_frame objects.  Anything else indicates an error in
+    // the implementation.
+
+    panic_impossible ();
+  }
+
+  void stack_frame::clear_objects (void)
+  {
+    symbol_cleaner sc (true, true);
+
+    accept (sc);
+  }
+
+  void stack_frame::clear_variable (const std::string& name)
+  {
+    symbol_cleaner sc (name);
+
+    accept (sc);
+  }
+
+  void stack_frame::clear_variable_pattern (const std::string& pattern)
+  {
+    symbol_cleaner sc (pattern);
+
+    accept (sc);
+  }
+
+  void stack_frame::clear_variable_pattern (const string_vector& patterns)
+  {
+    symbol_cleaner sc (patterns);
+
+    accept (sc);
+  }
+
+  void stack_frame::clear_variable_regexp (const std::string& pattern)
+  {
+    symbol_cleaner sc (pattern, true);
+
+    accept (sc);
+  }
+
+  void stack_frame::clear_variable_regexp (const string_vector& patterns)
+  {
+    symbol_cleaner sc (patterns, true);
+
+    accept (sc);
+  }
+
+  void stack_frame::clear_variables (void)
+  {
+    symbol_cleaner sc;
+
+    accept (sc);
+  }
+
+  void stack_frame::display (bool follow) const
+  {
+    std::ostream& os = octave_stdout;
+
+    os << "-- [stack_frame] (" << this << ") --" << std::endl;
+
+    os << "static link: ";
+    if (m_static_link)
+      os << m_static_link;
+    else
+      os << "NULL";
+    os << std::endl;
+
+    os << "access link: ";
+    if (m_access_link)
+      os << m_access_link;
+    else
+      os << "NULL";
+    os << std::endl;
+
+    os << "line: " << m_line << std::endl;
+    os << "column: " << m_column << std::endl;
+    os << "prev: " << m_prev << std::endl;
+
+    os << std::endl;
+
+    if (! follow)
+      return;
+
+    os << "FOLLOWING ACCESS LINKS:" << std::endl;
+    const stack_frame *frm = access_link ();
+    while (frm)
+      {
+        frm->display (false);
+        os << std::endl;
+
+        frm = frm->access_link ();
+      }
+  }
+
+  void compiled_fcn_stack_frame::display (bool follow) const
+  {
+    std::ostream& os = octave_stdout;
+
+    os << "-- [compiled_fcn_stack_frame] (" << this << ") --" << std::endl;
+    stack_frame::display (follow);
+
+    os << "fcn: " << m_fcn->name ()
+       << " (" << m_fcn->type_name () << ")" << std::endl;
+  }
+
+  void compiled_fcn_stack_frame::accept (stack_frame_walker& sfw)
+  {
+    sfw.visit_compiled_fcn_stack_frame (*this);
+  }
+
+  script_stack_frame::script_stack_frame (call_stack& cs,
+                                          octave_user_script *script,
+                                          unwind_protect *up_frame,
+                                          size_t prev,
+                                          stack_frame *static_link)
+    : stack_frame (cs, prev, static_link, get_access_link (static_link)),
+      m_script (script), m_unwind_protect_frame (up_frame),
+      m_lexical_frame_offsets (get_num_symbols (script), 1),
+      m_value_offsets (get_num_symbols (script), 0)
+  {
+    set_script_offsets ();
+  }
+
+  size_t script_stack_frame::get_num_symbols (octave_user_script *script)
+  {
+    symbol_scope script_scope = script->scope ();
+
+    return script_scope.num_symbols ();
+  }
+
+  void script_stack_frame::set_script_offsets (void)
+  {
+    // Set frame and data offsets inside stack frame based on enclosing
+    // scope(s).
+
+    symbol_scope script_scope = m_script->scope ();
+
+    size_t num_script_symbols = script_scope.num_symbols ();
+
+    resize (num_script_symbols);
+
+    const std::map<std::string, symbol_record>& script_symbols
+      = script_scope.symbols ();
+
+    set_script_offsets_internal (script_symbols);
+  }
+
+  void script_stack_frame::set_script_offsets_internal
+   (const std::map<std::string, symbol_record>& script_symbols)
+  {
+    // This scope will be used to evaluate the script.  Find (or
+    // possibly insert) symbols from the dummy script scope here.
+
+    symbol_scope eval_scope = m_access_link->get_scope ();
+
+    if (eval_scope.is_nested ())
+      {
+        bool found = false;
+
+        for (const auto& nm_sr : script_symbols)
+          {
+            std::string name = nm_sr.first;
+            symbol_record script_sr = nm_sr.second;
+
+            symbol_scope parent_scope = eval_scope;
+
+            size_t count = 1;
+
+            while (parent_scope)
+              {
+                const std::map<std::string, symbol_record>& parent_scope_symbols
+                  = parent_scope.symbols ();
+
+                auto p = parent_scope_symbols.find (name);
+
+                if (p != parent_scope_symbols.end ())
+                  {
+                    found = true;
+                    symbol_record parent_scope_sr = p->second;
+
+                    size_t script_sr_data_offset = script_sr.data_offset ();
+
+                    m_lexical_frame_offsets.at (script_sr_data_offset)
+                      = parent_scope_sr.frame_offset () + 1;
+
+                    m_value_offsets.at (script_sr_data_offset)
+                      = parent_scope_sr.data_offset ();
+
+                    break;
+                  }
+                else
+                  {
+                    count++;
+                    parent_scope = parent_scope.parent_scope ();
+                  }
+              }
+
+            if (! found)
+              error ("symbol '%s' cannot be added to static scope",
+                     name.c_str ());
+          }
+      }
+    else
+      {
+        const std::map<std::string, symbol_record>& eval_scope_symbols
+          = eval_scope.symbols ();
+
+        for (const auto& nm_sr : script_symbols)
+          {
+            std::string name = nm_sr.first;
+            symbol_record script_sr = nm_sr.second;
+
+            auto p = eval_scope_symbols.find (name);
+
+            symbol_record eval_scope_sr;
+
+            if (p == eval_scope_symbols.end ())
+              eval_scope_sr = eval_scope.insert (name);
+            else
+              eval_scope_sr = p->second;
+
+            size_t script_sr_data_offset = script_sr.data_offset ();
+
+            // The +1 is for going from the script frame to the eval
+            // frame.  Only one access_link should need to be followed.
+
+            m_lexical_frame_offsets.at (script_sr_data_offset)
+              = eval_scope_sr.frame_offset () + 1;
+
+            m_value_offsets.at (script_sr_data_offset)
+              = eval_scope_sr.data_offset ();
+          }
+      }
+  }
+
+  void script_stack_frame::resize_and_update_script_offsets (const symbol_record& sym)
+  {
+    size_t data_offset = sym.data_offset ();
+
+    // This function is called when adding new symbols to a script
+    // scope.  If the symbol wasn't present before, it should be outside
+    // the range so we need to resize then update offsets.
+
+    assert (data_offset >= size ());
+
+    resize (data_offset+1);
+
+    // FIXME: We should be able to avoid creating the map object and the
+    // looping in the set_scripts_offsets_internal function.  Can we do
+    // that without (or with minimal) code duplication?
+
+    std::map<std::string, symbol_record> tmp_symbols;
+    tmp_symbols[sym.name ()] = sym;
+    set_script_offsets_internal (tmp_symbols);
+  }
+
+  // If this is a nested scope, set access_link to nearest parent
+  // stack frame that corresponds to the lexical parent of this scope.
+
+  stack_frame *
+  script_stack_frame::get_access_link (stack_frame *static_link)
+  {
+    stack_frame *alink = nullptr;
+
+    // If this script is called from another script, set access
+    // link to ultimate parent stack frame.
+
+    alink = static_link;
+
+    while (alink->is_user_script_frame ())
+      {
+        if (alink->access_link ())
+          alink = alink->access_link ();
+        else
+          break;
+      }
+
+    return alink;
+  }
+
+  symbol_record script_stack_frame::lookup_symbol (const std::string& name) const
+  {
+    symbol_scope scope = get_scope ();
+
+    symbol_record sym = scope.lookup_symbol (name);
+
+    if (sym)
+      {
+        assert (sym.frame_offset () == 0);
+
+        return sym;
+      }
+
+    sym = m_access_link->lookup_symbol (name);
+
+    // Return symbol record with adjusted frame offset.
+    symbol_record new_sym = sym.dup ();
+
+    new_sym.set_frame_offset (sym.frame_offset () + 1);
+
+    return new_sym;
+  }
+
+  symbol_record script_stack_frame::insert_symbol (const std::string& name)
+  {
+    // If the symbols is already in the immediate scope, there is
+    // nothing more to do.
+
+    symbol_scope scope = get_scope ();
+
+    symbol_record sym = scope.lookup_symbol (name);
+
+    if (sym)
+      {
+        // All symbol records in a script scope should have zero offset,
+        // which means we redirect our lookup using
+        // lexical_frame_offsets and values_offets.
+        assert (sym.frame_offset () == 0);
+
+        return sym;
+      }
+
+    // Insert the symbol in the current scope then resize and update
+    // offsets.  This operation should never fail.
+
+    sym = scope.find_symbol (name);
+
+    assert (sym);
+
+    resize_and_update_script_offsets (sym);
+
+    return sym;
+  }
+
+  void script_stack_frame::get_val_offsets (const symbol_record& sym,
+                                            size_t& frame_offset,
+                                            size_t& data_offset) const
+  {
+    data_offset = sym.data_offset ();
+    frame_offset = sym.frame_offset ();
+
+    if (frame_offset == 0)
+      {
+        // An out of range data_offset value here indicates an error in
+        // the implementation.
+
+        if (data_offset >= size ())
+          panic_impossible ();
+
+        // Use frame and value offsets stored in this stack frame,
+        // indexed by data_offset from the symbol_record to find the
+        // values.  These offsets were determined by
+        // script_stack_frame::set_script_offsets when this script was
+        // invoked.
+
+        frame_offset = m_lexical_frame_offsets.at (data_offset);
+        data_offset = m_value_offsets.at (data_offset);
+      }
+    else
+      {
+        // If frame_offset is not zero, then then we must have a symbol
+        // that was not originally in the script.  The values should
+        // have been determined by the script_stack_frame::lookup function.
+      }
+  }
+
+  void script_stack_frame::get_val_offsets_with_insert (const symbol_record& sym,
+                                                        size_t& frame_offset,
+                                                        size_t& data_offset)
+  {
+    data_offset = sym.data_offset ();
+    frame_offset = sym.frame_offset ();
+
+    if (frame_offset == 0)
+      {
+        if (data_offset >= size ())
+          {
+            // If the data_offset is out of range, then we must have a
+            // symbol that was not originally in the script.  Resize and
+            // update the offsets.
+
+            resize_and_update_script_offsets (sym);
+          }
+
+        // Use frame and value offsets stored in this stack frame,
+        // indexed by data_offset from the symbol_record to find the
+        // values.  These offsets were determined by
+        // script_stack_frame::set_script_offsets when this script was
+        // invoked.
+
+        frame_offset = m_lexical_frame_offsets.at (data_offset);
+        data_offset = m_value_offsets.at (data_offset);
+      }
+    else
+      {
+        // If frame_offset is not zero, then then we must have a symbol
+        // that was not originally in the script.  The values were
+        // determined by the script_stack_frame::lookup function.
+      }
+  }
+
+  stack_frame::scope_flags
+  script_stack_frame::scope_flag (const symbol_record& sym) const
+  {
+    size_t frame_offset;
+    size_t data_offset;
+    get_val_offsets (sym, frame_offset, data_offset);
+
+    // Follow frame_offset access links to stack frame that holds
+    // the value.
+
+    const stack_frame *frame = this;
+
+    for (size_t i = 0; i < frame_offset; i++)
+      frame = frame->access_link ();
+
+    if (! frame)
+      error ("internal error: invalid access link in function call stack");
+
+    if (data_offset >= frame->size ())
+      return LOCAL;
+
+    return frame->get_scope_flag (data_offset);
+  }
+
+  octave_value script_stack_frame::varval (const symbol_record& sym) const
+  {
+    size_t frame_offset;
+    size_t data_offset;
+    get_val_offsets (sym, frame_offset, data_offset);
+
+    // Follow frame_offset access links to stack frame that holds
+    // the value.
+
+    const stack_frame *frame = this;
+
+    for (size_t i = 0; i < frame_offset; i++)
+      frame = frame->access_link ();
+
+    if (! frame)
+      error ("internal error: invalid access link in function call stack");
+
+    if (data_offset >= frame->size ())
+      return octave_value ();
+
+    switch (frame->get_scope_flag (data_offset))
+      {
+      case LOCAL:
+        return frame->varval (data_offset);
+
+      case PERSISTENT:
+        {
+          symbol_scope scope = frame->get_scope ();
+
+          return scope.persistent_varval (data_offset);
+        }
+
+      case GLOBAL:
+        return m_call_stack.global_varval (sym.name ());
+      }
+
+    error ("internal error: invalid switch case");
+  }
+
+  octave_value& script_stack_frame::varref (const symbol_record& sym)
+  {
+    size_t frame_offset;
+    size_t data_offset;
+    get_val_offsets_with_insert (sym, frame_offset, data_offset);
+
+    // Follow frame_offset access links to stack frame that holds
+    // the value.
+
+    stack_frame *frame = this;
+
+    for (size_t i = 0; i < frame_offset; i++)
+      frame = frame->access_link ();
+
+    if (data_offset >= frame->size ())
+      frame->resize (data_offset+1);
+
+    switch (frame->get_scope_flag (data_offset))
+      {
+      case LOCAL:
+        return frame->varref (data_offset);
+
+      case PERSISTENT:
+        {
+          symbol_scope scope = frame->get_scope ();
+
+          return scope.persistent_varref (data_offset);
+        }
+
+      case GLOBAL:
+        return m_call_stack.global_varref (sym.name ());
+      }
+
+    error ("internal error: invalid switch case");
+  }
+
+  void script_stack_frame::mark_scope (const symbol_record& sym,
+                                       scope_flags flag)
+  {
+    size_t data_offset = sym.data_offset ();
+
+    // Redirection to evaluation context for the script.
+
+    size_t frame_offset = m_lexical_frame_offsets.at (data_offset);
+    data_offset = m_value_offsets.at (data_offset);
+
+    if (frame_offset > 1)
+      error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used");
+
+    stack_frame *frame = access_link ();
+
+    if (data_offset >= frame->size ())
+      frame->resize (data_offset+1);
+
+    frame->set_scope_flag (data_offset, flag);
+  }
+
+  void script_stack_frame::display (bool follow) const
+  {
+    std::ostream& os = octave_stdout;
+
+    os << "-- [script_stack_frame] (" << this << ") --" << std::endl;
+    stack_frame::display (follow);
+
+    os << "script: " << m_script->name ()
+       << " (" << m_script->type_name () << ")" << std::endl;
+
+    os << "lexical_offsets: " << m_lexical_frame_offsets.size ()
+       << " elements:";
+
+    for (size_t i = 0; i < m_lexical_frame_offsets.size (); i++)
+      os << "  " << m_lexical_frame_offsets.at (i);
+    os << std::endl;
+
+    os << "value_offsets: " << m_value_offsets.size () << " elements:";
+    for (size_t i = 0; i < m_value_offsets.size (); i++)
+      os << "  " << m_value_offsets.at (i);
+    os << std::endl;
+
+    display_scope (os, get_scope ());
+  }
+
+  void script_stack_frame::accept (stack_frame_walker& sfw)
+  {
+    sfw.visit_script_stack_frame (*this);
+  }
+
+  void base_value_stack_frame::display (bool follow) const
+  {
+    std::ostream& os = octave_stdout;
+
+    os << "-- [base_value_stack_frame] (" << this << ") --" << std::endl;
+    stack_frame::display (follow);
+
+    os << "values: " << m_values.size ()
+       << " elements (idx, scope flag, type):" << std::endl;
+
+    for (size_t i = 0; i < m_values.size (); i++)
+      {
+        os << "  (" << i << ", " << m_flags.at (i) << ", ";
+
+        octave_value val = varval (i);
+
+        os << (val.is_defined () ? val.type_name () : " UNDEFINED") << ")"
+           << std::endl;
+      }
+  }
+
+  // If this is a nested scope, set access_link to nearest parent
+  // stack frame that corresponds to the lexical parent of this scope.
+
+  stack_frame *
+  user_fcn_stack_frame::get_access_link (octave_user_function *fcn,
+                                         stack_frame *static_link)
+  {
+    stack_frame *alink = nullptr;
+
+    symbol_scope fcn_scope = fcn->scope ();
+
+    if (fcn_scope.is_nested ())
+      {
+        if (! static_link)
+          error ("internal call stack error (invalid static link)");
+
+        symbol_scope caller_scope = static_link->get_scope ();
+
+        int nesting_depth = fcn_scope.nesting_depth ();
+        int caller_nesting_depth = caller_scope.nesting_depth ();
+
+        if (caller_nesting_depth < nesting_depth)
+          {
+            // FIXME: do we need to ensure that the called
+            // function is a child of the caller?  Does it hurt
+            // to assert this condition, at least for now?
+
+            alink = static_link;
+          }
+        else
+          {
+            // FIXME: do we need to check that the parent of the
+            // called function is also a parent of the caller?
+            // Does it hurt to assert this condition, at least
+            // for now?
+
+            int links_to_follow = caller_nesting_depth - nesting_depth + 1;
+
+            alink = static_link;
+
+            while (alink && --links_to_follow >= 0)
+              alink = alink->access_link ();
+
+            if (! alink)
+              error ("internal function nesting error (invalid access link)");
+          }
+      }
+
+    return alink;
+  }
+
+  void user_fcn_stack_frame::clear_values (void)
+  {
+    symbol_scope fcn_scope = m_fcn->scope ();
+
+    const std::list<symbol_record>& symbols = fcn_scope.symbol_list ();
+
+    if (size () == 0)
+      return;
+
+    for (const auto& sym : symbols)
+      {
+        size_t frame_offset = sym.frame_offset ();
+
+        if (frame_offset > 0)
+          continue;
+
+        size_t data_offset = sym.data_offset ();
+
+        if (data_offset >= size ())
+          continue;
+
+        if (get_scope_flag (data_offset) == LOCAL)
+          {
+            octave_value& ref = m_values.at (data_offset);
+
+            if (ref.get_count () == 1)
+              {
+                ref.call_object_destructor ();
+                ref = octave_value ();
+              }
+          }
+      }
+  }
+
+  symbol_record user_fcn_stack_frame::lookup_symbol (const std::string& name) const
+  {
+    const stack_frame *frame = this;
+
+    while (frame)
+      {
+        symbol_scope scope = frame->get_scope ();
+
+        symbol_record sym = scope.lookup_symbol (name);
+
+        if (sym)
+          return sym;
+
+        frame = frame->access_link ();
+      }
+
+    return symbol_record ();
+  }
+
+  symbol_record user_fcn_stack_frame::insert_symbol (const std::string& name)
+  {
+    // If the symbols is already in the immediate scope, there is
+    // nothing more to do.
+
+    symbol_scope scope = get_scope ();
+
+    symbol_record sym = scope.lookup_symbol (name);
+
+    if (sym)
+      return sym;
+
+    // FIXME: This needs some thought... We may need to add a symbol to
+    // a static workspace, but the symbol can never be defined as a
+    // variable.  This currently works by tagging the added symbol as
+    // "added_static".  Aside from the bad name, this doesn't seem like
+    // the best solution.  Maybe scopes should have a separate set of
+    // symbols that may only be defined as functions?
+
+    // Insert the symbol in the current scope.  This is not possible for
+    // anonymous functions, nested functions, or functions that contain
+    // nested functions (their scopes will all be marked static).
+
+    //    if (scope.is_static ())
+    //      error ("can not add variable '%s' to a static workspace",
+    //             name.c_str ());
+
+    // At this point, non-local references are not possible so we only
+    // need to look in the current scope and insert there.  This
+    // operation should never fail.
+
+    sym = scope.find_symbol (name);
+
+    assert (sym);
+
+    return sym;
+  }
+
+  stack_frame::scope_flags
+  user_fcn_stack_frame::scope_flag (const symbol_record& sym) const
+  {
+    size_t frame_offset = sym.frame_offset ();
+    size_t data_offset = sym.data_offset ();
+
+    // Follow frame_offset access links to stack frame that holds
+    // the value.
+
+    const stack_frame *frame = this;
+
+    for (size_t i = 0; i < frame_offset; i++)
+      frame = frame->access_link ();
+
+    if (! frame)
+      error ("internal error: invalid access link in function call stack");
+
+    if (data_offset >= frame->size ())
+      return LOCAL;
+
+    return frame->get_scope_flag (data_offset);
+  }
+
+  octave_value user_fcn_stack_frame::varval (const symbol_record& sym) const
+  {
+    size_t frame_offset = sym.frame_offset ();
+    size_t data_offset = sym.data_offset ();
+
+    // Follow frame_offset access links to stack frame that holds
+    // the value.
+
+    const stack_frame *frame = this;
+
+    for (size_t i = 0; i < frame_offset; i++)
+      frame = frame->access_link ();
+
+    if (! frame)
+      error ("internal error: invalid access link in function call stack");
+
+    if (data_offset >= frame->size ())
+      return octave_value ();
+
+    switch (frame->get_scope_flag (data_offset))
+      {
+      case LOCAL:
+        return frame->varval (data_offset);
+
+      case PERSISTENT:
+        {
+          symbol_scope scope = frame->get_scope ();
+
+          return scope.persistent_varval (data_offset);
+        }
+
+      case GLOBAL:
+        return m_call_stack.global_varval (sym.name ());
+      }
+
+    error ("internal error: invalid switch case");
+  }
+
+  octave_value& user_fcn_stack_frame::varref (const symbol_record& sym)
+  {
+    size_t frame_offset = sym.frame_offset ();
+    size_t data_offset = sym.data_offset ();
+
+    // Follow frame_offset access links to stack frame that holds
+    // the value.
+
+    stack_frame *frame = this;
+
+    for (size_t i = 0; i < frame_offset; i++)
+      frame = frame->access_link ();
+
+    if (data_offset >= frame->size ())
+      frame->resize (data_offset+1);
+
+    switch (frame->get_scope_flag (data_offset))
+      {
+      case LOCAL:
+        return frame->varref (data_offset);
+
+      case PERSISTENT:
+        {
+          symbol_scope scope = frame->get_scope ();
+
+          return scope.persistent_varref (data_offset);
+        }
+
+      case GLOBAL:
+        return m_call_stack.global_varref (sym.name ());
+      }
+
+    error ("internal error: invalid switch case");
+  }
+
+  void user_fcn_stack_frame::mark_scope (const symbol_record& sym, scope_flags flag)
+  {
+    size_t frame_offset = sym.frame_offset ();
+
+    if (frame_offset > 0 && (flag == PERSISTENT || flag == GLOBAL))
+      error ("variables must be made PERSISTENT or GLOBAL in the first scope in which they are used");
+
+    size_t data_offset = sym.data_offset ();
+
+    if (data_offset >= size ())
+      resize (data_offset+1);
+
+    set_scope_flag (data_offset, flag);
+  }
+
+  void user_fcn_stack_frame::display (bool follow) const
+  {
+    std::ostream& os = octave_stdout;
+
+    os << "-- [user_fcn_stack_frame] (" << this << ") --" << std::endl;
+    base_value_stack_frame::display (follow);
+
+    os << "fcn: " << m_fcn->name ()
+       << " (" << m_fcn->type_name () << ")" << std::endl;
+
+    display_scope (os, get_scope ());
+ }
+
+  void user_fcn_stack_frame::accept (stack_frame_walker& sfw)
+  {
+    sfw.visit_user_fcn_stack_frame (*this);
+  }
+
+  symbol_record scope_stack_frame::insert_symbol (const std::string& name)
+  {
+    // There is no access link for scope frames, so there is no other
+    // frame to search in and the offset must be zero.
+
+    symbol_record sym = m_scope.lookup_symbol (name);
+
+    if (sym)
+      return sym;
+
+    // If the symbol is not found, insert it.  We only need to search in
+    // the local scope object.  This operation should never fail.
+
+    sym = m_scope.find_symbol (name);
+
+    assert (sym);
+
+    return sym;
+  }
+
+  stack_frame::scope_flags
+  scope_stack_frame::scope_flag (const symbol_record& sym) const
+  {
+    // There is no access link for scope frames, so the frame
+    // offset must be zero.
+
+    size_t data_offset = sym.data_offset ();
+
+    if (data_offset >= size ())
+      return LOCAL;
+
+    return get_scope_flag (data_offset);
+  }
+
+  octave_value scope_stack_frame::varval (const symbol_record& sym) const
+  {
+    // There is no access link for scope frames, so the frame
+    // offset must be zero.
+
+    size_t data_offset = sym.data_offset ();
+
+    if (data_offset >= size ())
+      return octave_value ();
+
+    switch (get_scope_flag (data_offset))
+      {
+      case LOCAL:
+        return m_values.at (data_offset);
+
+      case PERSISTENT:
+        return m_scope.persistent_varval (data_offset);
+
+      case GLOBAL:
+        return m_call_stack.global_varval (sym.name ());
+      }
+
+    error ("internal error: invalid switch case");
+  }
+
+  octave_value& scope_stack_frame::varref (const symbol_record& sym)
+  {
+    // There is no access link for scope frames, so the frame
+    // offset must be zero.
+
+    size_t data_offset = sym.data_offset ();
+
+    if (data_offset >= size ())
+      resize (data_offset+1);
+
+    switch (get_scope_flag (data_offset))
+      {
+      case LOCAL:
+        return m_values.at (data_offset);
+
+      case PERSISTENT:
+        return m_scope.persistent_varref (data_offset);
+
+      case GLOBAL:
+        return m_call_stack.global_varref (sym.name ());
+      }
+
+    error ("internal error: invalid switch case");
+  }
+
+  void scope_stack_frame::mark_scope (const symbol_record& sym,
+                                      scope_flags flag)
+  {
+    // There is no access link for scope frames, so the frame
+    // offset must be zero.
+
+    size_t data_offset = sym.data_offset ();
+
+    if (data_offset >= size ())
+      resize (data_offset+1);
+
+    set_scope_flag (data_offset, flag);
+  }
+
+  void scope_stack_frame::display (bool follow) const
+  {
+    std::ostream& os = octave_stdout;
+
+    os << "-- [scope_stack_frame] (" << this << ") --" << std::endl;
+    base_value_stack_frame::display (follow);
+
+    display_scope (os, m_scope);
+  }
+
+  void scope_stack_frame::accept (stack_frame_walker& sfw)
+  {
+    sfw.visit_scope_stack_frame (*this);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/stack-frame.h	Mon Jan 28 18:01:46 2019 +0000
@@ -0,0 +1,996 @@
+/*
+
+Copyright (C) 1993-2018 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_stack_frame_h)
+#define octave_stack_frame_h 1
+
+#include "octave-config.h"
+
+#include <deque>
+#include <iosfwd>
+#include <list>
+#include <map>
+#include <string>
+
+class octave_value;
+class octave_value_list;
+
+#include "ov-fcn.h"
+#include "ov-fcn.h"
+#include "ov-fcn-handle.h"
+#include "ov-usr-fcn.h"
+#include "syminfo.h"
+#include "symscope.h"
+
+// Variable values are stored in the stack_frame objects that make up
+// the call_stack.  There are four separate stack_frame objects
+// corresponding to the following language elements:
+//
+//  * user-defined functions
+//
+//    These are .m files.  They have local variables.
+//
+//  * scripts
+//
+//    These are .m files, but not functions.  They access variables,
+//    but do not store any values directly.  All values are stored in
+//    the stack frame corresponding to the scope in which they are
+//    executed.
+//
+//  * scopes that do not correspond to functions
+//
+//    This is primarily used by the top-level scope but the
+//    interpreter may also create temporary scopes in which to
+//    evaluate functions or scripts.
+//
+// * compiled functions
+//
+//   These are built-in functions and dynamically-loaded compiled
+//   functions (.mex and .oct files) and do not contain variable
+//   values of their own.  They are skipped when Octave displays a
+//   stack trace.
+//
+// All stack frames also contain the following data:
+//
+//  * a reference to the call stack that contains the frame
+//
+//    Global variables are now stored in the call stack and this link
+//    gives us immediate access to them.
+//
+//  * line and column in the source file where the stack frame was created
+//
+//    These values are used to print stack traces.
+//
+//  * A pointer to the nearest parent frame that contains variable
+//    info (the "static" link)
+//
+//    A frame that contains variable info may be a user-defined
+//    function, script, or scope frame.  This pointer should never
+//    point to a compiled function stack frame.
+//
+//  * A pointer to the nearest lexical parent frame (the "access" link)
+//
+//    Used to access non-local variables for nested and anonymous
+//    functions or as a link to the parent frame in which a script is
+//    executed.  This pointer should only point to a parent function
+//    stack frame.
+
+namespace octave
+{
+  class call_stack;
+  class tree_evaluator;
+  class symbol_info_list;
+  class unwind_protect;
+
+  class compiled_fcn_stack_frame;
+  class script_stack_frame;
+  class user_fcn_stack_frame;
+  class scope_stack_frame;
+
+  class stack_frame_walker;
+
+  class stack_frame
+  {
+  public:
+
+    // Markers indicating the type of a variable.  Values for local
+    // variables are stored in the stack frame.  Values for
+    // global variables are stored in the call_stack object that
+    // contains the stack frame.  Values for persistent variables are
+    // stored in the function scope corresponding to the stack frame.
+
+    enum scope_flags
+      {
+        LOCAL,
+        GLOBAL,
+        PERSISTENT
+      };
+
+    // Index into the list of automatic variables for user-defined
+    // function stack frames.
+
+    enum auto_var_type
+      {
+        ARG_NAMES,
+        IGNORED,
+        NARGIN,
+        NARGOUT,
+        SAVED_WARNING_STATES,
+        NUM_AUTO_VARS
+      };
+
+    stack_frame (void) = delete;
+
+    stack_frame (call_stack& cs, size_t prev, stack_frame *static_link,
+                 stack_frame *access_link)
+      : m_call_stack (cs), m_line (-1), m_column (-1), m_prev (prev),
+        m_static_link (static_link), m_access_link (access_link)
+    { }
+
+    stack_frame (const stack_frame& elt) = default;
+
+    stack_frame& operator = (const stack_frame& elt) = delete;
+
+    virtual ~stack_frame (void) = default;
+
+    // FIXME: It would be nice to eliminate these but there are a few
+    // places where we still need to know the specific type of the
+    // stack frame that we are handling.
+
+    virtual bool is_compiled_fcn_frame (void) const { return false; }
+    virtual bool is_user_script_frame (void) const { return false; }
+    virtual bool is_user_fcn_frame (void) const { return false; }
+    virtual bool is_scope_frame (void) const { return false; }
+
+    virtual void clear_values (void);
+
+    size_t previous (void) const { return m_prev; }
+
+    void line (int l) { m_line = l; }
+    int line (void) const { return m_line; }
+
+    void column (int c) { m_column = c; }
+    int column (void) const { return m_column; }
+
+    std::string fcn_file_name (void) const
+    {
+      octave_function *fcn = function ();
+
+      return fcn ? fcn->fcn_file_name () : "";
+    }
+
+    std::string fcn_name (bool print_subfn = true) const
+    {
+      std::string retval;
+
+      octave_function *fcn = function ();
+
+      if (fcn)
+        {
+          std::string parent_fcn_name = fcn->parent_fcn_name ();
+
+          if (print_subfn && ! parent_fcn_name.empty ())
+            retval = parent_fcn_name + '>';
+
+          if (fcn->is_anonymous_function ())
+            retval += octave_fcn_handle::anonymous;
+          else
+            retval += fcn->name ();
+        }
+      else
+        retval = "<unknown>";
+
+      return retval;
+    }
+
+    virtual symbol_scope get_scope (void) const = 0;
+
+    virtual octave_function * function (void) const { return nullptr; }
+
+    virtual unwind_protect *
+    unwind_protect_frame (void) const { return nullptr; }
+
+    // FIXME: Should this function be private?
+
+    symbol_info_list
+    make_symbol_info_list (const std::list<symbol_record>& symrec_list) const
+    {
+      symbol_info_list symbol_stats;
+
+      for (const auto& sym : symrec_list)
+        {
+          octave_value value = varval (sym);
+
+          if (value.is_defined ())
+            {
+              symbol_info syminf (sym.name (), value, sym.is_formal (),
+                                  is_global (sym), is_persistent (sym));
+
+              symbol_stats.append (syminf);
+            }
+        }
+
+      return symbol_stats;
+    }
+
+    symbol_info_list all_variables (void);
+
+    // FIXME: Should these exist?  Probably we should avoid returning
+    // lists of symbol_record objects, so maybe they should be
+    // private functions?
+
+    std::list<symbol_record> glob (const std::string& pattern) const;
+
+    std::list<symbol_record> regexp (const std::string& pattern) const;
+
+    std::list<std::string> variable_names (void) const;
+
+    // Look for named symbol visible from current scope.  Don't
+    // attempt to insert if missing.
+    virtual symbol_record lookup_symbol (const std::string&) const = 0;
+
+    // Look for named symbol visible from current scope.  Attempt to
+    // insert if missing.
+    virtual symbol_record insert_symbol (const std::string&) = 0;
+
+    // FIXME: should these functions should return all symbols visible in
+    // the current stack frame including those that come from a parent
+    // scope/frame?
+
+    symbol_info_list glob_symbol_info (const std::string& pattern) const
+    {
+      return make_symbol_info_list (glob (pattern));
+    }
+
+    symbol_info_list regexp_symbol_info (const std::string& pattern) const
+    {
+      return make_symbol_info_list (regexp (pattern));
+    }
+
+    symbol_info_list get_symbol_info (void)
+    {
+      return all_variables ();
+    }
+
+    void make_persistent (const symbol_record& sym)
+    {
+      if (sym.is_formal ())
+        {
+          std::string nm = sym.name ();
+          error ("can't make function parameter %s persistent", nm.c_str ());
+        }
+
+      if (is_global (sym))
+        {
+          std::string nm = sym.name ();
+          error ("can't make global variable '%s' persistent", nm.c_str ());
+        }
+
+      install_variable (sym, octave_value (), false);
+
+      mark_persistent (sym);
+    }
+
+    void make_global (const symbol_record& sym)
+    {
+      if (is_persistent (sym))
+        {
+          std::string nm = sym.name ();
+          error ("can't make persistent variable '%s' global", nm.c_str ());
+        }
+
+      install_variable (sym, octave_value (), true);
+
+      mark_global (sym);
+    }
+
+    stack_frame * static_link (void) const {return m_static_link; }
+
+    stack_frame * access_link (void) const {return m_access_link; }
+
+    virtual size_t size (void) const;
+
+    virtual void resize (size_t);
+
+    void mark_global (const symbol_record& sym)
+    {
+      mark_scope (sym, GLOBAL);
+    }
+
+    void unmark_global (const symbol_record& sym)
+    {
+      mark_scope (sym, LOCAL);
+    }
+
+    void mark_persistent (const symbol_record& sym)
+    {
+      mark_scope (sym, PERSISTENT);
+    }
+
+    void unmark_persistent (const symbol_record& sym)
+    {
+      mark_scope (sym, LOCAL);
+    }
+
+    bool is_defined (const symbol_record& sym) const
+    {
+      octave_value val = varval (sym);
+
+      return val.is_defined ();
+    }
+
+    bool is_variable (const symbol_record& sym) const
+    {
+      octave_value val = varval (sym);
+
+      return val.is_defined ();
+    }
+
+    bool is_variable (const std::string& name) const
+    {
+      symbol_record sym = lookup_symbol (name);
+
+      return sym ? is_variable (sym) : false;
+    }
+
+    bool is_local_variable (const std::string& name) const
+    {
+      symbol_record sym = lookup_symbol (name);
+
+      return sym ? (is_variable (sym) && ! is_global (sym)) : false;
+    }
+
+    bool is_object (const symbol_record& sym) const
+    {
+      octave_value val = varval (sym);
+
+      return val.isobject ();
+    }
+
+    bool is_object (const std::string& name) const
+    {
+      symbol_record sym = lookup_symbol (name);
+
+      return sym ? is_object (sym) : false;
+    }
+
+    virtual scope_flags scope_flag (const symbol_record&) const = 0;
+
+    virtual scope_flags get_scope_flag (size_t) const;
+
+    virtual void set_scope_flag (size_t, scope_flags);
+
+    bool is_global (const symbol_record& sym) const
+    {
+      return scope_flag (sym) == GLOBAL;
+    }
+
+    bool is_global (const std::string& name) const
+    {
+      symbol_record sym = lookup_symbol (name);
+
+      return sym ? is_global (sym) : false;
+    }
+
+    bool is_persistent (const symbol_record& sym) const
+    {
+      return scope_flag (sym) == PERSISTENT;
+    }
+
+    bool is_persistent (const std::string& name) const
+    {
+      symbol_record sym = lookup_symbol (name);
+
+      return sym ? is_persistent (sym) : false;
+    }
+
+    void install_variable (const symbol_record& sym,
+                           const octave_value& value, bool global);
+
+    void install_variable (const std::string& name,
+                           const octave_value& value, bool global)
+    {
+      symbol_record sym = insert_symbol (name);
+
+      install_variable (sym, value, global);
+    }
+
+    virtual octave_value get_auto_fcn_var (auto_var_type) const = 0;
+
+    virtual void set_auto_fcn_var (auto_var_type, const octave_value&) = 0;
+
+    virtual octave_value varval (const symbol_record& sym) const = 0;;
+
+    virtual octave_value varval (size_t data_offset) const;
+
+    octave_value varval (const std::string& name) const
+    {
+      symbol_record sym = lookup_symbol (name);
+
+      return sym ? varval (sym) : octave_value ();
+    }
+
+    virtual octave_value& varref (const symbol_record& sym) = 0;
+
+    virtual octave_value& varref (size_t data_offset);
+
+    void assign (const symbol_record& sym, const octave_value& val)
+    {
+      octave_value& lhs = varref (sym);
+
+      if (lhs.get_count () == 1)
+        lhs.call_object_destructor ();
+
+      // Regularize a null matrix if stored into a variable.
+      lhs = val.storable_value ();
+    }
+
+    void assign (const std::string& name, const octave_value& val)
+    {
+      symbol_record sym = insert_symbol (name);
+
+      assign (sym, val);
+    }
+
+    void assign (octave_value::assign_op op, const symbol_record& sym,
+                 const std::string& type,
+                 const std::list<octave_value_list>& idx,
+                 const octave_value& rhs)
+    {
+      if (idx.empty ())
+        {
+          if (op == octave_value::op_asn_eq)
+            assign (sym, rhs);
+          else
+            varref (sym).assign (op, rhs);
+        }
+      else
+        varref (sym).assign (op, type, idx, rhs);
+    }
+
+    void do_non_const_unary_op (octave_value::unary_op op,
+                                const symbol_record& sym,
+                                const std::string& type,
+                                const std::list<octave_value_list>& idx)
+    {
+      if (idx.empty ())
+        varref (sym).do_non_const_unary_op (op);
+      else
+        varref (sym).do_non_const_unary_op (op, type, idx);
+    }
+
+    octave_value value (const symbol_record& sym, const std::string& type,
+                        const std::list<octave_value_list>& idx) const
+    {
+      octave_value retval = varval (sym);
+
+      if (! idx.empty ())
+        {
+          if (retval.is_constant ())
+            retval = retval.subsref (type, idx);
+          else
+            {
+              octave_value_list t = retval.subsref (type, idx, 1);
+
+              retval = t.length () > 0 ? t(0) : octave_value ();
+            }
+        }
+
+      return retval;
+    }
+
+    octave_value find_subfunction (const std::string& name) const
+    {
+      symbol_scope scope = get_scope ();
+
+      return scope.find_subfunction (name);
+    }
+
+    void clear (const symbol_record& sym)
+    {
+      if (is_global (sym))
+        unmark_global (sym);
+
+      assign (sym, octave_value ());
+
+      if (is_persistent (sym))
+        unmark_persistent (sym);
+    }
+
+    void clear_objects (void);
+
+    void clear_variable (const std::string& name);
+
+    void clear_variable_pattern (const std::string& pattern);
+    void clear_variable_pattern (const string_vector& patterns);
+
+    void clear_variable_regexp (const std::string& pattern);
+    void clear_variable_regexp (const string_vector& patterns);
+
+    void clear_variables (void);
+
+    virtual void mark_scope (const symbol_record&, scope_flags) = 0;
+
+    virtual void display (bool follow = true) const;
+
+    virtual void accept (stack_frame_walker& sfw) = 0;
+
+  protected:
+
+    // Reference to the call stack that contains this frame.  Global
+    // variables are stored in the call stack.  This link gives us
+    // immediate access to them.
+    call_stack& m_call_stack;
+
+    // The line and column of the source file where this stack frame
+    // was created.  Used to print stack traces.
+    int m_line;
+    int m_column;
+
+    // FIXME: We could probably eliminate this variable.  Now that we
+    // maintain the static and access links to previous frames, this
+    // index should not be necessary.
+    size_t m_prev;
+
+    // Pointer to the nearest parent frame that contains variable
+    // information (script, function, or scope).
+    stack_frame *m_static_link;
+
+    // Pointer to the nearest lexical parent frame.  Used to access
+    // non-local variables for nested and anonymous functions or as a
+    // link to the parent frame in which a script is executed.
+    stack_frame *m_access_link;
+  };
+
+  class compiled_fcn_stack_frame : public stack_frame
+  {
+  public:
+
+    compiled_fcn_stack_frame (void) = delete;
+
+    compiled_fcn_stack_frame (call_stack& cs, octave_function *fcn,
+                              size_t prev, stack_frame *static_link)
+      : stack_frame (cs, prev, static_link, static_link->access_link ()),
+        m_fcn (fcn)
+    { }
+
+    compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default;
+
+    compiled_fcn_stack_frame&
+    operator = (const compiled_fcn_stack_frame& elt) = delete;
+
+    ~compiled_fcn_stack_frame (void) = default;
+
+    bool is_compiled_fcn_frame (void) const { return true; }
+
+    symbol_scope get_scope (void) const
+    {
+      return m_static_link->get_scope ();
+    }
+
+    octave_function * function (void) const { return m_fcn; }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      return m_static_link->lookup_symbol (name);
+    }
+
+    symbol_record insert_symbol (const std::string& name)
+    {
+      return m_static_link->insert_symbol (name);
+    }
+
+    stack_frame::scope_flags scope_flag (const symbol_record& sym) const
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->scope_flag (sym);
+    }
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_static_link->set_auto_fcn_var (avt, val);
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_static_link->get_auto_fcn_var (avt);
+    }
+
+    octave_value varval (const symbol_record& sym) const
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->varval (sym);
+    }
+
+    octave_value& varref (const symbol_record& sym)
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->varref (sym);
+    }
+
+    void mark_scope (const symbol_record& sym,
+                                               scope_flags flag)
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      m_static_link->mark_scope (sym, flag);
+    }
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // Compiled function object associated with this stack frame.
+    // Should always be a built-in, .oct or .mex file function and
+    // should always be valid.
+    octave_function *m_fcn;
+  };
+
+  // Scripts have a symbol_scope object to store the set of variables
+  // in the script, but values for those variables are stored in the
+  // stack frame corresponding to the nearest calling function or in
+  // the top-level scope (the evaluation stack frame).
+  //
+  // Accessing values in a scope requires a mapping from the index of
+  // the variable for the script scope to the list of values in the
+  // evaluation frame(s).  The frame offset tells us how many access
+  // links we must follow to find the stack frame that holds the
+  // value.  The value offset is the index into the vector of values
+  // in that stack frame that we should use to find the value.
+  //
+  // Frame and value offsets are set in this stack frame when it is
+  // created using information from the script and enclosing scopes.
+  //
+  // If a script is invoked in a nested function context, the frame
+  // offsets for individual values may be different.  Some may be
+  // accessed from the invoking function and some may come from a
+  // parent function.
+
+  class script_stack_frame : public stack_frame
+  {
+  public:
+
+    script_stack_frame (void) = delete;
+
+    script_stack_frame (call_stack& cs, octave_user_script *script,
+                        unwind_protect *up_frame, size_t prev,
+                        stack_frame *static_link);
+
+    script_stack_frame (const script_stack_frame& elt) = delete;
+
+    script_stack_frame& operator = (const script_stack_frame& elt) = delete;
+
+    ~script_stack_frame (void) = default;
+
+    bool is_user_script_frame (void) const { return true; }
+
+    static stack_frame * get_access_link (stack_frame *static_link);
+
+    static size_t get_num_symbols (octave_user_script *script);
+
+    void set_script_offsets (void);
+
+    void set_script_offsets_internal (const std::map<std::string, symbol_record>& symbols);
+
+    void resize_and_update_script_offsets (const symbol_record& sym);
+
+    symbol_scope get_scope (void) const { return m_script->scope (); }
+
+    octave_function * function (void) const { return m_script; }
+
+    unwind_protect *
+    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
+
+    symbol_record lookup_symbol (const std::string& name) const;
+
+    symbol_record insert_symbol (const std::string&);
+
+    size_t size (void) const { return m_lexical_frame_offsets.size (); }
+
+    void resize (size_t size)
+    {
+      m_lexical_frame_offsets.resize (size, 0);
+      m_value_offsets.resize (size, 0);
+    }
+
+    void get_val_offsets_with_insert (const symbol_record& sym,
+                                      size_t& frame_offset,
+                                      size_t& data_offset);
+
+    void get_val_offsets (const symbol_record& sym, size_t& frame_offset,
+                          size_t& data_offset) const;
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_access_link->set_auto_fcn_var (avt, val);
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_access_link->get_auto_fcn_var (avt);
+    }
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // Script object associated with this stack frame.  Should always
+    // be valid.
+    octave_user_script *m_script;
+
+    // The nearest unwind protect frame that was active when this
+    // stack frame was created.  Should always be valid.
+    unwind_protect *m_unwind_protect_frame;
+
+    // Mapping between the symbols in the symbol_scope object of the
+    // script to the stack frame in which the script is executed.  The
+    // frame offsets may be greater than one if the script is executed
+    // in a nested function context.
+
+    std::vector<size_t> m_lexical_frame_offsets;
+    std::vector<size_t> m_value_offsets;
+  };
+
+  // Base class for values and offsets shared by user_fcn and scope
+  // frames.
+
+  class base_value_stack_frame : public stack_frame
+  {
+  public:
+
+    base_value_stack_frame (void) = delete;
+
+    base_value_stack_frame (call_stack& cs, size_t num_symbols,
+                            size_t prev, stack_frame *static_link,
+                            stack_frame *access_link)
+      : stack_frame (cs, prev, static_link, access_link),
+        m_values (num_symbols, octave_value ()),
+        m_flags (num_symbols, LOCAL),
+        m_auto_vars (NUM_AUTO_VARS, octave_value ())
+    { }
+
+    base_value_stack_frame (const stack_frame& elt) = delete;
+
+    base_value_stack_frame& operator = (const stack_frame& elt) = delete;
+
+    ~base_value_stack_frame (void) = default;
+
+    size_t size (void) const
+    {
+      return m_values.size ();
+    }
+
+    void resize (size_t size)
+    {
+      m_values.resize (size, octave_value ());
+      m_flags.resize (size, LOCAL);
+    }
+
+    stack_frame::scope_flags get_scope_flag (size_t data_offset) const
+    {
+      return m_flags.at (data_offset);
+    }
+
+    void set_scope_flag (size_t data_offset, scope_flags flag)
+    {
+      m_flags.at (data_offset) = flag;
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_auto_vars.at (avt);
+    }
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_auto_vars.at (avt) = val;
+    }
+
+    octave_value varval (size_t data_offset) const
+    {
+      return m_values.at (data_offset);
+    }
+
+    octave_value& varref (size_t data_offset)
+    {
+      return m_values.at (data_offset);
+    }
+
+    void display (bool follow = true) const;
+
+  protected:
+
+    // Variable values.  This array is indexed by the data_offset
+    // value stored in the symbol_record objects of the scope
+    // associated with this stack frame.
+    std::vector<octave_value> m_values;
+
+    // The type of each variable (local, global, persistent) of each
+    // value.  This array is indexed by the data_offset value stored
+    // in the symbol_record objects of the scope associated with this
+    // stack frame.  Local values are found in the M_VALUES array.
+    // Global values are stored in the call_stack object that contains
+    // the stack frame.  Persistent values are stored in the function
+    // scope corresponding to the stack frame.
+    std::vector<scope_flags> m_flags;
+
+    // A fixed list of Automatic variables created for this function.
+    // The elements of this vector correspond to the auto_var_type
+    // enum.
+    std::vector<octave_value> m_auto_vars;
+  };
+
+  // User-defined functions have a symbol_scope object to store the set
+  // of variables in the function and values are stored in the stack
+  // frame corresponding to the invocation of the function or one of
+  // its parents.  The frame offset tells us how many access links we
+  // must follow to find the stack frame that holds the value.  The
+  // value offset is the index into the vector of values in that stack
+  // frame that we should use to find the value.
+  //
+  // Frame and value offsets are determined when the corresponding
+  // function is parsed.
+
+  class user_fcn_stack_frame : public base_value_stack_frame
+  {
+  public:
+
+    user_fcn_stack_frame (void) = delete;
+
+    user_fcn_stack_frame (call_stack& cs, octave_user_function *fcn,
+                          unwind_protect *up_frame, size_t prev,
+                          stack_frame *static_link)
+      : base_value_stack_frame (cs, get_num_symbols (fcn), prev, static_link,
+                                get_access_link (fcn, static_link)),
+        m_fcn (fcn), m_unwind_protect_frame (up_frame)
+    { }
+
+    user_fcn_stack_frame (const user_fcn_stack_frame& elt) = delete;
+
+    user_fcn_stack_frame& operator = (const user_fcn_stack_frame& elt) = delete;
+
+    ~user_fcn_stack_frame (void) = default;
+
+    bool is_user_fcn_frame (void) const { return true; }
+
+    static stack_frame *
+    get_access_link (octave_user_function *fcn, stack_frame *static_link);
+
+    static size_t get_num_symbols (octave_user_function *fcn)
+    {
+      symbol_scope fcn_scope = fcn->scope ();
+
+      return fcn_scope.num_symbols ();
+    }
+
+    void clear_values (void);
+
+    symbol_scope get_scope (void) const { return m_fcn->scope (); }
+
+    octave_function * function (void) const { return m_fcn; }
+
+    unwind_protect *
+    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
+
+    symbol_record lookup_symbol (const std::string& name) const;
+
+    symbol_record insert_symbol (const std::string&);
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // User-defined object associated with this stack frame.  Should
+    // always be valid.
+    octave_user_function *m_fcn;
+
+    // The nearest unwind protect frame that was active when this
+    // stack frame was created.  Should always be valid.
+    unwind_protect *m_unwind_protect_frame;
+  };
+
+  // Pure scope stack frames (primarily the top-level workspace) have
+  // a set of variables and values are stored in the stack frame.  All
+  // variable accesses are direct as there are no parent stack frames.
+  //
+  // Value offsets are determined when the corresponding variable is
+  // entered into the symbol_scope object corresponding to the frame.
+
+  class scope_stack_frame : public base_value_stack_frame
+  {
+  public:
+
+    scope_stack_frame (void) = delete;
+
+    scope_stack_frame (call_stack& cs, size_t prev, const symbol_scope& scope,
+                       stack_frame *static_link)
+      : base_value_stack_frame (cs, scope.num_symbols (), prev,
+                                static_link, nullptr),
+        m_scope (scope)
+    { }
+
+    scope_stack_frame (const stack_frame& elt) = delete;
+
+    scope_stack_frame& operator = (const stack_frame& elt) = delete;
+
+    ~scope_stack_frame (void) = default;
+
+    bool is_scope_frame (void) const { return true; }
+
+    symbol_scope get_scope (void) const { return m_scope; }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      return m_scope.lookup_symbol (name);
+    }
+
+    symbol_record insert_symbol (const std::string&);
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // The scope object associated with this stack frame.
+    symbol_scope m_scope;
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/syminfo-accumulator.h	Mon Jan 28 18:01:46 2019 +0000
@@ -0,0 +1,291 @@
+/*
+
+Copyright (C) 2018 John W. Eaton
+
+This file is part of Octave.
+
+Octave is free software: you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+Octave is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with Octave; see the file COPYING.  If not, see
+<https://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_syminfo_accumulator_h)
+#define octave_syminfo_accumulator_h 1
+
+#include "octave-config.h"
+
+#include <string>
+
+#include "oct-map.h"
+#include "ov.h"
+#include "stack-frame.h"
+#include "syminfo.h"
+#include "symrec.h"
+
+namespace octave
+{
+  class symbol_info_accumulator : public stack_frame_walker
+  {
+  public:
+
+    symbol_info_accumulator (const std::string& pattern,
+                             bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (pattern), m_match_all (false),
+        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
+        m_found_names ()
+    { }
+
+    symbol_info_accumulator (const string_vector& patterns,
+                             bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (patterns), m_match_all (false),
+        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
+        m_found_names ()
+    { }
+
+    symbol_info_accumulator (bool match_all = true, bool first_only = true)
+      : stack_frame_walker (), m_patterns (), m_match_all (match_all),
+        m_first_only (first_only), m_have_regexp (false),
+        m_sym_inf_list (), m_found_names ()
+    { }
+
+    symbol_info_accumulator (const symbol_info_accumulator&) = delete;
+
+    symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete;
+
+    ~symbol_info_accumulator (void) = default;
+
+    std::list<std::string> names (void) const
+    {
+      std::list<std::string> retval;
+
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          std::list<std::string> nm_list = lst.names ();
+
+          for (const auto& nm : nm_list)
+            retval.push_back (nm);
+        }
+
+      return retval;
+    }
+
+    symbol_info_list symbol_info (void) const
+    {
+      symbol_info_list retval;
+
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          for (const auto& syminf : lst)
+            retval.push_back (syminf);
+        }
+
+      return retval;
+    }
+
+    octave_map map_value (void) const
+    {
+      octave_map retval;
+
+      // FIXME: is there a better way to concatenate structures?
+
+      size_t n_frames = m_sym_inf_list.size ();
+
+      OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames);
+
+      size_t j = 0;
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          std::string scope_name = nm_sil.first;
+          const symbol_info_list& lst = nm_sil.second;
+
+          map_list[j] = lst.map_value (scope_name, n_frames-j);
+
+          j++;
+        }
+
+      return octave_map::cat (-1, n_frames, map_list);
+    }
+
+    void display (std::ostream& os, const std::string& format) const
+    {
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          os << "\nvariables in scope: " << nm_sil.first << "\n\n";
+
+          const symbol_info_list& lst = nm_sil.second;
+
+          lst.display (os, format);
+        }
+    }
+
+    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
+    {
+      // This one follows static link always.  Hmm, should the access
+      // link for a compiled_fcn_stack_frame be the same as the static
+      // link?
+
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+    void visit_script_stack_frame (script_stack_frame& frame)
+    {
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
+    {
+      append_list (frame);
+
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_scope_stack_frame (scope_stack_frame& frame)
+    {
+      append_list (frame);
+
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+  private:
+
+    typedef std::pair<std::string, symbol_info_list> syminf_list_elt;
+
+    // FIXME: the following is too complex and duplicates too much
+    // code.  Maybe it should be split up so we have separate classes
+    // that do each job that is needed?
+
+    std::list<symbol_record>
+    filter (stack_frame& frame, const std::list<symbol_record>& symbols)
+    {
+      std::list<symbol_record> new_symbols;
+
+      if (m_match_all)
+        {
+          for (const auto& sym : symbols)
+            {
+              if (frame.is_defined (sym))
+                {
+                  std::string name = sym.name ();
+
+                  if (m_first_only
+                      && m_found_names.find (name) != m_found_names.end ())
+                    continue;
+
+                  m_found_names.insert (name);
+
+                  new_symbols.push_back (sym);
+                }
+            }
+        }
+      else if (m_have_regexp)
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              regexp pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  std::string name = sym.name ();
+
+                  if (pat.is_match (name) && frame.is_defined (sym))
+                    {
+                      if (m_first_only
+                          && m_found_names.find (name) != m_found_names.end ())
+                        continue;
+
+                      m_found_names.insert (name);
+
+                      new_symbols.push_back (sym);
+                    }
+                }
+            }
+        }
+      else
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              glob_match pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  std::string name = sym.name ();
+
+                  if (pat.match (name) && frame.is_defined (sym))
+                    {
+                      if (m_first_only
+                          && m_found_names.find (name) == m_found_names.end ())
+                        continue;
+
+                      m_found_names.insert (name);
+
+                      new_symbols.push_back (sym);
+                    }
+                }
+            }
+        }
+
+      return new_symbols;
+    }
+
+    void append_list (stack_frame& frame)
+    {
+      symbol_scope scope = frame.get_scope ();
+
+      std::list<symbol_record> symbols = scope.symbol_list ();
+
+      if (m_match_all || ! m_patterns.empty ())
+        symbols = filter (frame, symbols);
+
+      symbol_info_list syminf_list = frame.make_symbol_info_list (symbols);
+
+      m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list));
+    }
+
+    string_vector m_patterns;
+
+    bool m_match_all;
+    bool m_first_only;
+    bool m_have_regexp;
+
+    std::list<std::pair<std::string, symbol_info_list>> m_sym_inf_list;
+
+    std::set<std::string> m_found_names;
+  };
+}
+
+#endif
--- a/libinterp/corefcn/symrec.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symrec.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -37,85 +37,25 @@
 #include "interpreter.h"
 #include "symrec.h"
 #include "symscope.h"
-#include "symtab.h"
 
 namespace octave
 {
-  symbol_record::context_id
-  symbol_record::symbol_record_rep::get_fwd_scope_context (void) const
-  {
-    // This should NOT recurse.  We only want to know the current
-    // context of the immediately forwarded rep object.  This is used
-    // only in symbol_record::symbol_record_rep::varref and
-    // symbol_record::symbol_record_rep::varval.
-
-    auto t_fwd_scope = m_fwd_scope.lock ();
-    return t_fwd_scope ? t_fwd_scope->current_context () : 0;
-  }
-
-  void
-  symbol_record::symbol_record_rep::init_persistent (void)
+  std::shared_ptr<symbol_record::symbol_record_rep>
+  symbol_record::symbol_record_rep::dup (void) const
   {
-    if (auto t_fwd_rep = m_fwd_rep.lock ())
-      {
-        t_fwd_rep->init_persistent ();
-        return;
-      }
-
-    mark_persistent ();
-  }
-
-  std::shared_ptr<symbol_record::symbol_record_rep>
-  symbol_record::symbol_record_rep::dup (const std::shared_ptr<symbol_scope_rep>& new_scope) const
-  {
-    // FIXME: is this the right thing do to?
-    if (auto t_fwd_rep = m_fwd_rep.lock ())
-      return t_fwd_rep->dup (new_scope);
-
-    static const context_id FIXME_CONTEXT = 0;
-
-    return std::shared_ptr<symbol_record_rep>
-      (new symbol_record_rep (m_name, varval (FIXME_CONTEXT),
-                              m_storage_class));
+    return std::shared_ptr<symbol_record::symbol_record_rep> (new symbol_record_rep (*this));
   }
 
   octave_value
-  symbol_record::symbol_record_rep::dump (context_id context) const
+  symbol_record::symbol_record_rep::dump (void) const
   {
-    if (auto t_fwd_rep = m_fwd_rep.lock ())
-      return t_fwd_rep->dump (context);
-
     std::map<std::string, octave_value> m
-      = {{ "name", m_name },
+      = {{ "frame_offset", m_frame_offset },
+         { "data_offset", m_data_offset },
+         { "name", m_name },
          { "local", is_local () },
-         { "automatic", is_automatic () },
-         { "formal", is_formal () },
-         { "hidden", is_hidden () },
-         { "inherited", is_inherited () },
-         { "global", is_global () },
-         { "persistent", is_persistent () }};
-
-    octave_value val = varval (context);
-
-    if (val.is_defined ())
-      m["value"] = val;
+         { "formal", is_formal () }};
 
     return octave_value (m);
   }
-
-  octave_value
-  symbol_record::find_function (const std::string& name,
-                                const octave_value_list& args) const
-  {
-    // FIXME: it would be better if the symbol_record object did not
-    // look back to the symbol_table when the value is undefined.  More
-    // refactoring is needed...
-
-    symbol_table& symtab
-      = __get_symbol_table__ ("symbol_record::find_function");
-
-    return symtab.find_function (name, args);
-  }
-
-  octave_value symbol_record::dummy_octave_value;
 }
--- a/libinterp/corefcn/symrec.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symrec.h	Mon Jan 28 18:01:46 2019 +0000
@@ -49,28 +49,12 @@
     // generic variable
     static const unsigned int local = 1;
 
-    // varargin, argn, .nargin., .nargout.
-    // (FIXME: is this really used now?)
-    static const unsigned int automatic = 2;
-
     // formal parameter
-    static const unsigned int formal = 4;
-
-    // not listed or cleared (.nargin., .nargout.)
-    static const unsigned int hidden = 8;
-
-    // inherited from parent scope; not cleared at function exit
-    static const unsigned int inherited = 16;
-
-    // global (redirects to global scope)
-    static const unsigned int global = 32;
-
-    // not cleared at function exit
-    static const unsigned int persistent = 64;
+    static const unsigned int formal = 2;
 
     // this symbol may NOT become a variable.
     // (symbol added to a static workspace)
-    static const unsigned int added_static = 128;
+    static const unsigned int added_static = 4;
 
   private:
 
@@ -78,464 +62,78 @@
     {
     public:
 
-      symbol_record_rep (const std::string& nm, const octave_value& v,
-                         unsigned int sc)
-        : m_storage_class (sc), m_name (nm), m_fwd_scope (),
-          m_fwd_rep (), m_value_stack ()
-      {
-        m_value_stack.push_back (v);
-      }
+      symbol_record_rep (const std::string& nm, unsigned int sc)
+        : m_frame_offset (0), m_data_offset (0), m_storage_class (sc),
+          m_name (nm)
+      { }
 
-      // No copying!
+      symbol_record_rep (const symbol_record_rep&) = default;
 
-      symbol_record_rep (const symbol_record_rep& ov) = delete;
-
-      symbol_record_rep& operator = (const symbol_record_rep&) = delete;
+      symbol_record_rep& operator = (const symbol_record_rep&) = default;
 
       ~symbol_record_rep (void) = default;
 
-      void assign (const octave_value& value, context_id context)
-      {
-        varref(context) = value;
-      }
-
-      void assign (octave_value::assign_op op,
-                   const std::string& type,
-                   const std::list<octave_value_list>& idx,
-                   const octave_value& value, context_id context)
-      {
-        varref(context).assign (op, type, idx, value);
-      }
-
-      void assign (octave_value::assign_op op, const octave_value& value,
-                   context_id context)
-      {
-        varref(context).assign (op, value);
-      }
-
-      void do_non_const_unary_op (octave_value::unary_op op,
-                                  context_id context)
-      {
-        varref(context).do_non_const_unary_op (op);
-      }
+      // FIXME: use special storage class instead?
+      bool is_valid (void) const { return ! m_name.empty (); }
 
-      void do_non_const_unary_op (octave_value::unary_op op,
-                                  const std::string& type,
-                                  const std::list<octave_value_list>& idx,
-                                  context_id context)
-      {
-        varref(context).do_non_const_unary_op (op, type, idx);
-      }
-
-      context_id get_fwd_scope_context (void) const;
-
-      octave_value& varref (context_id context)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->varref (get_fwd_scope_context ());
-
-        if (is_persistent ())
-          context = 0;
-
-        context_id n = m_value_stack.size ();
-        while (n++ <= context)
-          m_value_stack.push_back (octave_value ());
-
-        return m_value_stack[context];
-      }
-
-      octave_value varval (context_id context) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->varval (get_fwd_scope_context ());
-
-        if (is_persistent ())
-          context = 0;
-
-        if (context < m_value_stack.size ())
-          return m_value_stack[context];
-        else
-          return octave_value ();
-      }
+      void set_frame_offset (size_t offset) { m_frame_offset = offset; }
 
-      void push_context (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return;
-
-        if (! (is_persistent () || is_global ()))
-          m_value_stack.push_back (octave_value ());
-      }
-
-      // If pop_context returns 0, we are out of values and this element
-      // of the symbol table should be deleted.  This can happen for
-      // functions like
-      //
-      //   function foo (n)
-      //     if (n > 0)
-      //       foo (n-1);
-      //     else
-      //       eval ("x = 1");
-      //     endif
-      //   endfunction
-      //
-      // Here, X should only exist in the final stack frame.
-
-      context_id pop_context (void)
-      {
-        context_id retval = 1;
-
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return retval;
-
-        if (! (is_persistent () || is_global ()))
-          {
-            m_value_stack.pop_back ();
-            retval = m_value_stack.size ();
-          }
+      size_t frame_offset (void) const { return m_frame_offset; }
 
-        return retval;
-      }
-
-      void clear (context_id context)
-      {
-        // For globals, break the link to the global value first, then
-        // clear the local value.
-
-        if (is_global ())
-          unbind_global_rep ();
-
-        if (! (is_hidden () || is_inherited ()))
-          {
-            assign (octave_value (), context);
-
-            // For persistent values, we clear the value then unmark so
-            // that we clear the first element of the value stack.
+      void set_data_offset (size_t offset) { m_data_offset = offset; }
 
-            if (is_persistent ())
-              unmark_persistent ();
-          }
-      }
-
-      bool is_defined (context_id context) const
-      {
-        return varval (context).is_defined ();
-      }
-
-      bool is_variable (context_id context) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_variable (context);
-
-        return (! is_local () || is_defined (context));
-      }
+      size_t data_offset (void) const { return m_data_offset; }
 
       bool is_local (void) const
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_local ();
-
         return m_storage_class & local;
       }
 
-      bool is_automatic (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_automatic ();
-
-        return m_storage_class & automatic;
-      }
-
       bool is_formal (void) const
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_formal ();
-
         return m_storage_class & formal;
       }
 
-      bool is_hidden (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_hidden ();
-
-        return m_storage_class & hidden;
-      }
-
-      bool is_inherited (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_inherited ();
-
-        return m_storage_class & inherited;
-      }
-
-      bool is_forwarded (void) const
-      {
-        return ! m_fwd_rep.expired ();
-      }
-
-      bool is_global (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_global ();
-
-        return is_marked_global ();
-      }
-
-      bool is_persistent (void) const
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_persistent ();
-
-        return m_storage_class & persistent;
-      }
-
       bool is_added_static (void) const
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          return t_fwd_rep->is_added_static ();
-
         return m_storage_class & added_static;
       }
 
       void mark_local (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_local ();
-            return;
-          }
-
         m_storage_class |= local;
       }
 
-      void mark_automatic (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_automatic ();
-            return;
-          }
-
-        m_storage_class |= automatic;
-      }
-
       void mark_formal (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_formal ();
-            return;
-          }
-
         m_storage_class |= formal;
       }
 
-      void mark_hidden (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_hidden ();
-            return;
-          }
-
-        m_storage_class |= hidden;
-      }
-
-      void mark_inherited (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_inherited ();
-            return;
-          }
-
-        m_storage_class |= inherited;
-      }
-
-      // This flag should only be set for a symbol record that is
-      // actually in the global symbol_scope, and that should only
-      // happen when it is added to the global symbol_scope.
-
-      void mark_global (void)
-      {
-        m_storage_class |= global;
-      }
-
-      bool is_marked_global (void) const
-      {
-        return m_storage_class & global;
-      }
-
-      void mark_persistent (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_persistent ();
-            return;
-          }
-
-        if (is_global ())
-          error ("can't make global variable %s persistent", m_name.c_str ());
-
-        if (is_formal ())
-          error ("can't make function parameter %s persistent", m_name.c_str ());
-        m_storage_class |= persistent;
-      }
-
       void mark_added_static (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->mark_added_static ();
-            return;
-          }
-
         m_storage_class |= added_static;
       }
 
       void unmark_local (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_local ();
-            return;
-          }
-
         m_storage_class &= ~local;
       }
 
-      void unmark_automatic (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_automatic ();
-            return;
-          }
-
-        m_storage_class &= ~automatic;
-      }
-
       void unmark_formal (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_formal ();
-            return;
-          }
-
         m_storage_class &= ~formal;
       }
 
-      void unmark_hidden (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_hidden ();
-            return;
-          }
-
-        m_storage_class &= ~hidden;
-      }
-
-      void unmark_inherited (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_inherited ();
-            return;
-          }
-
-        m_storage_class &= ~inherited;
-      }
-
-      void unmark_persistent (void)
-      {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_persistent ();
-            return;
-          }
-
-        m_storage_class &= ~persistent;
-      }
-
       void unmark_added_static (void)
       {
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            t_fwd_rep->unmark_added_static ();
-            return;
-          }
-
         m_storage_class &= ~added_static;
       }
 
       unsigned int storage_class (void) const { return m_storage_class; }
 
-      void init_persistent (void);
-
-      void bind_fwd_rep (const std::shared_ptr<symbol_scope_rep>& fwd_scope,
-                         const std::shared_ptr<symbol_record_rep>& fwd_rep)
-      {
-        // If this object is already bound to another scope (for
-        // example, a variable in a script or nested function is bound
-        // to the enclosing scope), then bind that object to the next
-        // scope.  FIXME: can this happen for any other reason than we
-        // are making a variable in a script global?
-
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            // If this is the symbol in the global scope, then don't
-            // forward again!
-
-            if (t_fwd_rep->is_marked_global ())
-              return;
-
-            t_fwd_rep->bind_fwd_rep (fwd_scope, fwd_rep);
-          }
-
-        m_fwd_scope = fwd_scope;
-        m_fwd_rep = fwd_rep;
-      }
+      std::shared_ptr<symbol_record_rep> dup (void) const;
 
-      void unbind_fwd_rep (void)
-      {
-        // When unbinding variables in a script scope, we only break
-        // the immediate link.  By doing that, we ensure that any
-        // variables that are made global in a script remain linked as
-        // globals in the enclosing scope.
-
-        m_fwd_scope = std::weak_ptr<symbol_scope_rep> ();
-        m_fwd_rep.reset ();
-      }
-
-      void unbind_global_rep (void)
-      {
-        // Break the link to the global symbol_record_rep.  These must
-        // forwarded, so we don't do anything unless the forward rep
-        // points to something.
-
-        if (auto t_fwd_rep = m_fwd_rep.lock ())
-          {
-            if (t_fwd_rep->is_marked_global ())
-              {
-                // The rep this object points to is in the global
-                // scope, so delete the link to it.
-
-                m_fwd_scope = std::weak_ptr<symbol_scope_rep> ();
-                m_fwd_rep.reset ();
-              }
-            else
-              t_fwd_rep->unbind_global_rep ();
-          }
-      }
-
-      std::shared_ptr<symbol_record_rep>
-      dup (const std::shared_ptr<symbol_scope_rep>& new_scope) const;
-
-      octave_value dump (context_id context) const;
+      octave_value dump (void) const;
 
       std::string name (void) const { return m_name; }
 
@@ -543,23 +141,23 @@
 
     private:
 
+      size_t m_frame_offset;
+      size_t m_data_offset;
+
       unsigned int m_storage_class;
 
       std::string m_name;
-
-      std::weak_ptr<symbol_scope_rep> m_fwd_scope;
-
-      std::weak_ptr<symbol_record_rep> m_fwd_rep;
-
-      std::deque<octave_value> m_value_stack;
     };
 
   public:
 
-    symbol_record (const std::string& nm = "",
-                   const octave_value& v = octave_value (),
+    symbol_record (const std::string& nm = "", unsigned int sc = local)
+      : m_rep (new symbol_record_rep (nm, sc))
+    { }
+
+    symbol_record (const std::string& nm, const octave_value&,
                    unsigned int sc = local)
-      : m_rep (new symbol_record_rep (nm, v, sc))
+      : m_rep (new symbol_record_rep (nm, sc))
     { }
 
     symbol_record (const symbol_record&) = default;
@@ -568,132 +166,39 @@
 
     ~symbol_record (void) = default;
 
-    symbol_record dup (const std::shared_ptr<symbol_scope_rep>& sid) const
-    {
-      return symbol_record (m_rep->dup (sid));
-    }
+    bool is_valid (void) const { return m_rep->is_valid (); }
+
+    explicit operator bool () const { return is_valid (); }
+
+    void set_frame_offset (size_t offset) { m_rep->set_frame_offset (offset); }
+
+    size_t frame_offset (void) const { return m_rep->frame_offset (); }
+
+    void set_data_offset (size_t offset) { m_rep->set_data_offset (offset); }
+
+    size_t data_offset (void) const { return m_rep->data_offset (); }
+
+    symbol_record dup (void) const { return symbol_record (m_rep->dup ()); }
 
     std::string name (void) const { return m_rep->name (); }
 
     void rename (const std::string& new_name) { m_rep->rename (new_name); }
 
-    octave_value
-    find (context_id context,
-          const octave_value_list& args = octave_value_list ()) const
-    {
-      octave_value retval = varval (context);
-
-      if (retval.is_undefined ())
-        return find_function (name (), args);
-
-      return retval;
-    }
-
-    void assign (const octave_value& value, context_id context)
-    {
-      m_rep->assign (value, context);
-    }
-
-    void assign (octave_value::assign_op op,
-                 const std::string& type,
-                 const std::list<octave_value_list>& idx,
-                 const octave_value& value, context_id context)
-    {
-      m_rep->assign (op, type, idx, value, context);
-    }
-
-    void assign (octave_value::assign_op op, const octave_value& value,
-                 context_id context)
-    {
-      m_rep->assign (op, value, context);
-    }
-
-    void do_non_const_unary_op (octave_value::unary_op op, context_id context)
-    {
-      m_rep->do_non_const_unary_op (op, context);
-    }
-
-    void do_non_const_unary_op (octave_value::unary_op op,
-                                const std::string& type,
-                                const std::list<octave_value_list>& idx,
-                                context_id context)
-    {
-      m_rep->do_non_const_unary_op (op, type, idx, context);
-    }
-
-    octave_value varval (context_id context) const
-    {
-      return m_rep->varval (context);
-    }
-
-    void push_context (void) { m_rep->push_context (); }
-
-    context_id pop_context (void) { return m_rep->pop_context (); }
-
-    void clear (context_id context) { m_rep->clear (context); }
-
-    bool is_defined (context_id context) const
-    {
-      return m_rep->is_defined (context);
-    }
-
-    bool is_undefined (context_id context) const
-    {
-      return ! m_rep->is_defined (context);
-    }
-
-    bool is_variable (context_id context) const
-    {
-      return m_rep->is_variable (context);
-    }
-
     bool is_local (void) const { return m_rep->is_local (); }
-    bool is_automatic (void) const { return m_rep->is_automatic (); }
     bool is_formal (void) const { return m_rep->is_formal (); }
-    bool is_global (void) const { return m_rep->is_global (); }
-    bool is_hidden (void) const { return m_rep->is_hidden (); }
-    bool is_inherited (void) const { return m_rep->is_inherited (); }
-    bool is_forwarded (void) const { return m_rep->is_forwarded (); }
-    bool is_persistent (void) const { return m_rep->is_persistent (); }
     bool is_added_static (void) const { return m_rep->is_added_static (); }
 
     void mark_local (void) { m_rep->mark_local (); }
-    void mark_automatic (void) { m_rep->mark_automatic (); }
     void mark_formal (void) { m_rep->mark_formal (); }
-    void mark_hidden (void) { m_rep->mark_hidden (); }
-    void mark_inherited (void) { m_rep->mark_inherited (); }
-    void mark_persistent (void) { m_rep->mark_persistent (); }
     void mark_added_static (void) { m_rep->mark_added_static (); }
 
     void unmark_local (void) { m_rep->unmark_local (); }
-    void unmark_automatic (void) { m_rep->unmark_automatic (); }
     void unmark_formal (void) { m_rep->unmark_formal (); }
-    void unmark_hidden (void) { m_rep->unmark_hidden (); }
-    void unmark_inherited (void) { m_rep->unmark_inherited (); }
-    void unmark_persistent (void) { m_rep->unmark_persistent (); }
     void unmark_added_static (void) { m_rep->unmark_added_static (); }
 
-    bool is_marked_global (void) const { return m_rep->is_marked_global (); }
-    void mark_global (void) { m_rep->mark_global (); }
-
-    void init_persistent (void) { m_rep->init_persistent (); }
-
     unsigned int storage_class (void) const { return m_rep->storage_class (); }
 
-    void bind_fwd_rep (const std::shared_ptr<symbol_scope_rep>& fwd_scope,
-                       const symbol_record& sr)
-    {
-      m_rep->bind_fwd_rep (fwd_scope, sr.m_rep);
-    }
-
-    void unbind_global_rep (void) { m_rep->unbind_global_rep (); }
-
-    void unbind_fwd_rep (void) { m_rep->unbind_fwd_rep (); }
-
-    octave_value dump (context_id context) const
-    {
-      return m_rep->dump (context);
-    }
+    octave_value dump (void) const { return m_rep->dump (); }
 
   private:
 
@@ -703,11 +208,6 @@
     symbol_record (const std::shared_ptr<symbol_record_rep>& new_rep)
       : m_rep (new_rep)
     { }
-
-    octave_value find_function (const std::string& name,
-                                const octave_value_list& args) const;
-
-    static octave_value dummy_octave_value;
   };
 }
 
--- a/libinterp/corefcn/symscope.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symscope.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -34,66 +34,30 @@
 #include "ov-usr-fcn.h"
 #include "symrec.h"
 #include "symscope.h"
-#include "symtab.h"
 #include "utils.h"
 
 namespace octave
 {
-  void symbol_scope_rep::install_auto_fcn_vars (void)
+  symbol_record symbol_scope_rep::insert_local (const std::string& name)
   {
-    install_auto_fcn_var (".argn.");
-    install_auto_fcn_var (".ignored.");
-    install_auto_fcn_var (".nargin.");
-    install_auto_fcn_var (".nargout.");
-    install_auto_fcn_var (".saved_warning_states.");
-  }
+    symbol_record sym (name);
 
-  void symbol_scope_rep::install_auto_fcn_var (const std::string& name)
-  {
-    insert (name, true);
-    mark_hidden (name);
-    mark_automatic (name);
+    insert_symbol_record (sym);
+
+    return sym;
   }
 
-  octave_value
-  symbol_scope_rep::find (const std::string& name)
+  void symbol_scope_rep::insert_symbol_record (symbol_record& sr)
   {
-    symbol_table& symtab
-      = __get_symbol_table__ ("symbol_scope_rep::find");
-
-    // Variable.
-
-    table_iterator p = m_symbols.find (name);
-
-    if (p != m_symbols.end ())
-      {
-        symbol_record sr = p->second;
+    size_t data_offset = num_symbols ();
+    std::string name = sr.name ();
 
-        if (sr.is_global ())
-          return symtab.global_varval (name);
-        else
-          {
-            octave_value val = sr.varval (m_context);
-
-            if (val.is_defined ())
-              return val;
-          }
-      }
+    sr.set_data_offset (data_offset);
 
-    // Subfunction.  I think it only makes sense to check for
-    // subfunctions if we are currently executing a function defined
-    // from a .m file.
-
-    octave_value fcn = find_subfunction (name);
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    return symtab.fcn_table_find (name, ovl ());
+    m_symbols[name] = sr;
   }
 
-  symbol_record&
-  symbol_scope_rep::insert (const std::string& name, bool force_add)
+  symbol_record symbol_scope_rep::insert (const std::string& name)
   {
     table_iterator p = m_symbols.find (name);
 
@@ -101,13 +65,20 @@
       {
         symbol_record ret (name);
 
+        size_t data_offset = num_symbols ();
+
+        ret.set_data_offset (data_offset);
+
         auto t_parent = m_parent.lock ();
 
-        if (is_nested () && t_parent && t_parent->look_nonlocal (name, ret))
+        size_t offset = 0;
+
+        if (is_nested () && t_parent
+            && t_parent->look_nonlocal (name, offset, ret))
           return m_symbols[name] = ret;
         else
           {
-            if (m_is_static && ! force_add)
+            if (m_is_static)
               ret.mark_added_static ();
 
             return m_symbols[name] = ret;
@@ -139,13 +110,23 @@
     for (const auto& nm_sr : m_symbols)
       {
         std::string nm = nm_sr.first;
-        const symbol_record& sr = nm_sr.second;
-        info_map[nm] = sr.dump (m_context);
+        symbol_record sr = nm_sr.second;
+        info_map[nm] = sr.dump ();
       }
 
     return octave_value (info_map);
   }
 
+  std::list<symbol_record> symbol_scope_rep::symbol_list (void) const
+  {
+    std::list<symbol_record> retval;
+
+    for (const auto& nm_sr : m_symbols)
+      retval.push_back (nm_sr.second);
+
+    return retval;
+  }
+
   octave_value
   symbol_scope_rep::find_subfunction (const std::string& name) const
   {
@@ -194,18 +175,30 @@
         // Since is_nested is true, the following should always return a
         // valid scope.
 
+        auto t_parent = m_parent.lock ();
+
+        if (t_parent)
+          {
+            // SCOPE is the parent of this scope: this scope is a child
+            // of SCOPE.
+
+            if (t_parent == scope)
+              return true;
+          }
+
         auto t_primary_parent = m_primary_parent.lock ();
 
         if (t_primary_parent)
           {
             // SCOPE is the primary parent of this scope: this scope is a
-            // child of SCOPE.
+            // child (or grandchild) of SCOPE.
             if (t_primary_parent == scope)
               return true;
 
             // SCOPE and this scope share the same primary parent: they are
-            // siblings.
-            if (t_primary_parent == scope->primary_parent_scope_rep ())
+            // siblings (or cousins)
+            auto scope_primary_parent = scope->primary_parent_scope_rep ();
+            if (t_primary_parent == scope_primary_parent)
               return true;
           }
       }
@@ -213,8 +206,7 @@
     return false;
   }
 
-  void
-  symbol_scope_rep::update_nest (void)
+  void symbol_scope_rep::update_nest (void)
   {
     auto t_parent = m_parent.lock ();
 
@@ -225,12 +217,10 @@
           {
             symbol_record& ours = nm_sr.second;
 
-            if (! ours.is_formal ()
-                && is_nested () && t_parent->look_nonlocal (nm_sr.first, ours))
-              {
-                if (ours.is_global () || ours.is_persistent ())
-                  error ("global and persistent may only be used in the topmost level in which a nested variable is used");
-              }
+            size_t offset = 0;
+
+            if (! ours.is_formal () && is_nested ())
+              t_parent->look_nonlocal (nm_sr.first, offset, ours);
           }
 
         // The scopes of nested functions are static.
@@ -247,40 +237,37 @@
       scope_obj.update_nest ();
   }
 
-  bool
-  symbol_scope_rep::look_nonlocal (const std::string& name,
-                                   symbol_record& result)
+  bool symbol_scope_rep::look_nonlocal (const std::string& name,
+                                        size_t offset, symbol_record& result)
   {
+    offset++;
+
     table_iterator p = m_symbols.find (name);
+
     if (p == m_symbols.end ())
       {
         auto t_parent = m_parent.lock ();
 
         if (is_nested () && t_parent)
-          return t_parent->look_nonlocal (name, result);
+          return t_parent->look_nonlocal (name, offset, result);
       }
-    else if (! p->second.is_automatic ())
+    else
       {
-        result.bind_fwd_rep (shared_from_this (), p->second);
+        // Add scope offsets because the one we found may be used in
+        // this scope but initially from another parent scope beyond
+        // that.  The parent offset will already point to the first
+        // occurrence because we do the overall nesting update from the
+        // parent function down through the lists of all children.
+
+        size_t t_frame_offset = offset + p->second.frame_offset ();
+        size_t t_data_offset = p->second.data_offset ();
+
+        result.set_frame_offset (t_frame_offset);
+        result.set_data_offset (t_data_offset);
+
         return true;
       }
 
     return false;
   }
-
-  void
-  symbol_scope_rep::bind_script_symbols
-    (const std::shared_ptr<symbol_scope_rep>& curr_scope)
-  {
-    for (auto& nm_sr : m_symbols)
-      nm_sr.second.bind_fwd_rep (curr_scope,
-                                 curr_scope->find_symbol (nm_sr.first));
-  }
-
-  void
-  symbol_scope_rep::unbind_script_symbols (void)
-  {
-    for (auto& nm_sr : m_symbols)
-      nm_sr.second.unbind_fwd_rep ();
-  }
 }
--- a/libinterp/corefcn/symscope.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symscope.h	Mon Jan 28 18:01:46 2019 +0000
@@ -64,10 +64,15 @@
     subfunctions_iterator;
 
     symbol_scope_rep (const std::string& name = "")
-      : m_name (name), m_symbols (), m_subfunctions (), m_fcn (nullptr),
-        m_parent (), m_primary_parent (), m_children (),
-        m_nesting_depth (0), m_is_static (false), m_context (0)
-    { }
+      : m_name (name), m_symbols (), m_subfunctions (),
+        m_persistent_values (), m_fcn (nullptr), m_parent (),
+        m_primary_parent (), m_children (), m_nesting_depth (0),
+        m_is_static (false)
+    {
+      // All scopes have ans as the first symbol, initially undefined.
+
+      insert_local ("ans");
+    }
 
     // No copying!
 
@@ -77,14 +82,13 @@
 
     ~symbol_scope_rep (void) = default;
 
-    void insert_symbol_record (const symbol_record& sr)
-    {
-      m_symbols[sr.name ()] = sr;
-    }
+    size_t num_symbols (void) const { return m_symbols.size (); }
+
+    // Simply inserts symbol.  No non-local searching.
 
-    void install_auto_fcn_vars (void);
+    symbol_record insert_local (const std::string& name);
 
-    void install_auto_fcn_var (const std::string& name);
+    void insert_symbol_record (symbol_record& sr);
 
     bool is_nested (void) const { return m_nesting_depth > 0; }
 
@@ -111,23 +115,34 @@
       std::shared_ptr<symbol_scope_rep> new_sid
         = std::shared_ptr<symbol_scope_rep> (new symbol_scope_rep (m_name));
 
+      new_sid->m_name;
+
       for (const auto& nm_sr : m_symbols)
-        new_sid->insert_symbol_record (nm_sr.second.dup (new_sid));
+        new_sid->m_symbols[nm_sr.first] = nm_sr.second.dup ();
 
+      new_sid->m_subfunctions = m_subfunctions;
+      new_sid->m_persistent_values = m_persistent_values;
+      new_sid->m_subfunction_names = m_subfunction_names;
+      new_sid->m_fcn = m_fcn;
       new_sid->m_parent = m_parent;
       new_sid->m_primary_parent = m_primary_parent;
+      new_sid->m_children = m_children;
+      new_sid->m_nesting_depth = m_nesting_depth;
+      new_sid->m_is_static = m_is_static;
 
       return new_sid;
     }
 
-    void set_context (symbol_record::context_id context)
+    octave_value& persistent_varref (size_t data_offset)
     {
-      m_context = context;
+      return m_persistent_values[data_offset];
     }
 
-    symbol_record::context_id current_context (void) const
+    octave_value persistent_varval (size_t data_offset) const
     {
-      return m_context;
+      auto p = m_persistent_values.find (data_offset);
+
+      return p == m_persistent_values.end () ? octave_value () : p->second;
     }
 
     symbol_record find_symbol (const std::string& name)
@@ -140,51 +155,14 @@
         return p->second;
     }
 
-    void inherit_internal
-      (const std::shared_ptr<symbol_scope_rep>& donor_scope_rep)
+    symbol_record lookup_symbol (const std::string& name) const
     {
-      for (auto& nm_sr : m_symbols)
-        {
-          symbol_record& sr = nm_sr.second;
-
-          if (! (sr.is_automatic () || sr.is_formal ()))
-            {
-              std::string nm = sr.name ();
+      auto p = m_symbols.find (name);
 
-              if (nm != "__retval__")
-                {
-                  octave_value val = donor_scope_rep->varval (nm);
-
-                  if (val.is_defined ())
-                    {
-                      sr.assign (val, m_context);
-
-                      sr.mark_inherited ();
-                    }
-                }
-            }
-        }
+      return p == m_symbols.end () ? symbol_record () : p->second;
     }
 
-    void inherit (const std::shared_ptr<symbol_scope_rep>& donor_scope_rep)
-    {
-      std::shared_ptr<symbol_scope_rep> dsr = donor_scope_rep;
-
-      while (dsr)
-        {
-          inherit_internal (dsr);
-
-          if (dsr->is_nested ())
-            dsr = parent_scope_rep ();
-          else
-            break;
-        }
-    }
-
-    octave_value find (const std::string& name);
-
-    symbol_record&
-    insert (const std::string& name, bool force_add = false);
+    symbol_record insert (const std::string& name);
 
     void rename (const std::string& old_name, const std::string& new_name)
     {
@@ -202,289 +180,6 @@
         }
     }
 
-    void assign (const std::string& name, const octave_value& value,
-                 bool force_add)
-    {
-      auto p = m_symbols.find (name);
-
-      if (p == m_symbols.end ())
-        {
-          symbol_record& sr = insert (name, force_add);
-
-          sr.assign (value, m_context);
-        }
-      else
-        p->second.assign (value, m_context);
-    }
-
-    void assign (const std::string& name,
-                 const octave_value& value = octave_value ())
-    {
-      assign (name, value, false);
-    }
-
-    void force_assign (const std::string& name, const octave_value& value)
-    {
-      auto p = m_symbols.find (name);
-
-      if (p == m_symbols.end ())
-        {
-          symbol_record& sr = insert (name, true);
-
-          sr.assign (value, m_context);
-        }
-      else
-        p->second.assign (value, m_context);
-    }
-
-    octave_value varval (const std::string& name) const
-    {
-      table_const_iterator p = m_symbols.find (name);
-
-      return (p != m_symbols.end ()
-              ? p->second.varval (m_context) : octave_value ());
-    }
-
-    bool is_variable (const std::string& name) const
-    {
-      bool retval = false;
-
-      table_const_iterator p = m_symbols.find (name);
-
-      if (p != m_symbols.end ())
-        {
-          const symbol_record& sr = p->second;
-
-          retval = sr.is_variable (m_context);
-        }
-
-      return retval;
-    }
-
-    void push_context (void)
-    {
-      for (auto& nm_sr : m_symbols)
-        nm_sr.second.push_context ();
-    }
-
-    void pop_context (void)
-    {
-      auto tbl_it = m_symbols.begin ();
-
-      while (tbl_it != m_symbols.end ())
-        {
-          if (tbl_it->second.pop_context () == 0)
-            m_symbols.erase (tbl_it++);
-          else
-            tbl_it++;
-        }
-    }
-
-    void refresh (void)
-    {
-      for (auto& nm_sr : m_symbols)
-        {
-          symbol_record& sr = nm_sr.second;
-
-          if (sr.is_global ())
-            sr.unbind_global_rep ();
-          else if (! (sr.is_persistent () || sr.is_forwarded ()))
-            sr.clear (m_context);
-        }
-    }
-
-    void clear_variables (void)
-    {
-      for (auto& nm_sr : m_symbols)
-        nm_sr.second.clear (m_context);
-    }
-
-    void clear_objects (void)
-    {
-      for (auto& nm_sr : m_symbols)
-        {
-          symbol_record& sr = nm_sr.second;
-          octave_value val = sr.varval (m_context);
-          if (val.isobject ())
-            nm_sr.second.clear (m_context);
-        }
-    }
-
-    void clear_variable (const std::string& name)
-    {
-      auto p = m_symbols.find (name);
-
-      if (p != m_symbols.end ())
-        p->second.clear (m_context);
-      else if (is_nested ())
-        {
-          std::shared_ptr<symbol_scope_rep> psr = parent_scope_rep ();
-
-          if (psr)
-            psr->clear_variable (name);
-        }
-    }
-
-    void clear_variable_pattern (const std::string& pat)
-    {
-      glob_match pattern (pat);
-
-      for (auto& nm_sr : m_symbols)
-        {
-          symbol_record& sr = nm_sr.second;
-
-          if (sr.is_defined (m_context) || sr.is_global ())
-            {
-              if (pattern.match (sr.name ()))
-                sr.clear (m_context);
-            }
-        }
-
-      if (is_nested ())
-        {
-          std::shared_ptr<symbol_scope_rep> psr = parent_scope_rep ();
-
-          if (psr)
-            psr->clear_variable_pattern (pat);
-        }
-    }
-
-    void clear_variable_regexp (const std::string& pat)
-    {
-      octave::regexp pattern (pat);
-
-      for (auto& nm_sr : m_symbols)
-        {
-          symbol_record& sr = nm_sr.second;
-
-          if (sr.is_defined (m_context) || sr.is_global ())
-            {
-              if (pattern.is_match (sr.name ()))
-                sr.clear (m_context);
-            }
-        }
-
-      if (is_nested ())
-        {
-          std::shared_ptr<symbol_scope_rep> psr = parent_scope_rep ();
-
-          if (psr)
-            psr->clear_variable_regexp (pat);
-        }
-    }
-
-    void mark_automatic (const std::string& name)
-    {
-      insert (name).mark_automatic ();
-    }
-
-    void mark_hidden (const std::string& name)
-    {
-      insert (name).mark_hidden ();
-    }
-
-    void mark_global (const std::string& name)
-    {
-      insert (name).mark_global ();
-    }
-
-    std::list<symbol_record>
-    all_variables (bool defined_only = true,
-                   unsigned int exclude = symbol_record::hidden) const
-    {
-      std::list<symbol_record> retval;
-
-      for (const auto& nm_sr : m_symbols)
-        {
-          const symbol_record& sr = nm_sr.second;
-
-          if ((defined_only && ! sr.is_defined (m_context))
-              || (sr.storage_class () & exclude))
-            continue;
-
-          retval.push_back (sr);
-        }
-
-      return retval;
-    }
-
-    std::list<symbol_record>
-    glob (const std::string& pattern, bool vars_only = false) const
-    {
-      std::list<symbol_record> retval;
-
-      glob_match pat (pattern);
-
-      for (const auto& nm_sr : m_symbols)
-        {
-          if (pat.match (nm_sr.first))
-            {
-              const symbol_record& sr = nm_sr.second;
-
-              if (vars_only && ! sr.is_variable (m_context))
-                continue;
-
-              retval.push_back (sr);
-            }
-        }
-
-      return retval;
-    }
-
-    std::list<symbol_record>
-    regexp (const std::string& pattern, bool vars_only = false) const
-    {
-      std::list<symbol_record> retval;
-
-      octave::regexp pat (pattern);
-
-      for (const auto& nm_sr : m_symbols)
-        {
-          if (pat.is_match (nm_sr.first))
-            {
-              const symbol_record& sr = nm_sr.second;
-
-              if (vars_only && ! sr.is_variable (m_context))
-                continue;
-
-              retval.push_back (sr);
-            }
-        }
-
-      return retval;
-    }
-
-    std::list<std::string> variable_names (void)
-    {
-      std::list<std::string> retval;
-
-      for (const auto& nm_sr : m_symbols)
-        {
-          if (nm_sr.second.is_variable (m_context))
-            retval.push_back (nm_sr.first);
-        }
-
-      retval.sort ();
-
-      return retval;
-    }
-
-    bool is_local_variable (const std::string& name) const
-    {
-      table_const_iterator p = m_symbols.find (name);
-
-      return (p != m_symbols.end ()
-              && ! p->second.is_global ()
-              && p->second.is_defined (m_context));
-    }
-
-    bool is_global (const std::string& name) const
-    {
-      table_const_iterator p = m_symbols.find (name);
-
-      return p != m_symbols.end () && p->second.is_global ();
-    }
-
     void install_subfunction (const std::string& name,
                               const octave_value& fval)
     {
@@ -559,14 +254,23 @@
 
     void update_nest (void);
 
-    bool look_nonlocal (const std::string& name, symbol_record& result);
-
-    void bind_script_symbols (const std::shared_ptr<symbol_scope_rep>& curr_scope);
-
-    void unbind_script_symbols (void);
+    bool look_nonlocal (const std::string& name, size_t offset,
+                        symbol_record& result);
 
     octave_value dump_symbols_map (void) const;
 
+    const std::map<std::string, symbol_record>& symbols (void) const
+    {
+      return m_symbols;
+    }
+
+    std::map<std::string, symbol_record>& symbols (void)
+    {
+      return m_symbols;
+    }
+
+    std::list<symbol_record> symbol_list (void) const;
+
   private:
 
     //! Name for this scope (usually the corresponding filename of the
@@ -582,6 +286,9 @@
 
     std::map<std::string, octave_value> m_subfunctions;
 
+    //! Map from data offset to persistent values in this scope.
+    std::map<size_t, octave_value> m_persistent_values;
+
     //! The list of subfunctions (if any) in the order they appear in
     //! the function file.
 
@@ -611,8 +318,6 @@
     //! If true then no variables can be added.
 
     bool m_is_static;
-
-    symbol_record::context_id m_context;
   };
 
   class symbol_scope
@@ -640,18 +345,22 @@
 
     explicit operator bool () const { return bool (m_rep); }
 
-    void insert_symbol_record (const symbol_record& sr)
+    size_t num_symbols (void) const
+    {
+      return m_rep ? m_rep->num_symbols () : 0;
+    }
+
+    symbol_record insert_local (const std::string& name)
+    {
+      return m_rep ? m_rep->insert_local (name) : symbol_record ();
+    }
+
+    void insert_symbol_record (symbol_record& sr)
     {
       if (m_rep)
         m_rep->insert_symbol_record (sr);
     }
 
-    void install_auto_fcn_vars (void)
-    {
-      if (m_rep)
-        m_rep->install_auto_fcn_vars ();
-    }
-
     bool is_nested (void) const
     {
       return m_rep ? m_rep->is_nested () : false;
@@ -694,15 +403,16 @@
       return symbol_scope (m_rep ? m_rep->dup () : nullptr);
     }
 
-    void set_context (symbol_record::context_id context)
+    octave_value& persistent_varref (size_t data_offset)
     {
-      if (m_rep)
-        m_rep->set_context (context);
+      static octave_value dummy_value;
+
+      return m_rep ? m_rep->persistent_varref (data_offset) : dummy_value;
     }
 
-    symbol_record::context_id current_context (void) const
+    octave_value persistent_varval (size_t data_offset) const
     {
-      return m_rep ? m_rep->current_context () : 0;
+      return m_rep ? m_rep->persistent_varval (data_offset) : octave_value ();
     }
 
     symbol_record find_symbol (const std::string& name)
@@ -710,22 +420,15 @@
       return m_rep ? m_rep->find_symbol (name) : symbol_record ();
     }
 
-    void inherit (const symbol_scope& donor_scope)
+    // Like find_symbol, but does not insert.
+    symbol_record lookup_symbol (const std::string& name) const
     {
-      if (m_rep)
-        m_rep->inherit (donor_scope.get_rep ());
+      return m_rep ? m_rep->lookup_symbol (name) : symbol_record ();
     }
 
-    octave_value find (const std::string& name)
+    symbol_record insert (const std::string& name)
     {
-      return m_rep ? m_rep->find (name) : octave_value ();
-    }
-
-    symbol_record&
-    insert (const std::string& name, bool force_add = false)
-    {
-      static symbol_record dummy_symrec;
-      return m_rep ? m_rep->insert (name, force_add) : dummy_symrec;
+      return m_rep ? m_rep->insert (name) : symbol_record ();
     }
 
     void rename (const std::string& old_name, const std::string& new_name)
@@ -734,146 +437,6 @@
         m_rep->rename (old_name, new_name);
     }
 
-    void assign (const std::string& name, const octave_value& value,
-                 bool force_add)
-    {
-      if (m_rep)
-        m_rep->assign (name, value, force_add);
-    }
-
-    void assign (const std::string& name,
-                 const octave_value& value = octave_value ())
-    {
-      if (m_rep)
-        m_rep->assign (name, value);
-    }
-
-    void force_assign (const std::string& name, const octave_value& value)
-    {
-      if (m_rep)
-        m_rep->force_assign (name, value);
-    }
-
-    octave_value varval (const std::string& name) const
-    {
-      return m_rep ? m_rep->varval (name) : octave_value ();
-    }
-
-    bool is_variable (const std::string& name) const
-    {
-      return m_rep ? m_rep->is_variable (name) : false;
-    }
-
-    void push_context (void)
-    {
-      if (m_rep)
-        m_rep->push_context ();
-    }
-
-    void pop_context (void)
-    {
-      if (m_rep)
-        m_rep->pop_context ();
-    }
-
-    void refresh (void)
-    {
-      if (m_rep)
-        m_rep->refresh ();
-    }
-
-    void clear_variables (void)
-    {
-      if (m_rep)
-        m_rep->clear_variables ();
-    }
-
-    void clear_objects (void)
-    {
-      if (m_rep)
-        m_rep->clear_objects ();
-    }
-
-    void clear_variable (const std::string& name)
-    {
-      if (m_rep)
-        m_rep->clear_variable (name);
-    }
-
-    void clear_variable_pattern (const std::string& pat)
-    {
-      if (m_rep)
-        m_rep->clear_variable_pattern (pat);
-    }
-
-    void clear_variable_regexp (const std::string& pat)
-    {
-      if (m_rep)
-        m_rep->clear_variable_regexp (pat);
-    }
-
-    void mark_automatic (const std::string& name)
-    {
-      if (m_rep)
-        m_rep->mark_automatic (name);
-    }
-
-    void mark_hidden (const std::string& name)
-    {
-      if (m_rep)
-        m_rep->mark_hidden (name);
-    }
-
-    // This function should only be called for the global
-    // symbol_scope, and that should only happen when it is added to
-    // the global symbol_scope.
-
-    void mark_global (const std::string& name)
-    {
-      if (m_rep)
-        m_rep->mark_global (name);
-    }
-
-    std::list<symbol_record>
-    all_variables (bool defined_only = true,
-                   unsigned int exclude = symbol_record::hidden) const
-    {
-      return (m_rep
-              ? m_rep->all_variables (defined_only, exclude)
-              : std::list<symbol_record> ());
-    }
-
-    std::list<symbol_record>
-    glob (const std::string& pattern, bool vars_only = false) const
-    {
-      return (m_rep
-              ? m_rep->glob (pattern, vars_only)
-              : std::list<symbol_record> ());
-    }
-
-    std::list<symbol_record>
-    regexp (const std::string& pattern, bool vars_only = false) const
-    {
-      return (m_rep
-              ? m_rep->regexp (pattern, vars_only)
-              : std::list<symbol_record> ());
-    }
-
-    std::list<std::string> variable_names (void)
-    {
-      return m_rep ? m_rep->variable_names () : std::list<std::string> ();
-    }
-
-    bool is_local_variable (const std::string& name) const
-    {
-      return m_rep ? m_rep->is_local_variable (name) : false;
-    }
-
-    bool is_global (const std::string& name) const
-    {
-      return m_rep ? m_rep->is_global (name) : false;
-    }
-
     void install_subfunction (const std::string& name,
                               const octave_value& fval)
     {
@@ -991,21 +554,10 @@
         m_rep->update_nest ();
     }
 
-    bool look_nonlocal (const std::string& name, symbol_record& result)
-    {
-      return m_rep ? m_rep->look_nonlocal (name, result) : false;
-    }
-
-    void bind_script_symbols (const symbol_scope& curr_scope)
+    bool look_nonlocal (const std::string& name, size_t offset,
+                        symbol_record& result)
     {
-      if (m_rep)
-        m_rep->bind_script_symbols (curr_scope.get_rep ());
-    }
-
-    void unbind_script_symbols (void)
-    {
-      if (m_rep)
-        m_rep->unbind_script_symbols ();
+      return m_rep ? m_rep->look_nonlocal (name, offset, result) : false;
     }
 
     std::shared_ptr<symbol_scope_rep> get_rep (void) const
@@ -1023,14 +575,30 @@
       return a.m_rep != b.m_rep;
     }
 
+    const std::map<std::string, symbol_record>& symbols (void) const
+    {
+      static const std::map<std::string, symbol_record> empty_map;
+
+      return m_rep ? m_rep->symbols () : empty_map;
+    }
+
+    std::map<std::string, symbol_record>& symbols (void)
+    {
+      static std::map<std::string, symbol_record> empty_map;
+
+      return m_rep ? m_rep->symbols () : empty_map;
+    }
+
+    std::list<symbol_record> symbol_list (void) const
+    {
+      static const std::list<symbol_record> empty_list;
+
+      return m_rep ? m_rep->symbol_list () : empty_list;
+    }
+
   private:
 
     std::shared_ptr<symbol_scope_rep> m_rep;
-
-    octave_value dump_symbols_map (void) const
-    {
-      return m_rep ? m_rep->dump_symbols_map () : octave_value ();
-    }
   };
 }
 
--- a/libinterp/corefcn/symtab.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symtab.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -25,6 +25,8 @@
 #  include "config.h"
 #endif
 
+#include <iostream>
+
 #include <sstream>
 
 #include "file-ops.h"
@@ -33,6 +35,7 @@
 #include "oct-time.h"
 
 #include "bp-table.h"
+#include "call-stack.h"
 #include "defun.h"
 #include "dirfns.h"
 #include "fcn-info.h"
@@ -57,9 +60,566 @@
 
 namespace octave
 {
-  static void
-  split_name_with_package (const std::string& name, std::string& fname,
-                           std::string& pname)
+  symbol_table::symbol_table (interpreter& interp)
+    : m_interpreter (interp), m_fcn_table (), m_class_precedence_table (),
+      m_parent_map ()
+  {
+    install_builtins ();
+  }
+
+  symbol_scope symbol_table::current_scope (void) const
+  {
+    tree_evaluator& tw = m_interpreter.get_evaluator ();
+
+    return tw.get_current_scope ();
+  }
+
+  bool symbol_table::is_built_in_function_name (const std::string& name)
+  {
+    octave_value val = find_built_in_function (name);
+
+    return val.is_defined ();
+  }
+
+  // FIXME: this function only finds legacy class methods, not
+  // classdef methods.
+
+  octave_value symbol_table::find_method (const std::string& name,
+                                          const std::string& dispatch_type)
+  {
+    fcn_table_const_iterator p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      return p->second.find_method (dispatch_type);
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find_method (dispatch_type);
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
+  }
+
+  octave_value symbol_table::find_built_in_function (const std::string& name)
+  {
+    fcn_table_const_iterator p = m_fcn_table.find (name);
+
+    return (p != m_fcn_table.end ()
+            ? p->second.find_built_in_function () : octave_value ());
+  }
+
+  octave_value symbol_table::find_autoload (const std::string& name)
+  {
+    auto p = m_fcn_table.find (name);
+
+    return (p != m_fcn_table.end ()
+            ? p->second.find_autoload () : octave_value ());
+  }
+
+  octave_value symbol_table::builtin_find (const std::string& name)
+  {
+    fcn_table_iterator p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      return p->second.builtin_find ();
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.builtin_find ();
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
+
+    return octave_value ();
+  }
+
+  octave_value symbol_table::fcn_table_find (const std::string& name,
+                                             const octave_value_list& args)
+  {
+    fcn_table_iterator p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      return p->second.find (args);
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find (args);
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
+
+    return octave_value ();
+  }
+
+  octave_value symbol_table::find_function (const std::string& name)
+  {
+    if (name.empty ())
+      return octave_value ();
+
+    if (name[0] == '@')
+      {
+        size_t pos = name.find_first_of ('/');
+
+        if (pos == std::string::npos)
+          return octave_value ();
+
+        std::string method = name.substr (pos+1);
+        std::string dispatch_type = name.substr (1, pos-1);
+
+        return find_method (method, dispatch_type);
+      }
+    else
+      return find_function (name, ovl ());
+  }
+
+  octave_value symbol_table::find_function (const std::string& name,
+                                            const octave_value_list& args)
+  {
+    octave_value fcn;
+
+    symbol_scope curr_scope = current_scope ();
+
+    if (curr_scope)
+      {
+        fcn = curr_scope.find_subfunction (name);
+
+        if (fcn.is_defined ())
+          return fcn;
+      }
+
+    return fcn_table_find (name, args);
+  }
+
+  octave_value symbol_table::find_user_function (const std::string& name)
+  {
+    auto p = m_fcn_table.find (name);
+
+    return (p != m_fcn_table.end ()
+            ? p->second.find_user_function () : octave_value ());
+  }
+
+  octave_value symbol_table::find_cmdline_function (const std::string& name)
+  {
+    auto p = m_fcn_table.find (name);
+
+    return (p != m_fcn_table.end ()
+            ? p->second.find_cmdline_function () : octave_value ());
+  }
+
+  void symbol_table::install_cmdline_function (const std::string& name,
+                                               const octave_value& fcn)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.install_cmdline_function (fcn);
+      }
+    else
+      {
+        fcn_info finfo (name);
+
+        finfo.install_cmdline_function (fcn);
+
+        m_fcn_table[name] = finfo;
+      }
+  }
+
+  // Install local function FCN named NAME.  FILE_NAME is the name of
+  // the file containing the local function.
+
+  void symbol_table::install_local_function (const std::string& name,
+                                             const octave_value& fcn,
+                                             const std::string& file_name)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.install_local_function (fcn, file_name);
+      }
+    else
+      {
+        fcn_info finfo (name);
+
+        finfo.install_local_function (fcn, file_name);
+
+        m_fcn_table[name] = finfo;
+      }
+  }
+
+  void symbol_table::install_user_function (const std::string& name,
+                                            const octave_value& fcn)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.install_user_function (fcn);
+      }
+    else
+      {
+        fcn_info finfo (name);
+
+        finfo.install_user_function (fcn);
+
+        m_fcn_table[name] = finfo;
+      }
+  }
+
+  // FIXME: should we ensure that FCN really is a built-in function
+  // object?
+  void symbol_table::install_built_in_function (const std::string& name,
+                                                const octave_value& fcn)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.install_built_in_function (fcn);
+      }
+    else
+      {
+        fcn_info finfo (name);
+
+        finfo.install_built_in_function (fcn);
+
+        m_fcn_table[name] = finfo;
+      }
+  }
+
+  // This is written as two separate functions instead of a single
+  // function with default values so that it will work properly with
+  // unwind_protect.
+
+  void symbol_table::clear_functions (bool force)
+  {
+    auto p = m_fcn_table.begin ();
+
+    while (p != m_fcn_table.end ())
+      (p++)->second.clear (force);
+  }
+
+  void symbol_table::clear_function (const std::string& name)
+  {
+    clear_user_function (name);
+  }
+
+  void symbol_table::clear_function_pattern (const std::string& pat)
+  {
+    glob_match pattern (pat);
+
+    auto p = m_fcn_table.begin ();
+
+    while (p != m_fcn_table.end ())
+      {
+        if (pattern.match (p->first))
+          (p++)->second.clear_user_function ();
+        else
+          p++;
+      }
+  }
+
+  void symbol_table::clear_function_regexp (const std::string& pat)
+  {
+    regexp pattern (pat);
+
+    auto p = m_fcn_table.begin ();
+
+    while (p != m_fcn_table.end ())
+      {
+        if (pattern.is_match (p->first))
+          (p++)->second.clear_user_function ();
+        else
+          p++;
+      }
+  }
+
+  void symbol_table::clear_user_function (const std::string& name)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.clear_user_function ();
+      }
+    // FIXME: is this necessary, or even useful?
+    // else
+    //   error ("clear: no such function '%s'", name.c_str ());
+  }
+
+  // This clears oct and mex files, including autoloads.
+  void symbol_table::clear_dld_function (const std::string& name)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.clear_autoload_function ();
+        finfo.clear_user_function ();
+      }
+  }
+
+  void symbol_table::clear_mex_functions (void)
+  {
+    auto p = m_fcn_table.begin ();
+
+    while (p != m_fcn_table.end ())
+      (p++)->second.clear_mex_function ();
+  }
+
+  // Insert INF_CLASS in the set of class names that are considered
+  // inferior to SUP_CLASS.  Return FALSE if INF_CLASS is currently
+  // marked as superior to SUP_CLASS.
+
+  bool symbol_table::set_class_relationship (const std::string& sup_class,
+                                             const std::string& inf_class)
+  {
+    if (is_superiorto (inf_class, sup_class))
+      return false;
+
+    // If sup_class doesn't have an entry in the precedence table,
+    // this will automatically create it, and associate to it a
+    // singleton set {inf_class} of inferior classes.
+    m_class_precedence_table[sup_class].insert (inf_class);
+
+    return true;
+  }
+
+  // Has class A been marked as superior to class B?  Also returns
+  // TRUE if B has been marked as inferior to A, since we only keep
+  // one table, and convert inferiorto information to a superiorto
+  // relationship.  Two calls are required to determine whether there
+  // is no relationship between two classes:
+  //
+  //  if (symbol_table::is_superiorto (a, b))
+  //    // A is superior to B, or B has been marked inferior to A.
+  //  else if (symbol_table::is_superiorto (b, a))
+  //    // B is superior to A, or A has been marked inferior to B.
+  //  else
+  //    // No relation.
+
+  bool symbol_table::is_superiorto (const std::string& a, const std::string& b)
+  {
+    class_precedence_table_const_iterator p = m_class_precedence_table.find (a);
+    // If a has no entry in the precedence table, return false
+    if (p == m_class_precedence_table.end ())
+      return false;
+
+    const std::set<std::string>& inferior_classes = p->second;
+    std::set<std::string>::const_iterator q = inferior_classes.find (b);
+    return (q != inferior_classes.end ());
+  }
+
+  void symbol_table::alias_built_in_function (const std::string& alias,
+                                              const std::string& name)
+  {
+    octave_value fcn = find_built_in_function (name);
+
+    if (fcn.is_defined ())
+      {
+        fcn_info finfo (alias);
+
+        finfo.install_built_in_function (fcn);
+
+        m_fcn_table[alias] = finfo;
+      }
+    else
+      panic ("alias: '%s' is undefined", name.c_str ());
+  }
+
+  void symbol_table::install_built_in_dispatch (const std::string& name,
+                                                const std::string& klass)
+  {
+    auto p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.install_built_in_dispatch (klass);
+      }
+    else
+      error ("install_built_in_dispatch: '%s' is undefined", name.c_str ());
+  }
+
+  std::list<std::string> symbol_table::user_function_names (void)
+  {
+    std::list<std::string> retval;
+
+    for (const auto& nm_finfo : m_fcn_table)
+      {
+        if (nm_finfo.second.is_user_function_defined ())
+          retval.push_back (nm_finfo.first);
+      }
+
+    if (! retval.empty ())
+      retval.sort ();
+
+    return retval;
+  }
+
+  std::list<std::string> symbol_table::built_in_function_names (void)
+  {
+    std::list<std::string> retval;
+
+    for (const auto& nm_finfo : m_fcn_table)
+      {
+        octave_value fcn = nm_finfo.second.find_built_in_function ();
+
+        if (fcn.is_defined ())
+          retval.push_back (nm_finfo.first);
+      }
+
+    if (! retval.empty ())
+      retval.sort ();
+
+    return retval;
+  }
+
+  std::list<std::string> symbol_table::cmdline_function_names (void)
+  {
+    std::list<std::string> retval;
+
+    for (const auto& nm_finfo : m_fcn_table)
+      {
+        octave_value fcn = nm_finfo.second.find_cmdline_function ();
+
+        if (fcn.is_defined ())
+          retval.push_back (nm_finfo.first);
+      }
+
+    if (! retval.empty ())
+      retval.sort ();
+
+    return retval;
+  }
+
+  template <template <typename, typename...> class C, typename V,
+            typename... A>
+  static octave_value
+  dump_container_map (const std::map<std::string, C<V, A...>>& container_map)
+  {
+    if (container_map.empty ())
+      return octave_value (Matrix ());
+
+    std::map<std::string, octave_value> info_map;
+
+    for (const auto& nm_container : container_map)
+      {
+        std::string nm = nm_container.first;
+        const C<V, A...>& container = nm_container.second;
+        info_map[nm] = Cell (container);
+      }
+
+    return octave_value (info_map);
+  }
+
+  octave_value symbol_table::dump (void) const
+  {
+    std::map<std::string, octave_value> m
+      = {{ "function_info", dump_fcn_table_map () },
+         { "precedence_table", dump_container_map (m_class_precedence_table) },
+         { "parent_classes", dump_container_map (m_parent_map) }};
+
+    return octave_value (m);
+  }
+
+  void symbol_table::add_to_parent_map (const std::string& classname,
+                          const std::list<std::string>& parent_list)
+  {
+    m_parent_map[classname] = parent_list;
+  }
+
+  std::list<std::string> symbol_table::parent_classes (const std::string& dispatch_type)
+  {
+    std::list<std::string> retval;
+
+    const_parent_map_iterator it = m_parent_map.find (dispatch_type);
+
+    if (it != m_parent_map.end ())
+      retval = it->second;
+
+    for (const auto& nm : retval)
+      {
+        // Search for parents of parents and append them to the list.
+
+        // FIXME: should we worry about a circular inheritance graph?
+
+        std::list<std::string> parents = parent_classes (nm);
+
+        if (! parents.empty ())
+          retval.insert (retval.end (), parents.begin (), parents.end ());
+      }
+
+    return retval;
+  }
+
+  octave_user_function * symbol_table::get_curr_fcn (void)
+  {
+    symbol_scope curr_scope = current_scope ();
+
+    return curr_scope ? curr_scope.function () : nullptr;
+  }
+
+  void symbol_table::cleanup (void)
+  {
+    clear_functions ();
+
+    m_fcn_table.clear ();
+    m_class_precedence_table.clear ();
+    m_parent_map.clear ();
+  }
+
+  fcn_info * symbol_table::get_fcn_info (const std::string& name)
+  {
+    auto p = m_fcn_table.find (name);
+    return p != m_fcn_table.end () ? &p->second : nullptr;
+  }
+
+  octave_value symbol_table::dump_fcn_table_map (void) const
+  {
+    if (m_fcn_table.empty ())
+      return octave_value (Matrix ());
+
+    std::map<std::string, octave_value> info_map;
+
+    for (const auto& nm_finfo : m_fcn_table)
+      {
+        std::string nm = nm_finfo.first;
+        const fcn_info& finfo = nm_finfo.second;
+        info_map[nm] = finfo.dump ();
+      }
+
+    return octave_value (info_map);
+  }
+
+  static void split_name_with_package (const std::string& name,
+                                       std::string& fname, std::string& pname)
   {
     size_t pos = name.rfind ('.');
 
@@ -98,7 +658,7 @@
 
     octave_value ov_fcn
       = load_fcn_from_file (ff, dir_name, dispatch_type,
-                                    package_name);
+                            package_name);
 
     if (ov_fcn.is_defined ())
       {
@@ -112,10 +672,9 @@
     return retval;
   }
 
-  bool
-  out_of_date_check (octave_value& function,
-                     const std::string& dispatch_type,
-                     bool check_relative)
+  bool out_of_date_check (octave_value& function,
+                          const std::string& dispatch_type,
+                          bool check_relative)
   {
     bool retval = false;
 
@@ -125,7 +684,7 @@
       {
         // FIXME: we need to handle subfunctions properly here.
 
-        if (! fcn->is_subfunction ())
+        if (! (fcn->is_subfunction () || fcn->is_anonymous_function ()))
           {
             std::string ff = fcn->fcn_file_name ();
 
@@ -298,229 +857,6 @@
 
     return retval;
   }
-
-  void
-  symbol_table::clear_global (const std::string& name)
-  {
-    m_global_scope.clear_variable (name);
-  }
-
-  void
-  symbol_table::clear_global_pattern (const std::string& pattern)
-  {
-    m_global_scope.clear_variable_pattern (pattern);
-  }
-
-  // Insert INF_CLASS in the set of class names that are considered
-  // inferior to SUP_CLASS.  Return FALSE if INF_CLASS is currently
-  // marked as superior to SUP_CLASS.
-
-  bool
-  symbol_table::set_class_relationship (const std::string& sup_class,
-                                        const std::string& inf_class)
-  {
-    if (is_superiorto (inf_class, sup_class))
-      return false;
-
-    // If sup_class doesn't have an entry in the precedence table,
-    // this will automatically create it, and associate to it a
-    // singleton set {inf_class} of inferior classes.
-    m_class_precedence_table[sup_class].insert (inf_class);
-
-    return true;
-  }
-
-  // Has class A been marked as superior to class B?  Also returns
-  // TRUE if B has been marked as inferior to A, since we only keep
-  // one table, and convert inferiorto information to a superiorto
-  // relationship.  Two calls are required to determine whether there
-  // is no relationship between two classes:
-  //
-  //  if (symbol_table::is_superiorto (a, b))
-  //    // A is superior to B, or B has been marked inferior to A.
-  //  else if (symbol_table::is_superiorto (b, a))
-  //    // B is superior to A, or A has been marked inferior to B.
-  //  else
-  //    // No relation.
-
-  bool
-  symbol_table::is_superiorto (const std::string& a, const std::string& b)
-  {
-    class_precedence_table_const_iterator p = m_class_precedence_table.find (a);
-    // If a has no entry in the precedence table, return false
-    if (p == m_class_precedence_table.end ())
-      return false;
-
-    const std::set<std::string>& inferior_classes = p->second;
-    std::set<std::string>::const_iterator q = inferior_classes.find (b);
-    return (q != inferior_classes.end ());
-  }
-
-  octave_value
-  symbol_table::builtin_find (const std::string& name)
-  {
-    fcn_table_iterator p = m_fcn_table.find (name);
-
-    if (p != m_fcn_table.end ())
-      return p->second.builtin_find ();
-    else
-      {
-        fcn_info finfo (name);
-
-        octave_value fcn = finfo.builtin_find ();
-
-        if (fcn.is_defined ())
-          m_fcn_table[name] = finfo;
-
-        return fcn;
-      }
-
-    return octave_value ();
-  }
-
-  octave_value
-  symbol_table::fcn_table_find (const std::string& name,
-                                const octave_value_list& args)
-  {
-    fcn_table_iterator p = m_fcn_table.find (name);
-
-    if (p != m_fcn_table.end ())
-      return p->second.find (args);
-    else
-      {
-        fcn_info finfo (name);
-
-        octave_value fcn = finfo.find (args);
-
-        if (fcn.is_defined ())
-          m_fcn_table[name] = finfo;
-
-        return fcn;
-      }
-
-    return octave_value ();
-  }
-
-  octave_value symbol_table::find_function (const std::string& name)
-  {
-    if (name.empty ())
-      return octave_value ();
-
-    if (name[0] == '@')
-      {
-        size_t pos = name.find_first_of ('/');
-
-        if (pos == std::string::npos)
-          return octave_value ();
-
-        std::string method = name.substr (pos+1);
-        std::string dispatch_type = name.substr (1, pos-1);
-
-        return find_method (method, dispatch_type);
-      }
-    else
-      return find_function (name, ovl ());
-  }
-
-  octave_value
-  symbol_table::find_function (const std::string& name,
-                               const octave_value_list& args)
-  {
-    octave_value fcn;
-
-    if (m_current_scope)
-      {
-        fcn = m_current_scope.find_subfunction (name);
-
-        if (fcn.is_defined ())
-          return fcn;
-      }
-
-    return fcn_table_find (name, args);
-  }
-
-  // FIXME: this function only finds legacy class methods, not
-  // classdef methods.
-
-  octave_value
-  symbol_table::find_method (const std::string& name,
-                             const std::string& dispatch_type)
-  {
-    fcn_table_const_iterator p = m_fcn_table.find (name);
-
-    if (p != m_fcn_table.end ())
-      return p->second.find_method (dispatch_type);
-    else
-      {
-        fcn_info finfo (name);
-
-        octave_value fcn = finfo.find_method (dispatch_type);
-
-        if (fcn.is_defined ())
-          m_fcn_table[name] = finfo;
-
-        return fcn;
-      }
-  }
-
-  template <template <typename, typename...> class C, typename V,
-            typename... A>
-  static octave_value
-  dump_container_map (const std::map<std::string, C<V, A...>>& container_map)
-  {
-    if (container_map.empty ())
-      return octave_value (Matrix ());
-
-    std::map<std::string, octave_value> info_map;
-
-    for (const auto& nm_container : container_map)
-      {
-        std::string nm = nm_container.first;
-        const C<V, A...>& container = nm_container.second;
-        info_map[nm] = Cell (container);
-      }
-
-    return octave_value (info_map);
-  }
-
-  octave_value
-  symbol_table::dump (void) const
-  {
-    std::map<std::string, octave_value> m
-      = {{ "function_info", dump_fcn_table_map () },
-         { "precedence_table", dump_container_map (m_class_precedence_table) },
-         { "parent_classes", dump_container_map (m_parent_map) }};
-
-    return octave_value (m);
-  }
-
-  void
-  symbol_table::cleanup (void)
-  {
-    clear_all (true);
-
-    m_fcn_table.clear ();
-    m_class_precedence_table.clear ();
-    m_parent_map.clear ();
-  }
-
-  octave_value
-  symbol_table::dump_fcn_table_map (void) const
-  {
-    if (m_fcn_table.empty ())
-      return octave_value (Matrix ());
-
-    std::map<std::string, octave_value> info_map;
-
-    for (const auto& nm_finfo : m_fcn_table)
-      {
-        std::string nm = nm_finfo.first;
-        const fcn_info& finfo = nm_finfo.second;
-        info_map[nm] = finfo.dump ();
-      }
-
-    return octave_value (info_map);
-  }
 }
 
 DEFUN (ignore_function_time_stamp, args, nargout,
@@ -602,28 +938,11 @@
 %!error (ignore_function_time_stamp (42))
 */
 
-DEFMETHOD (__current_scope__, interp, , ,
-           doc: /* -*- texinfo -*-
-@deftypefn {} {[@var{scope}, @var{context}] =} __current_scope__ ()
-Return the current scope and context as integers.
-@seealso{__dump_symtab_info__}
-@end deftypefn */)
-{
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
-  octave::symbol_scope scope = symtab.current_scope ();
-
-  std::string nm = scope ? scope.name () : "<unknown>";
-
-  return ovl (nm, symtab.current_context ());
-}
-
 DEFMETHOD (__dump_symtab_info__, interp, args, ,
            doc: /* -*- texinfo -*-
 @deftypefn  {} {} __dump_symtab_info__ ()
 @deftypefnx {} {} __dump_symtab_info__ (@var{function})
 Undocumented internal function.
-@seealso{__current_scope__}
 @end deftypefn */)
 {
   int nargin = args.length ();
--- a/libinterp/corefcn/symtab.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/symtab.h	Mon Jan 28 18:01:46 2019 +0000
@@ -50,17 +50,10 @@
   {
   public:
 
-    typedef octave::symbol_record symbol_record;
     typedef octave::symbol_scope scope;
     typedef octave::fcn_info fcn_info;
 
-    symbol_table (void)
-      : m_fcn_table (), m_class_precedence_table (),
-        m_parent_map (), m_global_scope ("global scope"),
-        m_top_scope ("top scope"), m_current_scope (m_top_scope)
-    {
-      install_builtins ();
-    }
+    symbol_table (interpreter& interp);
 
     // No copying!
 
@@ -70,147 +63,23 @@
 
     ~symbol_table (void) = default;
 
-    symbol_scope global_scope (void) { return m_global_scope; }
-    symbol_scope top_scope (void) { return m_top_scope; }
-
-    symbol_scope current_scope (void) { return m_current_scope; }
-
-    symbol_scope require_current_scope (const std::string& who)
-    {
-      if (! m_current_scope)
-        error ("%s: missing scope", who.c_str ());
-
-      return m_current_scope;
-    }
-
-    symbol_record::context_id current_context (void) const
-    {
-      return m_current_scope ? m_current_scope.current_context () : 0;
-    }
-
-    void set_scope (const symbol_scope& sid)
-    {
-      set_scope_and_context (sid, 0);
-    }
-
-    void set_scope_and_context (const symbol_scope& sid,
-                                symbol_record::context_id context)
-    {
-      if (sid == m_global_scope)
-        error ("can't set scope to global");
-
-      m_current_scope = sid;
-
-      if (m_current_scope)
-        m_current_scope.set_context (context);
-    }
-
-    symbol_record find_symbol (const std::string& name)
-    {
-      return m_current_scope.find_symbol (name);
-    }
-
-    symbol_record find_global_symbol (const std::string& name)
-    {
-      symbol_record sym = m_global_scope.find_symbol (name);
-
-      sym.mark_global ();
-
-      return sym;
-    }
-
-    void
-    inherit (symbol_scope& recipient_scope, const symbol_scope& donor_scope)
-    {
-      if (recipient_scope)
-        recipient_scope.inherit (donor_scope);
-    }
+    symbol_scope current_scope (void) const;
 
-    void inherit (symbol_scope& recipient_scope)
-    {
-      inherit (recipient_scope, m_current_scope);
-    }
-
-    bool at_top_level (void) { return m_current_scope == m_top_scope; }
-
-    void assign (const std::string& name, const octave_value& value, bool force_add)
-    {
-      if (m_current_scope)
-        m_current_scope.assign (name, value, force_add);
-    }
-
-    void assign (const std::string& name,
-                 const octave_value& value = octave_value ())
-    {
-      if (m_current_scope)
-        m_current_scope.assign (name, value);
-    }
-
-    octave_value varval (const std::string& name) const
-    {
-      return (m_current_scope
-              ? m_current_scope.varval (name) : octave_value ());
-    }
-
-    void global_assign (const std::string& name,
-                        const octave_value& value = octave_value ())
-    {
-      m_global_scope.assign (name, value);
-    }
-
-    octave_value global_varval (const std::string& name) const
-    {
-      return m_global_scope.varval (name);
-    }
-
-    void
-    top_level_assign (const std::string& name,
-                      const octave_value& value = octave_value ())
-    {
-      m_top_scope.assign (name, value);
-    }
-
-    octave_value top_level_varval (const std::string& name) const
-    {
-      return m_top_scope.varval (name);
-    }
-
-    bool
-    is_built_in_function_name (const std::string& name)
-    {
-      octave_value val = find_built_in_function (name);
-
-      return val.is_defined ();
-    }
+    bool is_built_in_function_name (const std::string& name);
 
     // FIXME: this function only finds legacy class methods, not
     // classdef methods.
-    octave_value
-    find_method (const std::string& name, const std::string& dispatch_type);
-
-    octave_value
-    find_built_in_function (const std::string& name)
-    {
-      fcn_table_const_iterator p = m_fcn_table.find (name);
+    octave_value find_method (const std::string& name,
+                              const std::string& dispatch_type);
 
-      return (p != m_fcn_table.end ()
-              ? p->second.find_built_in_function () : octave_value ());
-    }
+    octave_value find_built_in_function (const std::string& name);
 
-    octave_value
-    find_autoload (const std::string& name)
-    {
-      auto p = m_fcn_table.find (name);
-
-      return (p != m_fcn_table.end ()
-              ? p->second.find_autoload () : octave_value ());
-    }
+    octave_value find_autoload (const std::string& name);
 
     octave_value builtin_find (const std::string& name);
 
-    octave_value
-    fcn_table_find (const std::string& name,
-                    const octave_value_list& args = octave_value_list ());
+    octave_value fcn_table_find (const std::string& name,
+                                 const octave_value_list& args = ovl ());
 
     // If NAME is of the form @CLASS/FUNCTION, call
     //
@@ -225,215 +94,49 @@
     // NAME should just be function name; dispatch type determined
     // from types of ARGS.
 
-    octave_value
-    find_function (const std::string& name, const octave_value_list& args);
-
-    octave_value find_user_function (const std::string& name)
-    {
-      auto p = m_fcn_table.find (name);
+    octave_value find_function (const std::string& name,
+                                const octave_value_list& args);
 
-      return (p != m_fcn_table.end ()
-              ? p->second.find_user_function () : octave_value ());
-    }
+    octave_value find_user_function (const std::string& name);
 
-    octave_value find_cmdline_function (const std::string& name)
-    {
-      auto p = m_fcn_table.find (name);
-
-      return (p != m_fcn_table.end ()
-              ? p->second.find_cmdline_function () : octave_value ());
-    }
+    octave_value find_cmdline_function (const std::string& name);
 
     void install_cmdline_function (const std::string& name,
-                                   const octave_value& fcn)
-    {
-      auto p = m_fcn_table.find (name);
-
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
-
-          finfo.install_cmdline_function (fcn);
-        }
-      else
-        {
-          fcn_info finfo (name);
-
-          finfo.install_cmdline_function (fcn);
-
-          m_fcn_table[name] = finfo;
-        }
-    }
+                                   const octave_value& fcn);
 
     // Install local function FCN named NAME.  FILE_NAME is the name of
     // the file containing the local function.
 
     void install_local_function (const std::string& name,
                                  const octave_value& fcn,
-                                 const std::string& file_name)
-    {
-      auto p = m_fcn_table.find (name);
-
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
-
-          finfo.install_local_function (fcn, file_name);
-        }
-      else
-        {
-          fcn_info finfo (name);
-
-          finfo.install_local_function (fcn, file_name);
-
-          m_fcn_table[name] = finfo;
-        }
-    }
+                                 const std::string& file_name);
 
     void install_user_function (const std::string& name,
-                                const octave_value& fcn)
-    {
-      auto p = m_fcn_table.find (name);
-
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
-
-          finfo.install_user_function (fcn);
-        }
-      else
-        {
-          fcn_info finfo (name);
-
-          finfo.install_user_function (fcn);
-
-          m_fcn_table[name] = finfo;
-        }
-    }
+                                const octave_value& fcn);
 
     // FIXME: should we ensure that FCN really is a built-in function
     // object?
     void install_built_in_function (const std::string& name,
-                                    const octave_value& fcn)
-    {
-      auto p = m_fcn_table.find (name);
-
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
-
-          finfo.install_built_in_function (fcn);
-        }
-      else
-        {
-          fcn_info finfo (name);
-
-          finfo.install_built_in_function (fcn);
-
-          m_fcn_table[name] = finfo;
-        }
-    }
-
-    void clear_all (bool force = false)
-    {
-      m_current_scope.clear_variables ();
-      m_global_scope.clear_variables ();
-
-      clear_functions (force);
-    }
-
-    void clear_global (const std::string& name);
-
-    void clear_global_pattern (const std::string& pattern);
+                                    const octave_value& fcn);
 
     // This is written as two separate functions instead of a single
     // function with default values so that it will work properly with
     // unwind_protect.
 
-    void clear_functions (bool force = false)
-    {
-      auto p = m_fcn_table.begin ();
-
-      while (p != m_fcn_table.end ())
-        (p++)->second.clear (force);
-    }
-
-    void clear_function (const std::string& name)
-    {
-      clear_user_function (name);
-    }
+    void clear_functions (bool force = false);
 
-    void clear_symbol (const std::string& name)
-    {
-      // FIXME: are we supposed to do both here?
-
-      if (m_current_scope)
-        m_current_scope.clear_variable (name);
-
-      clear_function (name);
-    }
-
-    void clear_function_pattern (const std::string& pat)
-    {
-      glob_match pattern (pat);
-
-      auto p = m_fcn_table.begin ();
+    void clear_function (const std::string& name);
 
-      while (p != m_fcn_table.end ())
-        {
-          if (pattern.match (p->first))
-            (p++)->second.clear_user_function ();
-          else
-            p++;
-        }
-    }
-
-    void clear_symbol_pattern (const std::string& pat)
-    {
-      // FIXME: are we supposed to do both here?
-
-      if (m_current_scope)
-        m_current_scope.clear_variable_pattern (pat);
+    void clear_function_pattern (const std::string& pat);
 
-      clear_function_pattern (pat);
-    }
-
-    void clear_user_function (const std::string& name)
-    {
-      auto p = m_fcn_table.find (name);
+    void clear_function_regexp (const std::string& pat);
 
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
-
-          finfo.clear_user_function ();
-        }
-      // FIXME: is this necessary, or even useful?
-      // else
-      //   error ("clear: no such function '%s'", name.c_str ());
-    }
+    void clear_user_function (const std::string& name);
 
     // This clears oct and mex files, including autoloads.
-    void clear_dld_function (const std::string& name)
-    {
-      auto p = m_fcn_table.find (name);
-
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
+    void clear_dld_function (const std::string& name);
 
-          finfo.clear_autoload_function ();
-          finfo.clear_user_function ();
-        }
-    }
-
-    void clear_mex_functions (void)
-    {
-      auto p = m_fcn_table.begin ();
-
-      while (p != m_fcn_table.end ())
-        (p++)->second.clear_mex_function ();
-    }
+    void clear_mex_functions (void);
 
     bool set_class_relationship (const std::string& sup_class,
                                  const std::string& inf_class);
@@ -441,209 +144,34 @@
     bool is_superiorto (const std::string& a, const std::string& b);
 
     void alias_built_in_function (const std::string& alias,
-                                  const std::string& name)
-    {
-      octave_value fcn = find_built_in_function (name);
-
-      if (fcn.is_defined ())
-        {
-          fcn_info finfo (alias);
-
-          finfo.install_built_in_function (fcn);
-
-          m_fcn_table[alias] = finfo;
-        }
-      else
-        panic ("alias: '%s' is undefined", name.c_str ());
-    }
+                                  const std::string& name);
 
     void install_built_in_dispatch (const std::string& name,
-                                    const std::string& klass)
-    {
-      auto p = m_fcn_table.find (name);
-
-      if (p != m_fcn_table.end ())
-        {
-          fcn_info& finfo = p->second;
-
-          finfo.install_built_in_dispatch (klass);
-        }
-      else
-        error ("install_built_in_dispatch: '%s' is undefined", name.c_str ());
-    }
-
-    std::list<symbol_record> glob (const std::string& pattern)
-    {
-      return (m_current_scope
-              ? m_current_scope.glob (pattern) : std::list<symbol_record> ());
-    }
-
-    std::list<symbol_record> glob_global_variables (const std::string& pattern)
-    {
-      return m_global_scope.glob (pattern);
-    }
-
-    std::list<symbol_record>
-    regexp_global_variables (const std::string& pattern)
-    {
-      return m_global_scope.regexp (pattern);
-    }
+                                    const std::string& klass);
 
-    std::list<symbol_record> glob_variables (const string_vector& patterns)
-    {
-      std::list<symbol_record> retval;
-
-      if (! m_current_scope)
-        return retval;
-
-      size_t len = patterns.numel ();
-
-      for (size_t i = 0; i < len; i++)
-        {
-          std::list<symbol_record> tmp = m_current_scope.glob (patterns[i]);
-
-          retval.insert (retval.begin (), tmp.begin (), tmp.end ());
-        }
-
-      return retval;
-    }
-
-    std::list<symbol_record> regexp_variables (const string_vector& patterns)
-    {
-      std::list<symbol_record> retval;
-
-      if (! m_current_scope)
-        return retval;
-
-      size_t len = patterns.numel ();
-
-      for (size_t i = 0; i < len; i++)
-        {
-          std::list<symbol_record> tmp = m_current_scope.regexp (patterns[i]);
-
-          retval.insert (retval.begin (), tmp.begin (), tmp.end ());
-        }
-
-      return retval;
-    }
+    std::list<std::string> user_function_names (void);
 
-    std::list<std::string> user_function_names (void)
-    {
-      std::list<std::string> retval;
-
-      for (const auto& nm_finfo : m_fcn_table)
-        {
-          if (nm_finfo.second.is_user_function_defined ())
-            retval.push_back (nm_finfo.first);
-        }
-
-      if (! retval.empty ())
-        retval.sort ();
-
-      return retval;
-    }
-
-    std::list<std::string> global_variable_names (void)
-    {
-      return m_global_scope.variable_names ();
-    }
-
-    std::list<std::string> top_level_variable_names (void)
-    {
-      return (m_top_scope
-              ? m_top_scope.variable_names () : std::list<std::string> ());
-    }
-
-    std::list<std::string> variable_names (void)
-    {
-      return (m_current_scope
-              ? m_current_scope.variable_names () : std::list<std::string> ());
-    }
+    std::list<std::string> built_in_function_names (void);
 
-    std::list<std::string> built_in_function_names (void)
-    {
-      std::list<std::string> retval;
-
-      for (const auto& nm_finfo : m_fcn_table)
-        {
-          octave_value fcn = nm_finfo.second.find_built_in_function ();
-
-          if (fcn.is_defined ())
-            retval.push_back (nm_finfo.first);
-        }
-
-      if (! retval.empty ())
-        retval.sort ();
-
-      return retval;
-    }
-
-    std::list<std::string> cmdline_function_names (void)
-    {
-      std::list<std::string> retval;
-
-      for (const auto& nm_finfo : m_fcn_table)
-        {
-          octave_value fcn = nm_finfo.second.find_cmdline_function ();
-
-          if (fcn.is_defined ())
-            retval.push_back (nm_finfo.first);
-        }
-
-      if (! retval.empty ())
-        retval.sort ();
-
-      return retval;
-    }
+    std::list<std::string> cmdline_function_names (void);
 
     octave_value dump (void) const;
 
     void add_to_parent_map (const std::string& classname,
-                            const std::list<std::string>& parent_list)
-    {
-      m_parent_map[classname] = parent_list;
-    }
-
-    std::list<std::string>
-    parent_classes (const std::string& dispatch_type)
-    {
-      std::list<std::string> retval;
-
-      const_parent_map_iterator it = m_parent_map.find (dispatch_type);
-
-      if (it != m_parent_map.end ())
-        retval = it->second;
+                            const std::list<std::string>& parent_list);
 
-      for (const auto& nm : retval)
-        {
-          // Search for parents of parents and append them to the list.
-
-          // FIXME: should we worry about a circular inheritance graph?
-
-          std::list<std::string> parents = parent_classes (nm);
+    std::list<std::string> parent_classes (const std::string& dispatch_type);
 
-          if (! parents.empty ())
-            retval.insert (retval.end (), parents.begin (), parents.end ());
-        }
-
-      return retval;
-    }
-
-    octave_user_function * get_curr_fcn (void)
-    {
-      return m_current_scope ? m_current_scope.function () : nullptr;
-    }
+    octave_user_function * get_curr_fcn (void);
 
     void cleanup (void);
 
-    fcn_info * get_fcn_info (const std::string& name)
-    {
-      auto p = m_fcn_table.find (name);
-      return p != m_fcn_table.end () ? &p->second : nullptr;
-    }
+    fcn_info * get_fcn_info (const std::string& name);
 
   private:
 
+    interpreter& m_interpreter;
+
     typedef std::map<std::string, octave_value>::const_iterator
       global_symbols_const_iterator;
     typedef std::map<std::string, octave_value>::iterator
@@ -677,11 +205,6 @@
     typedef std::map<std::string, std::list<std::string>>::iterator
       parent_map_iterator;
 
-    symbol_scope m_global_scope;
-    symbol_scope m_top_scope;
-
-    symbol_scope m_current_scope;
-
     octave_value dump_fcn_table_map (void) const;
 
     // This function is generated automatically by mk-builtins.pl.
--- a/libinterp/corefcn/syscalls.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/syscalls.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -484,9 +484,7 @@
   if (args.length () != 0)
     print_usage ();
 
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
-  if (symtab.at_top_level ())
+  if (interp.at_top_level ())
     error ("fork: cannot be called from command line");
 
   std::string msg;
--- a/libinterp/corefcn/variables.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/corefcn/variables.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -29,6 +29,7 @@
 #include <cstring>
 
 #include <iomanip>
+#include <list>
 #include <set>
 #include <string>
 
@@ -157,22 +158,6 @@
   return retval;
 }
 
-static octave_value
-do_isglobal (octave::symbol_table& symtab, const octave_value_list& args)
-{
-  if (args.length () != 1)
-    print_usage ();
-
-  if (! args(0).is_string ())
-    error ("isglobal: NAME must be a string");
-
-  octave::symbol_scope scope = symtab.current_scope ();
-
-  std::string name = args(0).string_value ();
-
-  return scope && scope.is_global (name);
-}
-
 DEFMETHOD (isglobal, interp, args, ,
            doc: /* -*- texinfo -*-
 @deftypefn {} {} isglobal (@var{name})
@@ -190,9 +175,12 @@
 @seealso{isvarname, exist}
 @end deftypefn */)
 {
-  octave::symbol_table& symtab = interp.get_symbol_table ();
+  if (args.length () != 1)
+    print_usage ();
 
-  return do_isglobal (symtab, args);
+  std::string name = args(0).xstring_value ("isglobal: NAME must be a string");
+
+  return ovl (interp.isglobal (name));
 }
 
 /*
@@ -228,9 +216,7 @@
 
   if (search_any || search_var)
     {
-      octave::symbol_scope scope = symtab.current_scope ();
-
-      octave_value val = scope ? scope.varval (name) : octave_value ();
+      octave_value val = interp.varval (name);
 
       if (val.is_constant () || val.isobject ()
           || val.is_function_handle ()
@@ -973,76 +959,59 @@
 }
 
 static void
-do_clear_functions (octave::symbol_table& symtab,
+do_clear_functions (octave::interpreter& interp,
                     const string_vector& argv, int argc, int idx,
                     bool exclusive = false)
 {
   if (idx == argc)
-    symtab.clear_functions ();
+    interp.clear_functions ();
   else
     {
       if (exclusive)
         {
-          string_vector fcns = symtab.user_function_names ();
-
-          int fcount = fcns.numel ();
+          std::list<std::string> fcns = interp.user_function_names ();
 
-          for (int i = 0; i < fcount; i++)
+          for (const auto& name : fcns)
             {
-              std::string nm = fcns[i];
-
-              if (! name_matches_any_pattern (nm, argv, argc, idx))
-                symtab.clear_function (nm);
+              if (! name_matches_any_pattern (name, argv, argc, idx))
+                interp.clear_function (name);
             }
         }
       else
         {
           while (idx < argc)
-            symtab.clear_function_pattern (argv[idx++]);
+            interp.clear_function_pattern (argv[idx++]);
         }
     }
 }
 
 static void
-do_clear_globals (octave::symbol_table& symtab,
+do_clear_globals (octave::interpreter& interp,
                   const string_vector& argv, int argc, int idx,
                   bool exclusive = false)
 {
-  octave::symbol_scope scope = symtab.current_scope ();
-
-  if (! scope)
-    return;
-
   if (idx == argc)
     {
-      string_vector gvars = symtab.global_variable_names ();
-
-      int gcount = gvars.numel ();
+      std::list<std::string> gvars = interp.global_variable_names ();
 
-      for (int i = 0; i < gcount; i++)
+      for (const auto& name : gvars)
         {
-          std::string name = gvars[i];
-
-          scope.clear_variable (name);
-          symtab.clear_global (name);
+          interp.clear_variable (name);
+          interp.clear_global_variable (name);
         }
     }
   else
     {
       if (exclusive)
         {
-          string_vector gvars = symtab.global_variable_names ();
-
-          int gcount = gvars.numel ();
+          std::list<std::string> gvars = interp.global_variable_names ();
 
-          for (int i = 0; i < gcount; i++)
+          for (const auto& name : gvars)
             {
-              std::string name = gvars[i];
-
               if (! name_matches_any_pattern (name, argv, argc, idx))
                 {
-                  scope.clear_variable (name);
-                  symtab.clear_global (name);
+                  interp.clear_variable (name);
+                  interp.clear_global_variable (name);
                 }
             }
         }
@@ -1052,64 +1021,53 @@
             {
               std::string pattern = argv[idx++];
 
-              scope.clear_variable_pattern (pattern);
-              symtab.clear_global_pattern (pattern);
+              interp.clear_variable_pattern (pattern);
+              interp.clear_global_variable_pattern (pattern);
             }
         }
     }
 }
 
 static void
-do_clear_variables (octave::symbol_table& symtab,
+do_clear_variables (octave::interpreter& interp,
                     const string_vector& argv, int argc, int idx,
                     bool exclusive = false, bool have_regexp = false)
 {
-  octave::symbol_scope scope = symtab.current_scope ();
-
-  if (! scope)
-    return;
-
   if (idx == argc)
-    scope.clear_variables ();
+    interp.clear_variables ();
   else
     {
       if (exclusive)
         {
-          string_vector lvars = scope.variable_names ();
-
-          int lcount = lvars.numel ();
+          std::list<std::string> lvars = interp.variable_names ();
 
-          for (int i = 0; i < lcount; i++)
+          for (const auto& name : lvars)
             {
-              std::string nm = lvars[i];
-
-              if (! name_matches_any_pattern (nm, argv, argc, idx, have_regexp))
-                scope.clear_variable (nm);
+              if (! name_matches_any_pattern (name, argv, argc, idx,
+                                              have_regexp))
+                interp.clear_variable (name);
             }
         }
       else
         {
           if (have_regexp)
             while (idx < argc)
-              scope.clear_variable_regexp (argv[idx++]);
+              interp.clear_variable_regexp (argv[idx++]);
           else
             while (idx < argc)
-              scope.clear_variable_pattern (argv[idx++]);
+              interp.clear_variable_pattern (argv[idx++]);
         }
     }
 }
 
 static void
-do_clear_symbols (octave::symbol_table& symtab,
+do_clear_symbols (octave::interpreter& interp,
                   const string_vector& argv, int argc, int idx,
                   bool exclusive = false)
 {
   if (idx == argc)
     {
-      octave::symbol_scope scope = symtab.current_scope ();
-
-      if (scope)
-        scope.clear_variables ();
+      interp.clear_variables ();
     }
   else
     {
@@ -1120,60 +1078,54 @@
           // shadowed by local variables?  It seems that would be a
           // bit harder to do.
 
-          do_clear_variables (symtab, argv, argc, idx, exclusive);
-          do_clear_functions (symtab, argv, argc, idx, exclusive);
+          do_clear_variables (interp, argv, argc, idx, exclusive);
+          do_clear_functions (interp, argv, argc, idx, exclusive);
         }
       else
         {
           while (idx < argc)
-            symtab.clear_symbol_pattern (argv[idx++]);
+            interp.clear_symbol_pattern (argv[idx++]);
         }
     }
 }
 
 static void
-do_matlab_compatible_clear (octave::symbol_table& symtab,
+do_matlab_compatible_clear (octave::interpreter& interp,
                             const string_vector& argv, int argc, int idx)
 {
   // This is supposed to be mostly Matlab compatible.
 
-  octave::symbol_scope scope = symtab.current_scope ();
-
-  if (! scope)
-    return;
-
   for (; idx < argc; idx++)
     {
-      if (argv[idx] == "all"
-          && ! scope.is_local_variable ("all"))
+      if (argv[idx] == "all" && ! interp.is_local_variable ("all"))
         {
-          symtab.clear_all ();
+          interp.clear_all ();
         }
       else if (argv[idx] == "functions"
-               && ! scope.is_local_variable ("functions"))
+               && ! interp.is_local_variable ("functions"))
         {
-          do_clear_functions (symtab, argv, argc, ++idx);
+          do_clear_functions (interp, argv, argc, ++idx);
         }
       else if (argv[idx] == "global"
-               && ! scope.is_local_variable ("global"))
+               && ! interp.is_local_variable ("global"))
         {
-          do_clear_globals (symtab, argv, argc, ++idx);
+          do_clear_globals (interp, argv, argc, ++idx);
         }
       else if (argv[idx] == "variables"
-               && ! scope.is_local_variable ("variables"))
+               && ! interp.is_local_variable ("variables"))
         {
-          scope.clear_variables ();
+          interp.clear_variables ();
         }
       else if (argv[idx] == "classes"
-               && ! scope.is_local_variable ("classes"))
+               && ! interp.is_local_variable ("classes"))
         {
-          scope.clear_objects ();
+          interp.clear_objects ();
           octave_class::clear_exemplar_map ();
-          symtab.clear_all ();
+          interp.clear_all ();
         }
       else
         {
-          symtab.clear_symbol_pattern (argv[idx]);
+          interp.clear_symbol_pattern (argv[idx]);
         }
     }
 }
@@ -1262,15 +1214,13 @@
 @seealso{who, whos, exist, mlock}
 @end deftypefn */)
 {
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
   int argc = args.length () + 1;
 
   string_vector argv = args.make_argv ("clear");
 
   if (argc == 1)
     {
-      do_clear_variables (symtab, argv, argc, argc);
+      do_clear_variables (interp, argv, argc, true);
 
       octave_link::clear_workspace ();
     }
@@ -1287,8 +1237,6 @@
       bool have_regexp = false;
       bool have_dash_option = false;
 
-      octave::symbol_scope scope = symtab.current_scope ();
-
       while (++idx < argc)
         {
           if (argv[idx] == "-all" || argv[idx] == "-a")
@@ -1350,7 +1298,7 @@
       if (idx <= argc)
         {
           if (! have_dash_option && ! exclusive)
-            do_matlab_compatible_clear (symtab, argv, argc, idx);
+            do_matlab_compatible_clear (interp, argv, argc, idx);
           else
             {
               if (clear_all)
@@ -1360,34 +1308,33 @@
                   if (++idx < argc)
                     warning ("clear: ignoring extra arguments after -all");
 
-                  symtab.clear_all ();
+                  interp.clear_all ();
                 }
               else if (have_regexp)
                 {
-                  do_clear_variables (symtab, argv, argc, idx, exclusive, true);
+                  do_clear_variables (interp, argv, argc, idx, exclusive, true);
                 }
               else if (clear_functions)
                 {
-                  do_clear_functions (symtab, argv, argc, idx, exclusive);
+                  do_clear_functions (interp, argv, argc, idx, exclusive);
                 }
               else if (clear_globals)
                 {
-                  do_clear_globals (symtab, argv, argc, idx, exclusive);
+                  do_clear_globals (interp, argv, argc, idx, exclusive);
                 }
               else if (clear_variables)
                 {
-                  do_clear_variables (symtab, argv, argc, idx, exclusive);
+                  do_clear_variables (interp, argv, argc, idx, exclusive);
                 }
               else if (clear_objects)
                 {
-                  if (scope)
-                    scope.clear_objects ();
+                  interp.clear_objects ();
                   octave_class::clear_exemplar_map ();
-                  symtab.clear_all ();
+                  interp.clear_all ();
                 }
               else
                 {
-                  do_clear_symbols (symtab, argv, argc, idx, exclusive);
+                  do_clear_symbols (interp, argv, argc, idx, exclusive);
                 }
             }
         }
@@ -1472,9 +1419,18 @@
 
   std::string name = args(0).xstring_value ("__varval__: first argument must be a variable name");
 
-  octave::symbol_scope scope = interp.get_current_scope ();
+  std::string nm = args(0).string_value ();
+
+  // FIXME: we need this kluge to implement inputname in a .m file.
 
-  return scope ? scope.varval (args(0).string_value ()) : octave_value ();
+  if (nm == ".argn.")
+    {
+      octave::call_stack& cs = interp.get_call_stack ();
+
+      return cs.get_auto_fcn_var (octave::stack_frame::ARG_NAMES);
+    }
+
+  return interp.varval (nm);
 }
 
 static std::string Vmissing_component_hook;
@@ -1510,7 +1466,7 @@
   return SET_INTERNAL_VARIABLE (missing_component_hook);
 }
 
-// The following functions are deprecated.
+// The following function is deprecated.
 
 string_vector
 get_struct_elts (const std::string& text)
--- a/libinterp/octave-value/ov-classdef.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/octave-value/ov-classdef.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -1167,9 +1167,7 @@
           error ("cannot call superclass constructor with variable `%s'",
                  mname.c_str ());
 
-        octave::symbol_scope scope = tw.get_current_scope ();
-
-        octave_value sym = scope.varval (mname);
+        octave_value sym = tw.varval (mname);
 
         cls.run_constructor (to_cdef_ref (sym), idx);
 
@@ -3377,10 +3375,10 @@
 {
   std::string symbol_name = get_name () + '.' + nm;
 
-  octave::symbol_scope curr_scope
-    = octave::__get_current_scope__ ("cdef_package::cdef_package_rep::find");
-
-  return curr_scope.find (symbol_name);
+  octave::interpreter& interp
+    = octave::__get_interpreter__ ("cdef_package::cdef_package_rep::find");
+
+  return interp.find (symbol_name);
 }
 
 octave_value_list
@@ -3788,12 +3786,7 @@
           size_t pos = name.rfind ('.');
 
           if (pos == std::string::npos)
-            {
-              octave::symbol_scope curr_scope
-                = m_interpreter.get_current_scope ();
-
-              ov_cls = curr_scope.find (name);
-            }
+            ov_cls = m_interpreter.find (name);
           else
             {
               std::string pack_name = name.substr (0, pos);
--- a/libinterp/octave-value/ov-fcn-handle.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/octave-value/ov-fcn-handle.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -54,7 +54,6 @@
 #include "parse.h"
 #include "pr-output.h"
 #include "pt-arg-list.h"
-#include "pt-anon-scopes.h"
 #include "pt-assign.h"
 #include "pt-cmd.h"
 #include "pt-eval.h"
@@ -357,31 +356,24 @@
         return false;
 
       octave_user_function *f = fcn.user_function_value ();
-      octave::symbol_scope f_scope = f->scope ();
 
-      if (! f_scope)
-        error ("internal error, invalid scope");
+      octave_user_function::local_vars_map local_vars
+        = f->local_var_init_vals ();
 
-      octave::tree_anon_scopes tas (f);
+      size_t varlen = local_vars.size ();
 
       os << nm << "\n";
 
       print_raw (os, true);
       os << "\n";
 
-      size_t varlen = tas.symrec_map_size ();
-
       if (varlen > 0)
         {
-          octave::symbol_record::context_id context
-            = f_scope.current_context ();
-
           os << "# length: " << varlen << "\n";
 
-          for (const auto& name_symrec : tas)
+          for (const auto& nm_val : local_vars)
             {
-              if (! save_text_data (os, name_symrec.second.varval (context),
-                                    name_symrec.first, false, 0))
+              if (! save_text_data (os, nm_val.second, nm_val.first, false, 0))
                 return ! os.fail ();
             }
         }
@@ -483,17 +475,13 @@
       // Set up temporary scope to use for evaluating the text that
       // defines the anonymous function.
 
-      octave::symbol_table& symtab
-        = octave::__get_symbol_table__ ("octave_fcn_handle::load_ascii");
+      octave::interpreter& interp
+        = octave::__get_interpreter__ ("octave_fcn_handle::load_ascii");
+
+      octave::call_stack& cs = interp.get_call_stack ();
 
       octave::symbol_scope local_scope (buf);
-
-      symtab.set_scope (local_scope);
-
-      octave::call_stack& cs
-        = octave::__get_call_stack__ ("octave_fcn_handle::load_ascii");
-
-      cs.push (local_scope, 0);
+      cs.push (local_scope);
       frame.add_method (cs, &octave::call_stack::pop);
 
       octave_idx_type len = 0;
@@ -513,7 +501,7 @@
                   if (! is)
                     error ("load: failed to load anonymous function handle");
 
-                  local_scope.assign (name, t2, 0);
+                  interp.assign (name, t2);
                 }
             }
         }
@@ -545,14 +533,11 @@
         return false;
 
       octave_user_function *f = fcn.user_function_value ();
-      octave::symbol_scope f_scope = f->scope ();
 
-      if (! f_scope)
-        error ("internal error, invalid scope");
+      octave_user_function::local_vars_map local_vars
+        = f->local_var_init_vals ();
 
-      octave::tree_anon_scopes tas (f);
-
-      size_t varlen = tas.symrec_map_size ();
+      size_t varlen = local_vars.size ();
 
       if (varlen > 0)
         nmbuf << nm << ' ' << varlen;
@@ -573,13 +558,10 @@
 
       if (varlen > 0)
         {
-          octave::symbol_record::context_id context
-            = f_scope.current_context ();
-
-          for (const auto& name_symrec : tas)
+          for (const auto& nm_val : local_vars)
             {
-              if (! save_binary_data (os, name_symrec.second.varval (context),
-                                      name_symrec.first, "", 0, save_as_floats))
+              if (! save_binary_data (os, nm_val.second, nm_val.first,
+                                      "", 0, save_as_floats))
                 return ! os.fail ();
             }
         }
@@ -653,17 +635,13 @@
       // Set up temporary scope to use for evaluating the text that
       // defines the anonymous function.
 
-      octave::symbol_table& symtab
-        = octave::__get_symbol_table__ ("octave_fcn_handle::load_binary");
+      octave::interpreter& interp
+        = octave::__get_interpreter__ ("octave_fcn_handle::load_binary");
+
+      octave::call_stack& cs = interp.get_call_stack ();
 
       octave::symbol_scope local_scope (ctmp2);
-
-      symtab.set_scope (local_scope);
-
-      octave::call_stack& cs
-        = octave::__get_call_stack__ ("octave_fcn_handle::load_binary");
-
-      cs.push (local_scope, 0);
+      cs.push (local_scope);
       frame.add_method (cs, &octave::call_stack::pop);
 
       if (len > 0)
@@ -681,7 +659,7 @@
               if (! is)
                 error ("load: failed to load anonymous function handle");
 
-              local_scope.force_assign (name, t2);
+              interp.assign (name, t2);
             }
         }
 
@@ -805,14 +783,11 @@
       H5Dclose (data_hid);
 
       octave_user_function *f = fcn.user_function_value ();
-      octave::symbol_scope f_scope = f->scope ();
 
-      if (! f_scope)
-        error ("internal error, invalid scope");
+      octave_user_function::local_vars_map local_vars
+                     = f->local_var_init_vals ();
 
-      octave::tree_anon_scopes tas (f);
-
-      size_t varlen = tas.symrec_map_size ();
+      size_t varlen = local_vars.size ();
 
       if (varlen > 0)
         {
@@ -857,15 +832,10 @@
               return false;
             }
 
-          octave::symbol_record::context_id context
-            = f_scope.current_context ();
-
-          for (const auto& name_symrec : tas)
+          for (const auto& nm_val : local_vars)
             {
-              if (! add_hdf5_data (data_hid,
-                                   name_symrec.second.varval (context),
-                                   name_symrec.first, "", false,
-                                   save_as_floats))
+              if (! add_hdf5_data (data_hid, nm_val.second, nm_val.first,
+                                   "", false, save_as_floats))
                 break;
             }
           H5Gclose (data_hid);
@@ -1161,17 +1131,13 @@
       // Set up temporary scope to use for evaluating the text that
       // defines the anonymous function.
 
-      octave::symbol_table& symtab
-        = octave::__get_symbol_table__ ("octave_fcn_handle::load_hdf5");
+      octave::interpreter& interp
+        = octave::__get_interpreter__ ("octave_fcn_handle::load_hdf5");
+
+      octave::call_stack& cs = interp.get_call_stack ();
 
       octave::symbol_scope local_scope (fcn_tmp);
-
-      symtab.set_scope (local_scope);
-
-      octave::call_stack& cs
-        = octave::__get_call_stack__ ("octave_fcn_handle::load_hdf5");
-
-      cs.push (local_scope, 0);
+      cs.push (local_scope);
       frame.add_method (cs, &octave::call_stack::pop);
 
       if (len > 0 && success)
@@ -1196,7 +1162,7 @@
                                     &dsub) <= 0)
                 error ("load: failed to load anonymous function handle");
 
-              local_scope.force_assign (dsub.name, dsub.tc);
+              interp.assign (dsub.name, dsub.tc);
             }
         }
 
@@ -1781,27 +1747,13 @@
     {
       m.setfield ("file", nm);
 
-      std::list<octave::symbol_record> vars;
-
       octave_user_function *fu = fh->user_function_value ();
-      octave::symbol_scope fu_scope = fu->scope ();
-      octave::symbol_record::context_id context = 0;
-      if (fu_scope)
-        {
-          vars = fu_scope.all_variables ();
-          context = fu_scope.current_context ();
-        }
 
-      size_t varlen = vars.size ();
+      octave_scalar_map ws;
+      for (const auto& nm_val : fu->local_var_init_vals ())
+        ws.assign (nm_val.first, nm_val.second);
 
-      if (varlen > 0)
-        {
-          octave_scalar_map ws;
-          for (const auto& symrec : vars)
-            ws.assign (symrec.name (), symrec.varval (context));
-
-          m.setfield ("workspace", ws);
-        }
+      m.setfield ("workspace", ws);
     }
   else if (fcn->is_user_function () || fcn->is_user_script ())
     {
@@ -2017,11 +1969,6 @@
 
           if (arg_list && arg_list->length () > 0)
             {
-              octave::symbol_scope scope = tw.get_current_scope ();
-
-              octave::symbol_record::context_id context
-                = scope.current_context ();
-
               bool bad = false;
               int nargs = arg_list->length ();
               octave_value_list arg_template (nargs);
@@ -2047,7 +1994,7 @@
                         {
                           arg_mask[iarg] = arginmap[elt_id->name ()];
                         }
-                      else if (elt_id->is_defined (context))
+                      else if (tw.is_defined (elt_id))
                         {
                           arg_template(iarg) = tw.evaluate (elt_id);
                           arg_mask[iarg] = -1;
@@ -2070,7 +2017,7 @@
               if (! bad)
                 {
                   // If the head is a value, use it as root.
-                  if (head_id->is_defined (context))
+                  if (tw.is_defined (head_id))
                     root_val = tw.evaluate (head_id);
                   else
                     {
--- a/libinterp/octave-value/ov-fcn.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/octave-value/ov-fcn.h	Mon Jan 28 18:01:46 2019 +0000
@@ -93,6 +93,8 @@
   virtual octave::sys::time time_checked (void) const
   { return octave::sys::time (static_cast<time_t> (0)); }
 
+  virtual int call_depth (void) const { return 0; }
+
   virtual bool is_subfunction (void) const { return false; }
 
   virtual bool is_class_constructor (const std::string& = "") const
--- a/libinterp/octave-value/ov-usr-fcn.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -128,8 +128,7 @@
     = {{ "scope_info", m_scope ? m_scope.dump () : "0x0" },
        { "file_name", file_name },
        { "time_parsed", t_parsed },
-       { "time_checked", t_checked },
-       { "call_depth", m_call_depth }};
+       { "time_checked", t_checked }};
 
   return octave_value (m);
 }
@@ -185,9 +184,11 @@
 
 octave_user_function::octave_user_function
   (const octave::symbol_scope& scope, octave::tree_parameter_list *pl,
-   octave::tree_parameter_list *rl, octave::tree_statement_list *cl)
+   octave::tree_parameter_list *rl, octave::tree_statement_list *cl,
+   const local_vars_map& lviv)
   : octave_user_code ("", "", scope, cl, ""),
     param_list (pl), ret_list (rl),
+    m_local_var_init_vals (lviv),
     lead_comm (), trail_comm (),
     location_line (0), location_column (0),
     parent_name (), system_fcn_file (false),
@@ -556,11 +557,17 @@
 void
 octave_user_function::restore_warning_states (void)
 {
-  octave_value val = m_scope.varval (".saved_warning_states.");
+  octave::interpreter& interp
+    = octave::__get_interpreter__ ("octave_user_function::restore_warning_states");
+
+  octave::call_stack& cs = interp.get_call_stack ();
+
+  octave_value val
+    = cs.get_auto_fcn_var (octave::stack_frame::SAVED_WARNING_STATES);
 
   if (val.is_defined ())
     {
-      // Fail spectacularly if .saved_warning_states. is not an
+      // Fail spectacularly if SAVED_WARNING_STATES is not an
       // octave_map (or octave_scalar_map) object.
 
       if (! val.isstruct ())
@@ -571,9 +578,6 @@
       Cell ids = m.contents ("identifier");
       Cell states = m.contents ("state");
 
-      octave::interpreter& interp
-        = octave::__get_interpreter__ ("octave_user_function::restore_warning_states");
-
       for (octave_idx_type i = 0; i < m.numel (); i++)
         Fwarning (interp, ovl (states(i), ids(i)));
     }
@@ -619,14 +623,14 @@
 
   octave_value retval;
 
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
   if (nargin == 1)
     {
       octave_value func = args(0);
 
       if (func.is_string ())
         {
+          octave::symbol_table& symtab = interp.get_symbol_table ();
+
           std::string name = func.string_value ();
           func = symtab.find_function (name);
           if (func.is_undefined ())
@@ -657,8 +661,9 @@
     }
   else
     {
-      octave::symbol_scope scope = symtab.require_current_scope ("nargin");
-      retval = scope.varval (".nargin.");
+      octave::call_stack& cs = interp.get_call_stack ();
+
+      retval = cs.get_auto_fcn_var (octave::stack_frame::NARGIN);
 
       if (retval.is_undefined ())
         retval = 0;
@@ -729,14 +734,14 @@
 
   octave_value retval;
 
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
   if (nargin == 1)
     {
       octave_value func = args(0);
 
       if (func.is_string ())
         {
+          octave::symbol_table& symtab = interp.get_symbol_table ();
+
           std::string name = func.string_value ();
           func = symtab.find_function (name);
           if (func.is_undefined ())
@@ -780,11 +785,12 @@
     }
   else
     {
-      if (symtab.at_top_level ())
+      if (interp.at_top_level ())
         error ("nargout: invalid call at top level");
 
-      octave::symbol_scope scope = symtab.require_current_scope ("nargout");
-      retval = scope.varval (".nargout.");
+      octave::call_stack& cs = interp.get_call_stack ();
+
+      retval = cs.get_auto_fcn_var (octave::stack_frame::NARGOUT);
 
       if (retval.is_undefined ())
         retval = 0;
@@ -851,17 +857,20 @@
   if (args.length () != 1)
     print_usage ();
 
-  octave::symbol_table& symtab = interp.get_symbol_table ();
-
-  if (symtab.at_top_level ())
+  if (interp.at_top_level ())
     error ("isargout: invalid call at top level");
 
-  octave::symbol_scope scope = symtab.require_current_scope ("isargout");
+  octave::call_stack& cs = interp.get_call_stack ();
+
+  octave_value tmp;
 
-  int nargout1 = scope.varval (".nargout.").int_value ();
+  int nargout1 = 0;
+  tmp = cs.get_auto_fcn_var (octave::stack_frame::NARGOUT);
+  if (tmp.is_defined ())
+    nargout1 = tmp.int_value ();
 
   Matrix ignored;
-  octave_value tmp = scope.varval (".ignored.");
+  tmp = cs.get_auto_fcn_var (octave::stack_frame::IGNORED);
   if (tmp.is_defined ())
     ignored = tmp.matrix_value ();
 
--- a/libinterp/octave-value/ov-usr-fcn.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/octave-value/ov-usr-fcn.h	Mon Jan 28 18:01:46 2019 +0000
@@ -28,7 +28,6 @@
 #include <ctime>
 
 #include <string>
-#include <stack>
 
 #include "comment-list.h"
 #include "ovl.h"
@@ -44,6 +43,7 @@
 namespace octave
 {
   class file_info;
+  class stack_frame;
   class tree_parameter_list;
   class tree_statement_list;
   class tree_evaluator;
@@ -67,8 +67,7 @@
     : octave_function (nm, ds), m_scope (scope), file_name (fnm),
       t_parsed (static_cast<time_t> (0)),
       t_checked (static_cast<time_t> (0)),
-      m_call_depth (-1), m_file_info (nullptr),
-      cmd_list (cmds)
+      m_file_info (nullptr), cmd_list (cmds)
   { }
 
 public:
@@ -113,13 +112,6 @@
     return octave_value ();
   }
 
-  // XXX FIXME
-  int call_depth (void) const { return m_call_depth; }
-
-  void set_call_depth (int val) { m_call_depth = val; }
-
-  void increment_call_depth (void) { ++m_call_depth; }
-
   virtual std::map<std::string, octave_value> subfunctions (void) const;
 
   octave::tree_statement_list * body (void) { return cmd_list; }
@@ -143,9 +135,6 @@
   // parsed again.
   octave::sys::time t_checked;
 
-  // Used to keep track of recursion depth.
-  int m_call_depth;
-
   // Cached text of function or script code with line offsets
   // calculated.
   octave::file_info *m_file_info;
@@ -197,9 +186,6 @@
 
   void accept (octave::tree_walker& tw);
 
-  // XXX FIXME
-  void set_call_depth (int val) { octave_user_code::set_call_depth (val); }
-
 private:
 
   DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
@@ -212,10 +198,13 @@
 {
 public:
 
+  typedef std::map<std::string, octave_value> local_vars_map;
+
   octave_user_function (const octave::symbol_scope& scope = octave::symbol_scope (),
                         octave::tree_parameter_list *pl = nullptr,
                         octave::tree_parameter_list *rl = nullptr,
-                        octave::tree_statement_list *cl = nullptr);
+                        octave::tree_statement_list *cl = nullptr,
+                        const local_vars_map& lviv = local_vars_map ());
 
   // No copying!
 
@@ -225,12 +214,6 @@
 
   ~octave_user_function (void);
 
-  octave::symbol_record::context_id active_context () const
-  {
-    return is_anonymous_function ()
-      ? 0 : static_cast<octave::symbol_record::context_id>(m_call_depth);
-  }
-
   octave_function * function_value (bool = false) { return this; }
 
   octave_user_function * user_function_value (bool = false) { return this; }
@@ -380,6 +363,11 @@
 
   octave::comment_list * trailing_comment (void) { return trail_comm; }
 
+  const local_vars_map& local_var_init_vals (void) const
+  {
+    return m_local_var_init_vals;
+  }
+
   // If is_special_expr is true, retrieve the sigular expression that forms the
   // body.  May be null (even if is_special_expr is true).
   octave::tree_expression * special_expr (void);
@@ -396,9 +384,6 @@
 
   octave_value dump (void) const;
 
-  // XXX FIXME
-  void set_call_depth (int val) { octave_user_code::set_call_depth (val); }
-
 private:
 
   enum class_ctor_type
@@ -417,6 +402,9 @@
   // this function.
   octave::tree_parameter_list *ret_list;
 
+  // For anonymous function values inherited from parent scope.
+  local_vars_map m_local_var_init_vals;
+
   // The comments preceding the FUNCTION token.
   octave::comment_list *lead_comm;
 
--- a/libinterp/parse-tree/lex.ll	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/lex.ll	Mon Jan 28 18:01:46 2019 +0000
@@ -2471,10 +2471,10 @@
 
   bool
   base_lexer::is_variable (const std::string& name,
-                           const symbol_scope& scope)
+                           const symbol_scope& /*scope*/)
   {
-    return ((scope && scope.is_variable (name))
-            || (m_pending_local_variables.find (name)
+    return (/* (scope && scope.is_variable (name))
+            || */ (m_pending_local_variables.find (name)
                 != m_pending_local_variables.end ()));
   }
 
--- a/libinterp/parse-tree/oct-lvalue.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/oct-lvalue.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -31,16 +31,26 @@
 
 namespace octave
 {
+  bool octave_lvalue::is_defined (void) const
+  {
+    return ! is_black_hole () && m_frame.is_defined (m_sym);
+  }
+
+  bool octave_lvalue::is_undefined (void) const
+  {
+    return ! is_defined ();
+  }
+
+  void octave_lvalue::define (const octave_value& v)
+  {
+    m_frame.assign (m_sym, v);
+  }
+
   void octave_lvalue::assign (octave_value::assign_op op,
                               const octave_value& rhs)
   {
     if (! is_black_hole ())
-      {
-        if (m_idx.empty ())
-          m_sym.assign (op, rhs, m_context);
-        else
-          m_sym.assign (op, m_type, m_idx, rhs, m_context);
-      }
+      m_frame.assign (op, m_sym, m_type, m_idx, rhs);
   }
 
   void octave_lvalue::set_index (const std::string& t,
@@ -70,37 +80,12 @@
   void octave_lvalue::do_unary_op (octave_value::unary_op op)
   {
     if (! is_black_hole ())
-      {
-        if (m_idx.empty ())
-          m_sym.do_non_const_unary_op (op, m_context);
-        else
-          m_sym.do_non_const_unary_op (op, m_type, m_idx, m_context);
-      }
+      m_frame.do_non_const_unary_op (op, m_sym, m_type, m_idx);
   }
 
   octave_value octave_lvalue::value (void) const
   {
-    octave_value retval;
-
-    if (! is_black_hole ())
-      {
-        octave_value val = m_sym.varval (m_context);
-
-        if (m_idx.empty ())
-          retval = val;
-        else
-          {
-            if (val.is_constant ())
-              retval = val.subsref (m_type, m_idx);
-            else
-              {
-                octave_value_list t = val.subsref (m_type, m_idx, 1);
-                if (t.length () > 0)
-                  retval = t(0);
-              }
-          }
-      }
-
-    return retval;
+    return (is_black_hole ()
+            ? octave_value () : m_frame.value (m_sym, m_type, m_idx));
   }
 }
--- a/libinterp/parse-tree/oct-lvalue.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/oct-lvalue.h	Mon Jan 28 18:01:46 2019 +0000
@@ -28,6 +28,7 @@
 #include <string>
 
 #include "ovl.h"
+#include "call-stack.h"
 #include "symrec.h"
 
 namespace octave
@@ -36,13 +37,8 @@
   {
   public:
 
-    octave_lvalue (void)
-      : m_sym (), m_context (0), m_black_hole (false), m_type (),
-        m_idx (), m_nel (1)
-    { }
-
-    octave_lvalue (const symbol_record& sr, symbol_record::context_id context)
-      : m_sym (sr), m_context (context), m_black_hole (false),
+    octave_lvalue (const symbol_record& sr, stack_frame& frame)
+      : m_sym (sr), m_frame (frame), m_black_hole (false),
         m_type (), m_idx (), m_nel (1)
     { }
 
@@ -56,19 +52,14 @@
 
     void mark_black_hole (void) { m_black_hole = true; }
 
-    bool is_defined (void) const
-    {
-      return ! is_black_hole () && m_sym.is_defined (m_context);
-    }
+    bool is_defined (void) const;
 
-    bool is_undefined (void) const
-    {
-      return is_black_hole () || m_sym.is_undefined (m_context);
-    }
+    bool is_undefined (void) const;
 
     bool isstruct (void) const { return value().isstruct (); }
 
-    void define (const octave_value& v) { m_sym.assign (v, m_context); }
+    // OCTAVE_DEPRECATED (5, "foobar, for sure!")
+    void define (const octave_value& v);
 
     void assign (octave_value::assign_op, const octave_value&);
 
@@ -92,7 +83,7 @@
 
     symbol_record m_sym;
 
-    symbol_record::context_id m_context;
+    stack_frame& m_frame;
 
     bool m_black_hole;
 
--- a/libinterp/parse-tree/oct-parse.yy	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/oct-parse.yy	Mon Jan 28 18:01:46 2019 +0000
@@ -502,8 +502,9 @@
 
 identifier      : NAME
                   {
-                    octave::symbol_record sr = $1->sym_rec ();
-                    $$ = new octave::tree_identifier (sr, $1->line (), $1->column ());
+                    $$ = new octave::tree_identifier ($1->sym_rec (),
+                                                      $1->line (),
+                                                      $1->column ());
                   }
                 ;
 
@@ -3467,7 +3468,6 @@
 
         symbol_scope fcn_scope = fcn->scope ();
         fcn_scope.cache_name (tmp);
-        fcn_scope.install_auto_fcn_vars ();
 
         if (lc)
           fcn->stash_leading_comment (lc);
@@ -4978,14 +4978,15 @@
       {
         call_stack& cs = __get_call_stack__ ("source_file");
 
+        frame.add_method (cs, &octave::call_stack::restore_frame,
+                          cs.current_frame ());
+
         if (context == "caller")
           cs.goto_caller_frame ();
         else if (context == "base")
           cs.goto_base_frame ();
         else
           error ("source: context must be \"caller\" or \"base\"");
-
-        frame.add_method (cs, &call_stack::pop);
       }
 
     // Find symbol name that would be in symbol_table, if it were loaded.
@@ -5001,8 +5002,8 @@
     std::string full_name = sys::canonicalize_file_name (file_name);
 
     // Check if this file is already loaded (or in the path)
-    symbol_scope curr_scope = __get_current_scope__ ("source_file");
-    octave_value ov_code = curr_scope.find (symbol);
+    symbol_table& symtab = __get_symbol_table__ ("source_file");
+    octave_value ov_code = symtab.fcn_table_find (symbol);
 
     // For compatibility with Matlab, accept both scripts and
     // functions.
@@ -5556,6 +5557,9 @@
 
   octave::call_stack& cs = interp.get_call_stack ();
 
+  frame.add_method (cs, &octave::call_stack::restore_frame,
+                    cs.current_frame ());
+
   if (context == "caller")
     cs.goto_caller_frame ();
   else if (context == "base")
@@ -5563,8 +5567,6 @@
   else
     error ("assignin: CONTEXT must be \"caller\" or \"base\"");
 
-  frame.add_method (cs, &octave::call_stack::pop);
-
   std::string nm = args(1).xstring_value ("assignin: VARNAME must be a string");
 
   if (octave::valid_identifier (nm))
@@ -5576,10 +5578,7 @@
       if (octave::iskeyword (nm))
         error ("assignin: invalid assignment to keyword '%s'", nm.c_str ());
 
-      octave::symbol_scope scope = interp.get_current_scope ();
-
-      if (scope)
-        scope.assign (nm, args(2));
+      interp.assign (nm, args(2));
     }
   else
     error ("assignin: invalid variable name in argument VARNAME");
@@ -5615,6 +5614,9 @@
 
   octave::call_stack& cs = interp.get_call_stack ();
 
+  frame.add_method (cs, &octave::call_stack::restore_frame,
+                    cs.current_frame ());
+
   if (context == "caller")
     cs.goto_caller_frame ();
   else if (context == "base")
@@ -5622,8 +5624,6 @@
   else
     error ("evalin: CONTEXT must be \"caller\" or \"base\"");
 
-  frame.add_method (cs, &octave::call_stack::pop);
-
   if (nargin > 2)
     {
       frame.protect_var (buffer_error_messages);
--- a/libinterp/parse-tree/pt-anon-scopes.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-anon-scopes.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -33,46 +33,38 @@
 
 namespace octave
 {
-  tree_anon_scopes::tree_anon_scopes (octave_user_function *f)
-    : scopes (), merged_tables ()
+  tree_anon_scopes::tree_anon_scopes (tree_anon_fcn_handle& anon_fh)
+    : m_params (), m_vars ()
   {
-    if (f)
-      {
-        if (! f->is_anonymous_function ())
-          panic_impossible ();
-
-        // Collect the scope of the outer anonymous function.
-
-        stash_scope_if_valid (f->scope ());
-
-        // Further walk the tree to find nested definitions of further
-        // anonymous functions.
-
-        tree_statement_list *cmd_list = f->body ();
-
-        if (cmd_list)
-          cmd_list->accept (*this);
-
-        // Collect symbol records of all collected scopes.
-
-        merge_tables ();
-      }
+    visit_anon_fcn_handle (anon_fh);
   }
 
   void
   tree_anon_scopes::visit_anon_fcn_handle (tree_anon_fcn_handle& afh)
   {
-    // Collect the scope of this anonymous function.
+    tree_parameter_list *param_list = afh.parameter_list ();
+    tree_expression *expr = afh.expression ();
+
+    // Collect names of parameters.
 
-    stash_scope_if_valid (afh.scope ());
+    if (param_list)
+      {
+        std::list<std::string> pnames = param_list->variable_names ();
+
+        for (const auto& nm : pnames)
+          m_params.insert (nm);
 
-    // Further walk the tree to find nested definitions of further
-    // anonymous functions.
+        // Hmm, should this be included in the list returned from
+        // tree_parameter_list::variable_names?
+        if (param_list->takes_varargs ())
+          m_params.insert ("varargin");
+      }
 
-    tree_expression *e = afh.expression ();
+    // Further walk the tree to find free variables in this expression
+    // and any nested definitions of additional anonymous functions.
 
-    if (e)
-      e->accept (*this);
+    if (expr)
+      expr->accept (*this);
   }
 
   // The rest of visit_... methods is only for walking the tree. Many of
@@ -191,8 +183,12 @@
   }
 
   void
-  tree_anon_scopes::visit_identifier (tree_identifier& /* id */)
+  tree_anon_scopes::visit_identifier (tree_identifier& id)
   {
+    std::string nm = id.name ();
+
+    if (m_params.find (nm) == m_params.end ())
+      m_vars.insert (nm);
   }
 
   void
@@ -417,17 +413,5 @@
   {
     panic_impossible ();
   }
-
-  void
-  tree_anon_scopes::merge_tables (void)
-  {
-    for (const auto& sc : scopes)
-      {
-        symrec_list vars = sc.all_variables ();
-
-        for (const auto& symrec : vars)
-          merged_tables[symrec.name ()] = symrec;
-      }
-  }
 }
 
--- a/libinterp/parse-tree/pt-anon-scopes.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-anon-scopes.h	Mon Jan 28 18:01:46 2019 +0000
@@ -24,8 +24,10 @@
 #if !defined (octave_pt_anon_scopes_h)
 #define octave_pt_anon_scopes_h 1
 
+#include <set>
+#include <string>
+
 #include "pt-walk.h"
-#include "ov-usr-fcn.h"
 
 namespace octave
 {
@@ -37,33 +39,9 @@
   {
   public:
 
-    tree_anon_scopes (void) : scopes (), merged_tables () { }
-
-    tree_anon_scopes (octave_user_function *);
-
-    tree_anon_scopes& operator = (tree_anon_scopes&& tas)
-    {
-      scopes = std::move (tas.scopes);
-      merged_tables = std::move (tas.merged_tables);
-      return *this;
-    }
-
-    typedef std::map<std::string, symbol_record> symrec_map;
+    tree_anon_scopes (void) = delete;
 
-    symrec_map::const_iterator begin (void)
-    {
-      return merged_tables.cbegin ();
-    }
-
-    symrec_map::const_iterator end (void)
-    {
-      return merged_tables.cend ();
-    }
-
-    unsigned int symrec_map_size (void)
-    {
-      return merged_tables.size ();
-    }
+    tree_anon_scopes (tree_anon_fcn_handle& anon_fh);
 
     // No copying!
 
@@ -71,7 +49,11 @@
 
     tree_anon_scopes& operator = (const tree_anon_scopes&) = delete;
 
-    ~tree_anon_scopes (void) { }
+    ~tree_anon_scopes (void) = default;
+
+    std::set<std::string> fcn_parameters (void) const { return m_params; }
+
+    std::set<std::string> free_variables (void) const { return m_vars; }
 
     // The following methods, though public, don't belong to the
     // intended user interface of this class.
@@ -160,27 +142,11 @@
 
   private:
 
-    void stash_scope_if_valid (const symbol_scope& sc)
-    {
-      if (sc)
-        scopes.push_back (sc);
-      else
-        error ("internal error, invalid scope");
-    }
+    // Variable names that are function parameters.
+    std::set<std::string> m_params;
 
-    // The scope of this anonymous function and the collected scopes
-    // of all anonymous functions whose definitions are nested in the
-    // current anonymous function definition.
-
-    std::vector<symbol_scope> scopes;
-
-    // Symbol records of all collected scopes are merged over variable names.
-
-    typedef std::list<symbol_record> symrec_list;
-
-    void merge_tables (void);
-
-    symrec_map merged_tables;
+    // Other variable names.
+    std::set<std::string> m_vars;
   };
 }
 
--- a/libinterp/parse-tree/pt-decl.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-decl.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -41,6 +41,13 @@
 {
   // Declarations (global, static, etc.).
 
+  tree_decl_elt::tree_decl_elt (tree_identifier *i, tree_expression *e)
+    : type (unknown), m_id (i), m_expr (e)
+  {
+    if (! m_id)
+      error ("tree_decl_elt: invalid ID");
+  }
+
   tree_decl_elt::~tree_decl_elt (void)
   {
     delete m_id;
@@ -50,7 +57,7 @@
   tree_decl_elt *
   tree_decl_elt::dup (symbol_scope& scope) const
   {
-    return new tree_decl_elt (m_id ? m_id->dup (scope) : nullptr,
+    return new tree_decl_elt (m_id->dup (scope),
                               m_expr ? m_expr->dup (scope) : nullptr);
   }
 
--- a/libinterp/parse-tree/pt-decl.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-decl.h	Mon Jan 28 18:01:46 2019 +0000
@@ -55,8 +55,7 @@
       persistent
     };
 
-    tree_decl_elt (tree_identifier *i = nullptr, tree_expression *e = nullptr)
-      : type (unknown), m_id (i), m_expr (e) { }
+    tree_decl_elt (tree_identifier *i, tree_expression *e = nullptr);
 
     // No copying!
 
@@ -66,27 +65,16 @@
 
     ~tree_decl_elt (void);
 
-    bool is_defined (symbol_record::context_id context)
+    void mark_as_formal_parameter (void)
     {
-      return m_id ? m_id->is_defined (context) : false;
+      m_id->mark_as_formal_parameter ();
     }
 
-    bool is_variable (symbol_record::context_id context)
-    {
-      return m_id ? m_id->is_variable (context) : false;
-    }
-
-    void mark_as_formal_parameter (void)
-    {
-      if (m_id)
-        m_id->mark_as_formal_parameter ();
-    }
-
-    bool lvalue_ok (void) { return m_id ? m_id->lvalue_ok () : false; }
+    bool lvalue_ok (void) { return m_id->lvalue_ok (); }
 
     octave_lvalue lvalue (tree_evaluator& tw)
     {
-      return m_id ? m_id->lvalue (tw) : octave_lvalue ();
+      return m_id->lvalue (tw);
     }
 
     void mark_global (void) { type = global; }
@@ -97,7 +85,7 @@
 
     tree_identifier * ident (void) { return m_id; }
 
-    std::string name (void) const { return m_id ? m_id->name () : ""; }
+    std::string name (void) const { return m_id->name (); }
 
     tree_expression * expression (void) { return m_expr; }
 
--- a/libinterp/parse-tree/pt-eval.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-eval.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -48,6 +48,7 @@
 #include "ov-cx-sparse.h"
 #include "profiler.h"
 #include "pt-all.h"
+#include "pt-anon-scopes.h"
 #include "pt-eval.h"
 #include "pt-tm-const.h"
 #include "symtab.h"
@@ -62,6 +63,11 @@
 {
   // Normal evaluator.
 
+  bool tree_evaluator::at_top_level (void) const
+  {
+    return m_call_stack.at_top_level ();
+  }
+
   void
   tree_evaluator::reset (void)
   {
@@ -84,8 +90,6 @@
                         ? new lexer (m_interpreter)
                         : new lexer (stdin, m_interpreter));
 
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
     do
       {
         try
@@ -94,7 +98,7 @@
 
             repl_parser.reset ();
 
-            if (symtab.at_top_level ())
+            if (at_top_level ())
               reset_debug_state ();
 
             retval = repl_parser.run ();
@@ -223,24 +227,14 @@
                     if (silent)
                       expr->set_print_flag (false);
 
+                    retval = evaluate_n (expr, nargout);
+
                     bool do_bind_ans = false;
 
                     if (expr->is_identifier ())
-                      {
-                        symbol_scope scope = get_current_scope ();
-
-                        symbol_record::context_id context
-                          = scope.current_context ();
-
-                        tree_identifier *id
-                          = dynamic_cast<tree_identifier *> (expr);
-
-                        do_bind_ans = (! id->is_variable (context));
-                      }
+                      do_bind_ans = ! is_variable (expr);
                     else
-                      do_bind_ans = (! expr->is_assignment_expression ());
-
-                    retval = evaluate_n (expr, nargout);
+                      do_bind_ans = ! expr->is_assignment_expression ();
 
                     if (do_bind_ans && ! retval.empty ())
                       bind_ans (retval(0), expr->print_result ());
@@ -298,19 +292,10 @@
 
     symbol_scope af_scope = anon_fh.scope ();
 
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symbol_scope af_parent_scope;
-    if (anon_fh.has_parent_scope ())
-      af_parent_scope = symtab.current_scope ();
-
     symbol_scope new_scope;
     if (af_scope)
       new_scope = af_scope.dup ();
 
-    if (new_scope && af_parent_scope)
-      new_scope.inherit (af_parent_scope);
-
     tree_parameter_list *param_list_dup
       = param_list ? param_list->dup (new_scope) : nullptr;
 
@@ -318,6 +303,11 @@
 
     tree_statement_list *stmt_list = nullptr;
 
+    symbol_scope parent_scope = get_current_scope ();
+
+    new_scope.set_parent (parent_scope);
+    new_scope.set_primary_parent (parent_scope);
+
     if (expr)
       {
         tree_expression *expr_dup = expr->dup (new_scope);
@@ -325,11 +315,25 @@
         stmt_list = new tree_statement_list (stmt);
       }
 
+    tree_anon_scopes anon_fcn_ctx (anon_fh);
+
+    std::set<std::string> free_vars = anon_fcn_ctx.free_variables ();
+
+    octave_user_function::local_vars_map local_var_init_vals;
+
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    for (auto& name : free_vars)
+      {
+        octave_value val = frame.varval (name);
+
+        if (val.is_defined ())
+          local_var_init_vals[name] = val;
+      }
+
     octave_user_function *af
       = new octave_user_function (new_scope, param_list_dup, ret_list,
-                                  stmt_list);
-
-    new_scope.set_parent (af_parent_scope);
+                                  stmt_list, local_var_init_vals);
 
     octave_function *curr_fcn = m_call_stack.current ();
 
@@ -341,19 +345,22 @@
         af->stash_parent_fcn_name (curr_fcn->name ());
         af->stash_dir_name (curr_fcn->dir_name ());
 
+        // The following is needed so that class method dispatch works
+        // properly for anonymous functions that wrap class methods.
+
         if (curr_fcn->is_class_method () || curr_fcn->is_class_constructor ())
           af->stash_dispatch_class (curr_fcn->dispatch_class ());
+
+        af->stash_fcn_file_name (curr_fcn->fcn_file_name ());
       }
 
     af->mark_as_anonymous_function ();
 
-    // FIXME: these should probably come from ANON_FH.
-    //    af->stash_fcn_file_name (expr.file_name ());
-    //    af->stash_fcn_location (expr.line (), expr.column ());
-
     octave_value ov_fcn (af);
 
-    octave_value fh (octave_fcn_binder::maybe_binder (ov_fcn, *this));
+    // octave_value fh (octave_fcn_binder::maybe_binder (ov_fcn, *this));
+
+    octave_value fh (new octave_fcn_handle (ov_fcn, octave_fcn_handle::anonymous));
 
     push_result (fh);
   }
@@ -751,6 +758,148 @@
     return id ? evaluate (id).storable_value () : octave_value ();
   }
 
+  bool
+  tree_evaluator::is_variable (const std::string& name) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.is_variable (name);
+  }
+
+  bool
+  tree_evaluator::is_local_variable (const std::string& name) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.is_local_variable (name);
+  }
+
+  bool
+  tree_evaluator::is_variable (const tree_expression *expr) const
+  {
+    if (expr->is_identifier ())
+      {
+        const tree_identifier *id
+          = dynamic_cast<const tree_identifier *> (expr);
+
+        if (id->is_black_hole ())
+          return false;
+
+        return is_variable (id->symbol ());
+      }
+
+    return false;
+  }
+
+  bool
+  tree_evaluator::is_defined (const tree_expression *expr) const
+  {
+    if (expr->is_identifier ())
+      {
+        const tree_identifier *id
+          = dynamic_cast<const tree_identifier *> (expr);
+
+        return is_defined (id->symbol ());
+      }
+
+    return false;
+  }
+
+  bool
+  tree_evaluator::is_variable (const symbol_record& sym) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.is_variable (sym);
+  }
+
+  bool
+  tree_evaluator::is_defined (const symbol_record& sym) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.is_defined (sym);
+  }
+
+  bool tree_evaluator::is_global (const std::string& name) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.is_global (name);
+  }
+
+  octave_value
+  tree_evaluator::varval (const symbol_record& sym) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.varval (sym);
+  }
+
+  octave_value
+  tree_evaluator::varval (const std::string& name) const
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.varval (name);
+  }
+
+  void tree_evaluator::install_variable (const std::string& name,
+                                         const octave_value& value,
+                                         bool global)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    return frame.install_variable (name, value, global);
+  }
+
+  octave_value
+  tree_evaluator::global_varval (const std::string& name) const
+  {
+    return m_call_stack.global_varval (name);
+  }
+
+  void
+  tree_evaluator::global_assign (const std::string& name,
+                                 const octave_value& val)
+  {
+    m_call_stack.global_varref (name) = val;
+  }
+
+  octave_value
+  tree_evaluator::top_level_varval (const std::string& name) const
+  {
+    return m_call_stack.get_top_level_value (name);
+  }
+
+  void
+  tree_evaluator::top_level_assign (const std::string& name,
+                                const octave_value& val)
+  {
+    m_call_stack.set_top_level_value (name, val);
+  }
+
+  void
+  tree_evaluator::assign (const std::string& name, const octave_value& val)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    frame.assign (name, val);
+  }
+
+  void
+  tree_evaluator::set_auto_fcn_var (stack_frame::auto_var_type avt,
+                                    const octave_value& val)
+  {
+    m_call_stack.set_auto_fcn_var (avt, val);
+  }
+
+  octave_value
+  tree_evaluator::get_auto_fcn_var (stack_frame::auto_var_type avt) const
+  {
+    return m_call_stack.get_auto_fcn_var (avt);
+  }
+
   void
   tree_evaluator::define_parameter_list_from_arg_vector
     (tree_parameter_list *param_list, const octave_value_list& args)
@@ -949,17 +1098,13 @@
       return varargout;
     else if (nargout <= len)
       {
-        symbol_scope scope = get_current_scope ();
-
-        symbol_record::context_id context = scope.current_context ();
-
         octave_value_list retval (nargout);
 
         int i = 0;
 
         for (tree_decl_elt *elt : *ret_list)
           {
-            if (elt->is_defined (context))
+            if (is_defined (elt->ident ()))
               {
                 octave_value tmp = evaluate (elt);
                 retval(i) = tmp;
@@ -1040,12 +1185,148 @@
     return false;
   }
 
-  symbol_scope
-  tree_evaluator::get_current_scope (void)
+  symbol_scope tree_evaluator::get_top_scope (void) const
+  {
+    return m_call_stack.top_scope ();
+  }
+
+  symbol_scope tree_evaluator::get_current_scope (void) const
+  {
+    return m_call_stack.current_scope ();
+  }
+
+  octave_value tree_evaluator::find (const std::string& name)
+  {
+    const stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    octave_value val = frame.varval (name);
+
+    if (val.is_defined ())
+      return val;
+
+    // Subfunction.  I think it only makes sense to check for
+    // subfunctions if we are currently executing a function defined
+    // from a .m file.
+
+    octave_value fcn = frame.find_subfunction (name);
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    return symtab.fcn_table_find (name, ovl ());
+  }
+
+  void tree_evaluator::clear_objects (void)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    frame.clear_objects ();
+  }
+
+  void tree_evaluator::clear_variable (const std::string& name)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    frame.clear_variable (name);
+  }
+
+  void tree_evaluator::clear_variable_pattern (const std::string& pattern)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    frame.clear_variable_pattern (pattern);
+  }
+
+  void tree_evaluator::clear_variable_regexp (const std::string& pattern)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    frame.clear_variable_regexp (pattern);
+  }
+
+  void tree_evaluator::clear_variables (void)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    frame.clear_variables ();
+  }
+
+  void tree_evaluator::clear_global_variable (const std::string& name)
   {
+    m_call_stack.clear_global_variable (name);
+  }
+
+  void
+  tree_evaluator::clear_global_variable_pattern (const std::string& pattern)
+  {
+    m_call_stack.clear_global_variable_pattern (pattern);
+  }
+
+  void tree_evaluator::clear_global_variable_regexp(const std::string& pattern)
+  {
+    m_call_stack.clear_global_variable_regexp (pattern);
+  }
+
+  void tree_evaluator::clear_global_variables (void)
+  {
+    m_call_stack.clear_global_variables ();
+  }
+
+  void tree_evaluator::clear_all (bool force)
+  {
+    // FIXME: should this also clear objects?
+
+    clear_variables ();
+    clear_global_variables ();
+
     symbol_table& symtab = m_interpreter.get_symbol_table ();
 
-    return symtab.current_scope ();
+    symtab.clear_functions (force);
+  }
+
+  void tree_evaluator::clear_symbol (const std::string& name)
+  {
+    // FIXME: are we supposed to do both here?
+
+    clear_variable (name);
+
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    symtab.clear_function (name);
+  }
+
+  void tree_evaluator::clear_symbol_pattern (const std::string& pattern)
+  {
+    // FIXME: are we supposed to do both here?
+
+    clear_variable_pattern (pattern);
+
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    symtab.clear_function_pattern (pattern);
+  }
+
+  void tree_evaluator::clear_symbol_regexp (const std::string& pattern)
+  {
+    // FIXME: are we supposed to do both here?
+
+    clear_variable_regexp (pattern);
+
+    symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+    symtab.clear_function_regexp (pattern);
+  }
+
+  std::list<std::string> tree_evaluator::global_variable_names (void) const
+  {
+    return m_call_stack.global_variable_names ();
+  }
+
+  std::list<std::string> tree_evaluator::variable_names (void) const
+  {
+    return m_call_stack.variable_names ();
   }
 
   // Return a pointer to the user-defined function FNAME.  If FNAME is empty,
@@ -1161,60 +1442,9 @@
     if (id)
       {
         if (elt.is_global ())
-          {
-            std::string name = id->name ();
-
-            symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-            symbol_scope global_scope = symtab.global_scope ();
-
-            symbol_record global_sr = global_scope.find_symbol (name);
-
-            // FIXME: Hmmm.  Seems like this should happen automatically
-            // for symbols coming from the global scope...
-            global_sr.mark_global ();
-
-            symbol_scope scope = symtab.current_scope ();
-
-            if (! scope.is_global (name))
-              {
-                octave_value val = scope.varval (name);
-
-                bool local_val_is_defined = val.is_defined ();
-
-                if (local_val_is_defined)
-                  {
-                    warning_with_id ("Octave:global-local-conflict",
-                                     "global: '%s' is defined in the current scope.\n",
-                                     name.c_str ());
-                    warning_with_id ("Octave:global-local-conflict",
-                                     "global: in a future version, global variables must be declared before use.\n");
-
-                    // If the symbol is defined in the local but not the
-                    // global scope, then use the local value as the
-                    // initial value.  This value will also override any
-                    // initializer in the global statement.
-                    octave_value global_val = global_scope.varval (name);
-
-                    if (global_val.is_defined ())
-                      {
-                        warning_with_id ("Octave:global-local-conflict",
-                                         "global: global value overrides existing local value");
-                      }
-                    else
-                      {
-                        warning_with_id ("Octave:global-local-conflict",
-                                         "global: existing local value used to initialize global variable");
-
-                        global_scope.assign (name, val);
-                      }
-                  }
-
-                id->link_to_global (global_scope, global_sr);
-              }
-          }
+          m_call_stack.make_global (id->symbol ());
         else if (elt.is_persistent ())
-          id->mark_persistent ();
+          m_call_stack.make_persistent (id->symbol ());
         else
           error ("declaration list element not global or persistent");
 
@@ -1478,12 +1708,7 @@
 
     unwind_protect frame;
 
-    // XXX FIXME
-    frame.add_method (user_script, &octave_user_script::set_call_depth,
-                      user_script.call_depth ());
-    user_script.increment_call_depth ();
-
-    if (user_script.call_depth () >= max_recursion_depth ())
+    if (m_call_stack.size () >= static_cast<size_t> (m_max_recursion_depth))
       error ("max_recursion_depth exceeded");
 
     m_call_stack.push (&user_script, &frame);
@@ -1503,10 +1728,6 @@
 
     profiler::enter<octave_user_script> block (m_profiler, user_script);
 
-    symbol_scope script_scope = user_script.scope ();
-    frame.add_method (script_scope, &symbol_scope::unbind_script_symbols);
-    script_scope.bind_script_symbols (get_current_scope ());
-
     if (echo ())
       push_echo_state (frame, tree_evaluator::ECHO_SCRIPTS, file_name);
 
@@ -1565,56 +1786,31 @@
 
     unwind_protect frame;
 
-    // XXX FIXME
-    frame.add_method (user_function, &octave_user_function::set_call_depth,
-                      user_function.call_depth ());
-    user_function.increment_call_depth ();
-
-    if (user_function.call_depth () >= max_recursion_depth ())
+    if (m_call_stack.size () >= static_cast<size_t> (m_max_recursion_depth))
       error ("max_recursion_depth exceeded");
 
     // Save old and set current symbol table context, for
     // eval_undefined_error().
 
-    symbol_scope fcn_scope = user_function.scope ();
-
-    symbol_record::context_id context = user_function.active_context ();
-
-    m_call_stack.push (&user_function, &frame, fcn_scope, context);
+    m_call_stack.push (&user_function, &frame);
 
     frame.protect_var (Vtrack_line_num);
     // update source line numbers, even if debugging
     Vtrack_line_num = true;
+
     frame.add_method (m_call_stack, &call_stack::pop);
 
-    if (user_function.call_depth () > 0
-        && ! user_function.is_anonymous_function ())
-      {
-        fcn_scope.push_context ();
-
-#if 0
-        std::cerr << name () << " scope: " << fcn_scope
-                  << " call depth: " << user_function.call_depth ()
-                  << " context: " << fcn_scope.current_context () << std::endl;
-#endif
-
-        frame.add_method (fcn_scope, &symbol_scope::pop_context);
-      }
-
-    bind_auto_fcn_vars (fcn_scope, xargs.name_tags (), args.length (),
+    bind_auto_fcn_vars (xargs.name_tags (), args.length (),
                         nargout, user_function.takes_varargs (),
                         user_function.all_va_args (args));
 
+    if (user_function.is_anonymous_function ())
+      init_local_fcn_vars (user_function);
+
     tree_parameter_list *param_list = user_function.parameter_list ();
 
     if (param_list && ! param_list->varargs_only ())
-      {
-#if 0
-        std::cerr << "defining param list, scope: " << fcn_scope
-                  << ", context: " << fcn_scope.current_context () << std::endl;
-#endif
-        define_parameter_list_from_arg_vector (param_list, args);
-      }
+      define_parameter_list_from_arg_vector (param_list, args);
 
     // For classdef constructor, pre-populate the output arguments
     // with the pre-initialized object instance, extracted above.
@@ -1634,32 +1830,17 @@
     // Doing so decrements the reference counts on the values of local
     // variables that are also named function parameters.
 
-    if (param_list)
-      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
-                        param_list);
+    //    if (param_list)
+    //      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
+    //                        param_list);
 
     // Force return list to be undefined when this function exits.
     // Doing so decrements the reference counts on the values of local
     // variables that are also named values returned by this function.
 
-    if (ret_list)
-      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
-                        ret_list);
-
-    if (user_function.call_depth () == 0)
-      {
-        // Force symbols to be undefined again when this function
-        // exits.
-        //
-        // This cleanup function is added to the unwind_protect stack
-        // after the calls to clear the parameter lists so that local
-        // variables will be cleared before the parameter lists are
-        // cleared.  That way, any function parameters that have been
-        // declared global will be unmarked as global before they are
-        // undefined by the clear_param_list cleanup function.
-
-        frame.add_method (fcn_scope, &symbol_scope::refresh);
-      }
+    //    if (ret_list)
+    //      frame.add_method (this, &tree_evaluator::undefine_parameter_list,
+    //                        ret_list);
 
     frame.add_method (&user_function,
                       &octave_user_function::restore_warning_states);
@@ -1669,6 +1850,8 @@
     frame.protect_var (m_statement_context);
     m_statement_context = SC_FUNCTION;
 
+    frame.add_method (m_call_stack, &call_stack::clear_current_frame_values);
+
     {
       profiler::enter<octave_user_function> block (m_profiler, user_function);
 
@@ -1709,7 +1892,7 @@
 
         if (ret_list->takes_varargs ())
           {
-            octave_value varargout_varval = fcn_scope.varval ("varargout");
+            octave_value varargout_varval = varval ("varargout");
 
             if (varargout_varval.is_defined ())
               varargout = varargout_varval.xcell_value ("varargout must be a cell array object");
@@ -1752,10 +1935,7 @@
         // Make sure that any variable with the same name as the new
         // function is cleared.
 
-        symbol_scope scope = symtab.current_scope ();
-
-        if (scope)
-          scope.assign (nm);
+        assign (nm);
       }
   }
 
@@ -1764,13 +1944,16 @@
   {
     octave_value_list retval;
 
-    symbol_scope scope = get_current_scope ();
-
-    symbol_record::context_id context = scope.current_context ();
-
     symbol_record sym = expr.symbol ();
 
-    octave_value val = sym.find (context);
+    octave_value val = varval (sym);
+
+    if (val.is_undefined ())
+      {
+        symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+        val = symtab.find_function (sym.name ());
+      }
 
     if (val.is_defined ())
       {
@@ -1967,11 +2150,7 @@
       {
         tree_identifier *id = dynamic_cast<tree_identifier *> (expr);
 
-        symbol_scope scope = get_current_scope ();
-
-        symbol_record::context_id context = scope.current_context ();
-
-        if (! id->is_variable (context))
+        if (! is_variable (expr))
           {
             octave_value_list first_args;
 
@@ -1994,10 +2173,19 @@
                 first_args.stash_name_tags (anm);
               }
 
+            symbol_record sym = id->symbol ();
+
+            octave_value val = varval (sym);
+
+            if (val.is_undefined ())
+              {
+                symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+                val = symtab.find_function (sym.name (), first_args);
+              }
+
             octave_function *fcn = nullptr;
 
-            octave_value val = id->do_lookup (context, first_args);
-
             if (val.is_function ())
               fcn = val.function_value (true);
 
@@ -2780,19 +2968,9 @@
                     bool do_bind_ans = false;
 
                     if (expr->is_identifier ())
-                      {
-                        symbol_scope scope = get_current_scope ();
-
-                        symbol_record::context_id context
-                          = scope.current_context ();
-
-                        tree_identifier *id
-                          = dynamic_cast<tree_identifier *> (expr);
-
-                        do_bind_ans = (! id->is_variable (context));
-                      }
+                      do_bind_ans = ! is_variable (expr);
                     else
-                      do_bind_ans = (! expr->is_assignment_expression ());
+                      do_bind_ans = ! expr->is_assignment_expression ();
 
                     if (do_bind_ans)
                       bind_ans (tmp_result, expr->print_result ()
@@ -3247,9 +3425,7 @@
           }
         else
           {
-            symbol_scope scope = get_current_scope ();
-
-            scope.force_assign (ans, val);
+            assign (ans, val);
 
             if (print)
               {
@@ -3462,10 +3638,7 @@
 
     symbol_scope scope = get_current_scope ();
 
-    symbol_record::context_id ctxt = scope.current_context ();
-
-    if (expr->is_identifier ()
-        && dynamic_cast<const tree_identifier *> (expr)->is_variable (ctxt))
+    if (is_variable (expr))
       {
         std::string var = expr->name ();
 
@@ -3768,46 +3941,30 @@
     return quit;
   }
 
-  void tree_evaluator::bind_auto_fcn_vars (symbol_scope& scope,
-                                           const string_vector& arg_names,
+  void tree_evaluator::bind_auto_fcn_vars (const string_vector& arg_names,
                                            int nargin, int nargout,
                                            bool takes_varargs,
                                            const octave_value_list& va_args)
   {
-    scope.force_assign (".argn.", Cell (arg_names));
-    scope.mark_hidden (".argn.");
-    scope.mark_automatic (".argn.");
-
-    scope.force_assign (".ignored.", ignored_fcn_outputs ());
-    scope.mark_hidden (".ignored.");
-    scope.mark_automatic (".ignored.");
-
-    scope.force_assign (".nargin.", nargin);
-    scope.mark_hidden (".nargin.");
-    scope.mark_automatic (".nargin.");
-
-    scope.force_assign (".nargout.", nargout);
-    scope.mark_hidden (".nargout.");
-    scope.mark_automatic (".nargout.");
-
-    scope.force_assign (".saved_warning_states.", octave_value ());
-    scope.mark_hidden (".saved_warning_states.");
-    scope.mark_automatic (".saved_warning_states.");
-
-    if (! arg_names.empty ())
-      {
-        // It is better to save this in the hidden variable .argn. and
-        // then use that in the inputname function instead of using argn,
-        // which might be redefined in a function.  Keep the old argn name
-        // for backward compatibility of functions that use it directly.
-
-        charMatrix chm (arg_names, string_fill_char ());
-        scope.force_assign ("argn", chm);
-        scope.mark_automatic ("argn");
-      }
+    set_auto_fcn_var (stack_frame::ARG_NAMES, Cell (arg_names));
+    set_auto_fcn_var (stack_frame::IGNORED, ignored_fcn_outputs ());
+    set_auto_fcn_var (stack_frame::NARGIN, nargin);
+    set_auto_fcn_var (stack_frame::NARGOUT, nargout);
+    set_auto_fcn_var (stack_frame::SAVED_WARNING_STATES, octave_value ());
 
     if (takes_varargs)
-      scope.assign ("varargin", va_args.cell_value ());
+      assign ("varargin", va_args.cell_value ());
+  }
+
+  void tree_evaluator::init_local_fcn_vars (octave_user_function& user_fcn)
+  {
+    stack_frame& frame = m_call_stack.get_current_stack_frame ();
+
+    const octave_user_function::local_vars_map& lviv
+      = user_fcn.local_var_init_vals ();
+
+    for (const auto& nm_ov : lviv)
+      frame.assign (nm_ov.first, nm_ov.second);
   }
 }
 
@@ -3864,8 +4021,7 @@
 
 @table @code
 @item %a
-Prints attributes of variables (g=global, p=persistent, f=formal parameter,
-a=automatic variable).
+Prints attributes of variables (g=global, p=persistent, f=formal parameter).
 
 @item %b
 Prints number of bytes occupied by variables.
--- a/libinterp/parse-tree/pt-eval.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-eval.h	Mon Jan 28 18:01:46 2019 +0000
@@ -129,7 +129,7 @@
       : m_interpreter (interp), m_statement_context (SC_OTHER),
         m_result_type (RT_UNDEFINED), m_expr_result_value (),
         m_expr_result_value_list (), m_lvalue_list_stack (),
-        m_nargout_stack (), m_bp_table (*this), m_call_stack (interp),
+        m_nargout_stack (), m_bp_table (*this), m_call_stack (*this),
         m_profiler (), m_current_frame (0), m_debug_mode (false),
         m_quiet_breakpoint_flag (false), m_max_recursion_depth (256),
         m_whos_line_format ("  %a:4; %ln:6; %cs:16:6:1;  %rb:12;  %lc:-1;\n"),
@@ -138,8 +138,9 @@
         m_echo_state (false), m_echo_file_name (), m_echo_file_pos (1),
         m_echo_files (), m_in_loop_command (false),
         m_breaking (0), m_continuing (0), m_returning (0),
-        m_indexed_object (nullptr), m_index_position (0), m_num_indices (0)
-    { }
+        m_indexed_object (nullptr), m_index_position (0),
+        m_num_indices (0)
+      { }
 
     // No copying!
 
@@ -149,6 +150,8 @@
 
     ~tree_evaluator (void) = default;
 
+    bool at_top_level (void) const;
+
     void reset (void);
 
     int repl (bool interactive);
@@ -366,6 +369,45 @@
 
     octave_value evaluate (tree_decl_elt *);
 
+    void install_variable (const std::string& name,
+                           const octave_value& value, bool global);
+
+    octave_value global_varval (const std::string& name) const;
+
+    void global_assign (const std::string& name,
+                        const octave_value& val = octave_value ());
+
+    octave_value top_level_varval (const std::string& name) const;
+
+    void top_level_assign (const std::string& name,
+                           const octave_value& val = octave_value ());
+
+    bool is_variable (const std::string& name) const;
+
+    bool is_local_variable (const std::string& name) const;
+
+    bool is_variable (const tree_expression *expr) const;
+
+    bool is_defined (const tree_expression *expr) const;
+
+    bool is_variable (const symbol_record& sym) const;
+
+    bool is_defined (const symbol_record& sym) const;
+
+    bool is_global (const std::string& name) const;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value varval (const std::string& name) const;
+
+    void assign (const std::string& name,
+                 const octave_value& val = octave_value ());
+
+    void set_auto_fcn_var (stack_frame::auto_var_type avt,
+                           const octave_value& val = octave_value ());
+
+    octave_value get_auto_fcn_var (stack_frame::auto_var_type avt) const;
+
     void define_parameter_list_from_arg_vector
       (tree_parameter_list *param_list, const octave_value_list& args);
 
@@ -390,9 +432,52 @@
 
     call_stack& get_call_stack (void) { return m_call_stack; }
 
+    const stack_frame& get_current_stack_frame (void) const
+    {
+      return m_call_stack.get_current_stack_frame ();
+    }
+
+    stack_frame& get_current_stack_frame (void)
+    {
+      return m_call_stack.get_current_stack_frame ();
+    }
+
     profiler& get_profiler (void) { return m_profiler; }
 
-    symbol_scope get_current_scope (void);
+    symbol_scope get_top_scope (void) const;
+    symbol_scope get_current_scope (void) const;
+
+    octave_value find (const std::string& name);
+
+    void clear_objects (void);
+
+    void clear_variable (const std::string& name);
+
+    void clear_variable_pattern (const std::string& pattern);
+
+    void clear_variable_regexp (const std::string& pattern);
+
+    void clear_variables (void);
+
+    void clear_global_variable (const std::string& name);
+
+    void clear_global_variable_pattern (const std::string& pattern);
+
+    void clear_global_variable_regexp (const std::string& pattern);
+
+    void clear_global_variables (void);
+
+    void clear_all (bool force = false);
+
+    void clear_symbol (const std::string& name);
+
+    void clear_symbol_pattern (const std::string& pattern);
+
+    void clear_symbol_regexp (const std::string& pattern);
+
+    std::list<std::string> global_variable_names (void) const;
+
+    std::list<std::string> variable_names (void) const;
 
     octave_user_code * get_user_code (const std::string& fname = "");
 
@@ -570,11 +655,12 @@
 
     bool quit_loop_now (void);
 
-    void bind_auto_fcn_vars (symbol_scope& scope,
-                             const string_vector& arg_names, int nargin,
+    void bind_auto_fcn_vars (const string_vector& arg_names, int nargin,
                              int nargout, bool takes_varargs,
                              const octave_value_list& va_args);
 
+    void init_local_fcn_vars (octave_user_function& user_fcn);
+
     interpreter& m_interpreter;
 
     // The context for the current evaluation.
--- a/libinterp/parse-tree/pt-fcn-handle.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-fcn-handle.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -70,21 +70,11 @@
     symbol_scope af_scope = m_scope;
     symbol_scope af_parent_scope = m_parent_scope;
 
-    symbol_table& symtab
-      = __get_symbol_table__ ("tree_anon_fcn_handle::dup");
-
     symbol_scope new_scope;
 
     if (af_scope)
       new_scope = af_scope.dup ();
 
-    // FIXME: why should we inherit from the current scope here?  That
-    // doesn't seem right, but with the way things work now it appears
-    // to be required for bug-31371.tst to pass.
-
-    if (new_scope)
-      symtab.inherit (new_scope);
-
     // FIXME: if new scope is nullptr, then we are in big trouble here...
 
     tree_anon_fcn_handle *new_afh = new
--- a/libinterp/parse-tree/pt-id.cc	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-id.cc	Mon Jan 28 18:01:46 2019 +0000
@@ -38,15 +38,6 @@
 {
   // Symbols from the symbol table.
 
-  class tree_evaluator;
-
-  void tree_identifier::link_to_global (const symbol_scope& global_scope,
-                                        const symbol_record& global_sym)
-  {
-    if (! m_sym.is_global ())
-      m_sym.bind_fwd_rep (global_scope.get_rep (), global_sym);
-  }
-
   void
   tree_identifier::eval_undefined_error (void)
   {
@@ -70,9 +61,7 @@
     if (m_sym.is_added_static ())
       static_workspace_error ();
 
-    symbol_scope scope = tw.get_current_scope ();
-
-    return octave_lvalue (m_sym, scope.current_context ());
+    return octave_lvalue (m_sym, tw.get_current_stack_frame ());
   }
 
   tree_identifier *
@@ -90,4 +79,14 @@
 
     return new_id;
   }
+
+  octave_lvalue
+  tree_black_hole::lvalue (tree_evaluator& tw)
+  {
+    octave_lvalue retval (m_sym, tw.get_current_stack_frame ());
+
+    retval.mark_black_hole ();
+
+    return retval;
+  }
 }
--- a/libinterp/parse-tree/pt-id.h	Sat Jan 26 15:53:29 2019 +0000
+++ b/libinterp/parse-tree/pt-id.h	Mon Jan 28 18:01:46 2019 +0000
@@ -71,53 +71,16 @@
 
     std::string name (void) const { return m_sym.name (); }
 
-    bool is_defined (symbol_record::context_id context)
-    {
-      return m_sym.is_defined (context);
-    }
-
-    virtual bool is_variable (symbol_record::context_id context) const
-    {
-      return m_sym.is_variable (context);
-    }
-
-    virtual bool is_black_hole (void) { return false; }
-
-    // Try to find a definition for an identifier.  Here's how:
-    //
-    //   * If the identifier is already defined and is a function defined
-    //     in an function file that has been modified since the last time
-    //     we parsed it, parse it again.
-    //
-    //   * If the identifier is not defined, try to find a builtin
-    //     variable or an already compiled function with the same name.
-    //
-    //   * If the identifier is still undefined, try looking for an
-    //     function file to parse.
-    //
-    //   * On systems that support dynamic linking, we prefer .oct files,
-    //     then .mex files, then .m files.
-
-    octave_value
-    do_lookup (symbol_record::context_id context,
-               const octave_value_list& args = octave_value_list ())
-    {
-      return m_sym.find (context, args);
-    }
-
-    void link_to_global (const symbol_scope& global_scope,
-                         const symbol_record& global_sym);
-
-    void mark_persistent (void) { m_sym.init_persistent (); }
+    virtual bool is_black_hole (void) const { return false; }
 
     void mark_as_formal_parameter (void) { m_sym.mark_formal (); }
 
-    // We really need to know whether this symbol referst to a variable
+    // We really need to know whether this symbol refers to a variable
     // or a function, but we may not know that yet.
 
     bool lvalue_ok (void) const { return true; }
 
-    octave_lvalue lvalue (tree_evaluator&);
+    octave_lvalue lvalue (tree_evaluator& tw);
 
     void eval_undefined_error (void);
 
@@ -136,7 +99,7 @@
 
     symbol_record symbol (void) const { return m_sym; }
 
-  private:
+  protected:
 
     // The symbol record that this identifier references.
     symbol_record m_sym;
@@ -151,21 +114,14 @@
 
     std::string name (void) const { return "~"; }
 
-    bool is_variable (symbol_record::context_id) const { return false; }
-
-    bool is_black_hole (void) { return true; }
+    bool is_black_hole (void) const { return true; }
 
     tree_black_hole * dup (symbol_scope&) const
     {
       return new tree_black_hole;
     }
 
-    octave_lvalue lvalue (tree_evaluator&)
-    {
-      octave_lvalue retval;
-      retval.mark_black_hole ();
-      return retval;
-    }
+    octave_lvalue lvalue (tree_evaluator& tw);
   };
 }