diff libinterp/corefcn/symtab.cc @ 23599:5cb3a2bb5e1e

don't use singleton for symbol_table This is the first of a series of changes to make the symbol table a part of the interpreter instead of a global object. These changes also aim to simplify the implementation of symbol table so that it is easier to understand and modify. * Functions now own their scope (workspace) data. * The list of subfunctions is contained in the scope rather than a global list. * symtab.h, symtab.cc (class symbol_table): Don't use singleton pattern. * interpreter.h, interpreter.cc (interpreter::m_symbol_table): New data member. (interpreter::~interpreter): Don't set instance to 0. * interpreter-private.h, interpreter-private.cc (__get_symbol_table__): New function. Change all uses of call_stack to access call_stack object from the interpreter.
author John W. Eaton <jwe@octave.org>
date Fri, 09 Jun 2017 02:21:28 -0400
parents c3075ae020e1
children 214cb58ccc1c
line wrap: on
line diff
--- a/libinterp/corefcn/symtab.cc	Fri Jun 09 11:36:34 2017 -0400
+++ b/libinterp/corefcn/symtab.cc	Fri Jun 09 02:21:28 2017 -0400
@@ -38,6 +38,7 @@
 #include "dirfns.h"
 #include "input.h"
 #include "interpreter-private.h"
+#include "interpreter.h"
 #include "load-path.h"
 #include "ov-classdef.h"
 #include "ov-fcn.h"
@@ -51,26 +52,6 @@
 
 octave_value symbol_table::dummy_octave_value;
 
-symbol_table *symbol_table::instance = nullptr;
-
-symbol_table::scope_id_cache *symbol_table::scope_id_cache::instance = nullptr;
-
-std::map<symbol_table::scope_id, symbol_table*> symbol_table::all_instances;
-
-std::map<std::string, octave_value> symbol_table::global_table;
-
-std::map<std::string, symbol_table::fcn_info> symbol_table::fcn_table;
-
-std::map<std::string, std::set<std::string>>
-  symbol_table::class_precedence_table;
-
-std::map<std::string, std::list<std::string>> symbol_table::parent_map;
-
-const symbol_table::scope_id symbol_table::xglobal_scope = 0;
-const symbol_table::scope_id symbol_table::xtop_scope = 1;
-
-symbol_table::scope_id symbol_table::xcurrent_scope = 1;
-
 symbol_table::context_id symbol_table::xcurrent_context = 0;
 
 // Should Octave always check to see if function files have changed
@@ -78,11 +59,55 @@
 static int Vignore_function_time_stamp = 1;
 
 void
-symbol_table::scope_id_cache::create_instance (void)
+symbol_table::symbol_record::symbol_record_rep::clear (scope_id sid)
+{
+  if (! (is_hidden () || is_inherited ())
+      && sid == decl_scope ())
+    {
+      if (is_global ())
+        unmark_global ();
+
+      if (is_persistent ())
+        {
+          symbol_table& symtab
+            = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::clear");
+
+          symtab.persistent_assign (name, varval ());
+
+          unmark_persistent ();
+        }
+
+      assign (octave_value ());
+    }
+}
+
+void
+symbol_table::symbol_record::symbol_record_rep::init_persistent (void)
 {
-  instance = new scope_id_cache ();
+  if (! is_defined ())
+    {
+      mark_persistent ();
+
+      symbol_table& symtab
+        = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::init_persistent");
 
-  singleton_cleanup_list::add (cleanup_instance);
+      assign (symtab.persistent_varval (name));
+    }
+  // FIXME: this causes trouble with recursive calls.
+  // else
+  //   error ("unable to declare existing variable persistent");
+}
+
+void
+symbol_table::symbol_record::symbol_record_rep::erase_persistent (void)
+{
+  unmark_persistent ();
+
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::erase_persistent");
+
+
+  symtab.erase_persistent (name);
 }
 
 symbol_table::context_id
@@ -95,8 +120,8 @@
   // a variable from a function that has not been called yet is
   // happening.  This should be cleared up when an implementing closures.
 
-  return fcn && fcn->active_context () != static_cast<context_id> (-1)
-         ? fcn->active_context () : xcurrent_context;
+  return (fcn && fcn->active_context () != static_cast<context_id> (-1)
+          ? fcn->active_context () : xcurrent_context);
 }
 
 void
@@ -124,28 +149,85 @@
   os << "\n";
 }
 
+octave_value&
+symbol_table::symbol_record::symbol_record_rep::xglobal_varref (void)
+{
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::xglobal_varref");
+
+  symbol_table::global_symbols_iterator p
+    = symtab.m_global_symbols.find (name);
+
+  return (p == symtab.m_global_symbols.end ()
+          ? symtab.m_global_symbols[name] : p->second);
+}
+
+octave_value&
+symbol_table::symbol_record::symbol_record_rep::xpersistent_varref (void)
+{
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::xpersistent_varref");
+
+  scope *s = symtab.get_scope (symtab.current_scope ());
+
+  return s ? s->persistent_varref (name) : dummy_octave_value;
+}
+
+octave_value
+symbol_table::symbol_record::symbol_record_rep::xglobal_varval (void) const
+{
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::xglobal_varval");
+
+  return symtab.global_varval (name);
+}
+
+octave_value
+symbol_table::symbol_record::symbol_record_rep::xpersistent_varval (void) const
+{
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::symbol_record::symbol_record_rep::xpersistent_varval");
+
+  return symtab.persistent_varval (name);
+}
+
+symbol_table::symbol_record::symbol_record (void)
+  : rep (new symbol_record_rep (octave::__get_current_scope__ ("symbol_record"),
+                                "", octave_value (), local))
+{ }
+
 octave_value
 symbol_table::symbol_record::find (const octave_value_list& args) const
 {
   octave_value retval;
 
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::symbol_record::find");
+
   if (is_global ())
-    retval = symbol_table::global_varval (name ());
+    retval = symtab.global_varval (name ());
   else
     {
       retval = varval ();
 
       if (retval.is_undefined ())
         {
+#if 0
           // Use cached fcn_info pointer if possible.
           if (rep->finfo)
             retval = rep->finfo->find (args);
           else
+#endif
             {
-              retval = symbol_table::find_function (name (), args);
+              retval = symtab.find_function (name (), args);
 
               if (retval.is_defined ())
-                rep->finfo = get_fcn_info (name ());
+                return retval;
+#if 0
+              {
+                  rep->finfo = symtab.get_fcn_info (name ());
+              }
+#endif
             }
         }
     }
@@ -153,7 +235,29 @@
   return retval;
 }
 
-symbol_table::symbol_record symbol_table::dummy_symbol_record;
+symbol_table::symbol_record symbol_table::dummy_symbol_record = symbol_table::symbol_record (symbol_table::xtop_scope);
+
+symbol_table::symbol_reference::symbol_reference (const symbol_record& record)
+  : m_scope (-1), m_sym (record)
+{
+  symbol_table& symtab = octave::__get_symbol_table__ ("symbol_reference");
+
+  m_scope = symtab.current_scope ();
+}
+
+void
+symbol_table::symbol_reference::update (void) const
+{
+  symbol_table& symtab = octave::__get_symbol_table__ ("symbol_reference::update");
+
+  scope_id curr_scope = symtab.current_scope ();
+
+  if (m_scope != curr_scope || ! m_sym.is_valid ())
+    {
+      m_scope = curr_scope;
+      m_sym = symtab.insert (m_sym.name ());
+    }
+}
 
 static void
 split_name_with_package (const std::string& name, std::string& fname,
@@ -275,8 +379,12 @@
                                   std::string s_name;
                                   std::string s_pack;
 
+                                  symbol_table& symtab
+                                    = octave::__get_symbol_table__ ("out_of_date_check");
+
                                   const std::list<std::string>& plist
-                                    = symbol_table::parent_classes (dispatch_type);
+                                    = symtab.parent_classes (dispatch_type);
+
                                   std::list<std::string>::const_iterator it
                                     = plist.begin ();
 
@@ -523,8 +631,11 @@
             {
               // Search parent classes
 
+              symbol_table& symtab
+                = octave::__get_symbol_table__ ("symbol_table::fcn_info::fcn_info_rep::load_class_method");
+
               const std::list<std::string>& plist =
-                parent_classes (dispatch_type);
+                symtab.parent_classes (dispatch_type);
 
               std::list<std::string>::const_iterator it = plist.begin ();
 
@@ -565,21 +676,6 @@
   return retval;
 }
 
-void
-symbol_table::fcn_info::fcn_info_rep::mark_subfunction_in_scope_as_private
-  (scope_id scope, const std::string& class_name)
-{
-  scope_val_iterator p = subfunctions.find (scope);
-
-  if (p != subfunctions.end ())
-    {
-      octave_function *fcn = p->second.function_value ();
-
-      if (fcn)
-        fcn->mark_as_private_function (class_name);
-    }
-}
-
 // :-) JWE, can you parse this? Returns a 2D array with second dimension equal
 // to btyp_num_types (static constant).  Only the leftmost dimension can be
 // variable in C/C++.  Typedefs are boring.
@@ -642,6 +738,8 @@
           // There's a non-builtin class in the argument list.
           dispatch_type = args(i).class_name ();
 
+          symbol_table& symtab = octave::__get_symbol_table__ ("get_dispatch_type");
+
           for (int j = i+1; j < n; j++)
             {
               octave_value arg = args(j);
@@ -652,8 +750,8 @@
 
                   // Only switch to type of ARG if it is marked superior
                   // to the current DISPATCH_TYPE.
-                  if (! symbol_table::is_superiorto (dispatch_type, cname)
-                      && symbol_table::is_superiorto (cname, dispatch_type))
+                  if (! symtab.is_superiorto (dispatch_type, cname)
+                      && symtab.is_superiorto (cname, dispatch_type))
                     dispatch_type = cname;
                 }
             }
@@ -674,11 +772,8 @@
   return get_dispatch_type (args, builtin_type);
 }
 
-// Find the definition of NAME according to the following precedence
-// list:
+// Find function definition according to the following precedence list:
 //
-//   variable
-//   subfunction
 //   private function
 //   class method
 //   class constructor
@@ -718,7 +813,10 @@
 {
   if (local_funcs)
     {
-      octave_user_function *current_fcn = symbol_table::get_curr_fcn ();
+      symbol_table& symtab
+        = octave::__get_symbol_table__ ("symbol_table::fcn_info::fcn_info_rep::xfind");
+
+      octave_user_function *current_fcn = symtab.get_curr_fcn ();
 
       // Local function.
 
@@ -742,29 +840,6 @@
             }
         }
 
-      // Subfunction.  I think it only makes sense to check for
-      // subfunctions if we are currently executing a function defined
-      // from a .m file.
-
-      for (scope_id scope = xcurrent_scope; scope >= 0;)
-        {
-          scope_val_iterator r = subfunctions.find (scope);
-          if (r != subfunctions.end ())
-            {
-              // We shouldn't need an out-of-date check here since
-              // subfunctions may ultimately be called only from a
-              // primary function or method defined in the same file.
-
-              return r->second;
-            }
-
-          octave_user_function *scope_curr_fcn = get_curr_fcn (scope);
-          if (scope_curr_fcn)
-            scope = scope_curr_fcn->parent_fcn_scope ();
-          else
-            scope = -1;
-        }
-
       // Private function.
 
       if (current_fcn)
@@ -942,7 +1017,10 @@
 
   // Private function.
 
-  octave_user_function *current_fcn = symbol_table::get_curr_fcn ();
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::fcn_info::fcn_info_rep::x_builtin_find");
+
+  octave_user_function *current_fcn = symtab.get_curr_fcn ();
 
   if (current_fcn)
     {
@@ -1004,23 +1082,17 @@
   // subfunctions if we are currently executing a function defined
   // from a .m file.
 
-  for (scope_id scope = xcurrent_scope; scope >= 0;)
-    {
-      scope_val_iterator r = subfunctions.find (scope);
-      if (r != subfunctions.end ())
-        {
-          // We shouldn't need an out-of-date check here since
-          // subfunctions may ultimately be called only from a primary
-          // function or method defined in the same file.
+  symbol_table::scope_id current_scope
+    = octave::__get_current_scope__ ("symbol_table::fcn_info::fcn_info_rep::x_builtin_find");
+
+  scope *s = symtab.get_scope (current_scope);
 
-          return r->second;
-        }
+  if (s)
+    {
+      octave_value val = s->find_subfunction (name);
 
-      octave_user_function *scope_curr_fcn = get_curr_fcn (scope);
-      if (scope_curr_fcn)
-        scope = scope_curr_fcn->parent_fcn_scope ();
-      else
-        scope = -1;
+      if (val.is_defined ())
+        return val;
     }
 
   return octave_value ();
@@ -1156,7 +1228,7 @@
   // 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.
-  class_precedence_table[sup_class].insert (inf_class);
+  m_class_precedence_table[sup_class].insert (inf_class);
 
   return true;
 }
@@ -1177,9 +1249,9 @@
 bool
 symbol_table::is_superiorto (const std::string& a, const std::string& b)
 {
-  class_precedence_table_const_iterator p = class_precedence_table.find (a);
+  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 == class_precedence_table.end ())
+  if (p == m_class_precedence_table.end ())
     return false;
 
   const std::set<std::string>& inferior_classes = p->second;
@@ -1237,13 +1309,6 @@
     os << tprefix << "function from path: "
        << fcn_file_name (function_on_path) << "\n";
 
-  if (! subfunctions.empty ())
-    {
-      for (const auto& scope_val : subfunctions)
-        os << tprefix << "subfunction: " << fcn_file_name (scope_val.second)
-           << " [" << scope_val.first << "]\n";
-    }
-
   if (! local_functions.empty ())
     {
       for (const auto& str_val : local_functions)
@@ -1273,48 +1338,28 @@
     }
 }
 
-void
-symbol_table::install_nestfunction (const std::string& name,
-                                    const octave_value& fcn,
-                                    scope_id parent_scope)
+octave_value
+symbol_table::find (const std::string& name, const octave_value_list& args,
+                    bool skip_variables, bool local_funcs)
 {
-  install_subfunction (name, fcn, parent_scope);
-
-  // Stash the nest_parent for resolving variables after parsing is done.
-  octave_function *fv = fcn.function_value ();
-
-  symbol_table *fcn_table_loc = get_instance (fv->scope ());
-
-  symbol_table *parent_table = get_instance (parent_scope);
+  scope *s = get_scope (m_current_scope);
 
-  parent_table->add_nest_child (*fcn_table_loc);
-}
-
-octave_value
-symbol_table::find (const std::string& name,
-                    const octave_value_list& args,
-                    bool skip_variables,
-                    bool local_funcs)
-{
-  symbol_table *inst = get_instance (xcurrent_scope);
-
-  return inst
-         ? inst->do_find (name, args, skip_variables, local_funcs)
-         : octave_value ();
+  return (s
+          ? s->find (name, args, skip_variables, local_funcs)
+          : octave_value ());
 }
 
 octave_value
 symbol_table::builtin_find (const std::string& name)
 {
-  symbol_table *inst = get_instance (xcurrent_scope);
+  scope *s = get_scope (m_current_scope);
 
-  return inst ? inst->do_builtin_find (name) : octave_value ();
+  return s ? s->builtin_find (name) : octave_value ();
 }
 
 octave_value
 symbol_table::find_function (const std::string& name,
-                             const octave_value_list& args,
-                             bool local_funcs)
+                             const octave_value_list& args, bool local_funcs)
 {
   octave_value retval;
 
@@ -1340,8 +1385,8 @@
       else
         {
           std::string fcn_scope = name.substr (0, pos);
-          scope_id stored_scope = xcurrent_scope;
-          xcurrent_scope = xtop_scope;
+          scope_id stored_scope = m_current_scope;
+          m_current_scope = xtop_scope;
           octave_value parent = find_function (name.substr (0, pos),
                                                octave_value_list (), false);
 
@@ -1351,14 +1396,14 @@
 
               if (parent_fcn)
                 {
-                  xcurrent_scope = parent_fcn->scope ();
+                  m_current_scope = parent_fcn->scope ();
 
-                  if (xcurrent_scope > 1)
+                  if (m_current_scope > 1)
                     retval = find_function (name.substr (pos + 1), args);
                 }
             }
 
-          xcurrent_scope = stored_scope;
+          m_current_scope = stored_scope;
         }
     }
 
@@ -1379,8 +1424,8 @@
   if (pos != std::string::npos)
     {
       std::string fcn_scope = full_name.substr (0, pos);
-      scope_id stored_scope = xcurrent_scope;
-      xcurrent_scope = xtop_scope;
+      scope_id stored_scope = m_current_scope;
+      m_current_scope = xtop_scope;
       octave_value parent = find_function (full_name.substr (0, pos),
                                            octave_value_list (), false);
       if (parent.is_defined ())
@@ -1389,50 +1434,35 @@
 
           if (parent_fcn)
             {
-              xcurrent_scope = parent_fcn->scope ();
+              m_current_scope = parent_fcn->scope ();
 
-              if (xcurrent_scope > 1)
+              if (m_current_scope > 1)
                 fcn = find_function (full_name.substr (pos + 1),
                                      octave_value_list ());
             }
         }
 
-      xcurrent_scope = stored_scope;
+      m_current_scope = stored_scope;
     }
 
   return fcn;
 }
 
 void
-symbol_table::dump (std::ostream& os, scope_id scope)
+symbol_table::dump (std::ostream& os, scope_id sid)
 {
-  if (scope == xglobal_scope)
+  if (sid == xglobal_scope)
     dump_global (os);
   else
     {
-      symbol_table *inst = get_instance (scope, false);
-
-      if (inst)
-        {
-          os << "*** dumping symbol table scope " << scope
-             << " (" << inst->table_name << ")\n\n";
-
-          std::map<std::string, octave_value> sfuns
-            = symbol_table::subfunctions_defined_in_scope (scope);
+      scope *s = get_scope (sid, false);
 
-          if (! sfuns.empty ())
-            {
-              os << "  subfunctions defined in this scope:\n";
+      if (s)
+        {
+          os << "*** dumping symbol table scope " << sid
+             << " (" << s->name () << ")\n\n";
 
-              for (std::map<std::string,
-                   octave_value>::const_iterator p = sfuns.begin ();
-                   p != sfuns.end (); p++)
-                os << "    " << p->first << "\n";
-
-              os << "\n";
-            }
-
-          inst->do_dump (os);
+          s->dump (os);
         }
     }
 }
@@ -1440,11 +1470,11 @@
 void
 symbol_table::dump_global (std::ostream& os)
 {
-  if (! global_table.empty ())
+  if (! m_global_symbols.empty ())
     {
       os << "*** dumping global symbol table\n\n";
 
-      for (const auto& str_val : global_table)
+      for (const auto& str_val : m_global_symbols)
         {
           std::string nm = str_val.first;
           octave_value val = str_val.second;
@@ -1459,12 +1489,12 @@
 void
 symbol_table::dump_functions (std::ostream& os)
 {
-  if (! fcn_table.empty ())
+  if (! m_fcn_table.empty ())
     {
       os << "*** dumping globally visible functions from symbol table\n"
          << "    (c=commandline, b=built-in)\n\n";
 
-      for (const auto& nm_fi : fcn_table)
+      for (const auto& nm_fi : m_fcn_table)
         nm_fi.second.dump (os, "  ");
 
       os << "\n";
@@ -1472,52 +1502,39 @@
 }
 
 void
-symbol_table::stash_dir_name_for_subfunctions (scope_id scope,
-                                               const std::string& dir_name)
+symbol_table::cleanup (void)
 {
-  // FIXME: is this the best way to do this?  Maybe it would be
-  // better if we had a map from scope to list of subfunctions
-  // stored with the function.  Do we?
-
-  for (const auto& nm_fi : fcn_table)
-    {
-      std::pair<std::string, octave_value> tmp
-        = nm_fi.second.subfunction_defined_in_scope (scope);
+  clear_all (true);
 
-      std::string nm = tmp.first;
-
-      if (! nm.empty ())
-        {
-          octave_value& fcn = tmp.second;
-
-          octave_user_function *f = fcn.user_function_value ();
-
-          if (f)
-            f->stash_dir_name (dir_name);
-        }
-    }
+  m_global_symbols.clear ();
+  m_fcn_table.clear ();
+  m_class_precedence_table.clear ();
+  m_parent_map.clear ();
+  m_all_scopes.clear ();
 }
 
 octave_value
-symbol_table::do_find (const std::string& name,
-                       const octave_value_list& args,
-                       bool skip_variables,
-                       bool local_funcs)
+symbol_table::scope::find (const std::string& name,
+                           const octave_value_list& args,
+                           bool skip_variables, bool local_funcs)
 {
   octave_value retval;
 
   // Variable.
 
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::scope::find");
+
   if (! skip_variables)
     {
-      table_iterator p = table.find (name);
+      table_iterator p = m_symbols.find (name);
 
-      if (p != table.end ())
+      if (p != m_symbols.end ())
         {
           symbol_record sr = p->second;
 
           if (sr.is_global ())
-            return symbol_table::global_varval (name);
+            return symtab.global_varval (name);
           else
             {
               octave_value val = sr.varval ();
@@ -1528,9 +1545,21 @@
         }
     }
 
-  fcn_table_iterator p = fcn_table.find (name);
+  if (local_funcs)
+    {
+      // 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 (p != fcn_table.end ())
+      if (fcn.is_defined ())
+        return fcn;
+    }
+
+  fcn_table_iterator p = symtab.m_fcn_table.find (name);
+
+  if (p != symtab.m_fcn_table.end ())
     return p->second.find (args, local_funcs);
   else
     {
@@ -1539,7 +1568,7 @@
       octave_value fcn = finfo.find (args, local_funcs);
 
       if (fcn.is_defined ())
-        fcn_table[name] = finfo;
+        symtab.m_fcn_table[name] = finfo;
 
       return fcn;
     }
@@ -1548,13 +1577,16 @@
 }
 
 octave_value
-symbol_table::do_builtin_find (const std::string& name)
+symbol_table::scope::builtin_find (const std::string& name)
 {
   octave_value retval;
 
-  fcn_table_iterator p = fcn_table.find (name);
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::scope::find");
 
-  if (p != fcn_table.end ())
+  fcn_table_iterator p = symtab.m_fcn_table.find (name);
+
+  if (p != symtab.m_fcn_table.end ())
     return p->second.builtin_find ();
   else
     {
@@ -1563,7 +1595,7 @@
       octave_value fcn = finfo.builtin_find ();
 
       if (fcn.is_defined ())
-        fcn_table[name] = finfo;
+        symtab.m_fcn_table[name] = finfo;
 
       return fcn;
     }
@@ -1571,12 +1603,62 @@
   return retval;
 }
 
+void
+symbol_table::scope::clear_global (const std::string& name)
+{
+  table_iterator p = m_symbols.find (name);
+
+  if (p != m_symbols.end ())
+    {
+      symbol_table::symbol_record& sr = p->second;
+
+      if (sr.is_global ())
+        sr.unmark_global ();
+    }
+
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::scope::clear_global");
+
+  global_symbols_iterator q = symtab.m_global_symbols.find (name);
+
+  if (q != symtab.m_global_symbols.end ())
+    symtab.m_global_symbols.erase (q);
+
+}
+
+void
+symbol_table::scope::clear_global_pattern (const std::string& pat)
+{
+  glob_match pattern (pat);
+
+  for (auto& nm_sr : m_symbols)
+    {
+      symbol_table::symbol_record& sr = nm_sr.second;
+
+      if (sr.is_global () && pattern.match (sr.name ()))
+        sr.unmark_global ();
+    }
+
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::scope::clear_global_pattern");
+
+  global_symbols_iterator q = symtab.m_global_symbols.begin ();
+
+  while (q != symtab.m_global_symbols.end ())
+    {
+      if (pattern.match (q->first))
+        symtab.m_global_symbols.erase (q++);
+      else
+        q++;
+    }
+}
+
 std::list<workspace_element>
-symbol_table::do_workspace_info (void) const
+symbol_table::scope::workspace_info (void) const
 {
   std::list<workspace_element> retval;
 
-  for (const auto& nm_sr : table)
+  for (const auto& nm_sr : m_symbols)
     {
       std::string nm = nm_sr.first;
       symbol_record sr = nm_sr.second;
@@ -1626,13 +1708,23 @@
 }
 
 void
-symbol_table::do_dump (std::ostream& os)
+symbol_table::scope::dump (std::ostream& os)
 {
-  if (! persistent_table.empty ())
+  if (! m_subfunctions.empty ())
+    {
+      os << "  subfunctions defined in this scope:\n";
+
+      for (const auto& nm_sf : m_subfunctions)
+        os << "    " << nm_sf.first << "\n";
+
+      os << "\n";
+    }
+
+  if (! m_persistent_symbols.empty ())
     {
       os << "  persistent variables in this scope:\n\n";
 
-      for (const auto& nm_val : persistent_table)
+      for (const auto& nm_val : m_persistent_symbols)
         {
           std::string nm = nm_val.first;
           octave_value val = nm_val.second;
@@ -1645,61 +1737,97 @@
       os << "\n";
     }
 
-  if (! table.empty ())
+  if (! m_symbols.empty ())
     {
       os << "  other symbols in this scope (l=local; a=auto; f=formal\n"
          << "    h=hidden; i=inherited; g=global; p=persistent)\n\n";
 
-      for (const auto& nm_sr : table)
+      for (const auto& nm_sr : m_symbols)
         nm_sr.second.dump (os, "    ");
 
       os << "\n";
     }
 }
 
-void symbol_table::cleanup (void)
+void
+symbol_table::scope::install_subfunction (const std::string& name,
+                                          const octave_value& fval,
+                                          bool is_nested)
 {
-  clear_all (true);
+  m_subfunctions[name] = fval;
 
-  // Delete all possibly remaining scopes.
+  // This can be simpler once the scope object is stored in the function
+  // object...
+  octave_user_function *fcn = fval.user_function_value ();
 
-  while (! all_instances.empty ())
+  symbol_table& symtab
+    = octave::__get_symbol_table__ ("symbol_table::scope::install_subfunction");
+
+  scope *fcn_scope = symtab.get_scope (fcn->scope ());
+
+  fcn_scope->set_parent (this);
+
+  if (is_nested)
     {
-      // Note that deleting a scope may have side effects such as
-      // deleting other scopes.  If another scope is deleted, it may
-      // invalidate ITER, so erase this map element first.
-
-      all_instances_iterator iter = all_instances.begin ();
+      m_children.push_back (fcn_scope);
 
-      symbol_table *inst = iter->second;
+      fcn->mark_as_nested_function ();
 
-      all_instances.erase (iter);
-
-      delete inst;
+      fcn_scope->m_is_nested = true;
     }
 
-  global_table.clear ();
-  fcn_table.clear ();
-  class_precedence_table.clear ();
-  parent_map.clear ();
-  all_instances.clear ();
+}
+
+octave_value
+symbol_table::scope::find_subfunction (const std::string& name) const
+{
+  subfunctions_const_iterator p = m_subfunctions.find (name);
+
+  if (p != m_subfunctions.end ())
+    return p->second;
+
+  if (m_parent)
+    return m_parent->find_subfunction (name);
+
+  return octave_value ();
 }
 
 void
-symbol_table::do_update_nest (void)
+symbol_table::scope::stash_dir_name_for_subfunctions (const std::string& dir_name)
+{
+  for (const auto& nm_sf : m_subfunctions)
+    {
+      octave_user_function *fcn = nm_sf.second.user_function_value ();
+
+      if (fcn)
+        fcn->stash_dir_name (dir_name);
+    }
+}
+
+void
+symbol_table::scope::mark_subfunctions_in_scope_as_private (const std::string& class_name)
 {
-  if (nest_parent || nest_children.size ())
-    curr_fcn->mark_as_nested_function ();
+  for (auto& nm_sf : m_subfunctions)
+    {
+      octave_function *fcn = nm_sf.second.function_value ();
 
-  if (nest_parent)
+      if (fcn)
+        fcn->mark_as_private_function (class_name);
+    }
+}
+
+void
+symbol_table::scope::update_nest (void)
+{
+  if (m_parent)
     {
       // fix bad symbol_records
-      for (auto& nm_sr : table)
+      for (auto& nm_sr : m_symbols)
         {
           symbol_record& ours = nm_sr.second;
           symbol_record parents;
           if (! ours.is_formal ()
-              && nest_parent->look_nonlocal (nm_sr.first, parents))
+              && m_is_nested && m_parent->look_nonlocal (nm_sr.first, parents))
             {
               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");
@@ -1711,18 +1839,18 @@
                 }
             }
           else
-            ours.set_curr_fcn (curr_fcn);
+            ours.set_curr_fcn (m_fcn);
         }
     }
-  else if (nest_children.size ())
+  else if (m_children.size ())
     {
-      static_workspace = true;
-      for (auto& nm_sr : table)
-        nm_sr.second.set_curr_fcn (curr_fcn);
+      m_is_static = true;
+      for (auto& nm_sr : m_symbols)
+        nm_sr.second.set_curr_fcn (m_fcn);
     }
 
-  for (auto& symtab_p : nest_children)
-    symtab_p->do_update_nest ();
+  for (auto& symtab_p : m_children)
+    symtab_p->update_nest ();
 }
 
 DEFUN (ignore_function_time_stamp, args, nargout,
@@ -1804,18 +1932,20 @@
 %!error (ignore_function_time_stamp (42))
 */
 
-DEFUN (__current_scope__, , ,
-       doc: /* -*- texinfo -*-
+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 */)
 {
-  return ovl (symbol_table::current_scope (), symbol_table::current_context ());
+  symbol_table& symtab = interp.get_symbol_table ();
+
+  return ovl (symtab.current_scope (), symtab.current_context ());
 }
 
-DEFUN (__dump_symtab_info__, args, ,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (__dump_symtab_info__, interp, args, ,
+           doc: /* -*- texinfo -*-
 @deftypefn  {} {} __dump_symtab_info__ ()
 @deftypefnx {} {} __dump_symtab_info__ (@var{scope})
 @deftypefnx {} {} __dump_symtab_info__ ("scopes")
@@ -1831,16 +1961,18 @@
 
   octave_value retval;
 
+  symbol_table& symtab = interp.get_symbol_table ();
+
   if (nargin == 0)
     {
-      symbol_table::dump_functions (octave_stdout);
+      symtab.dump_functions (octave_stdout);
 
-      symbol_table::dump_global (octave_stdout);
+      symtab.dump_global (octave_stdout);
 
-      std::list<symbol_table::scope_id> lst = symbol_table::scopes ();
+      std::list<symbol_table::scope_id> lst = symtab.scopes ();
 
       for (const auto& scope_id : lst)
-        symbol_table::dump (octave_stdout, scope_id);
+        symtab.dump (octave_stdout, scope_id);
     }
   else
     {
@@ -1852,37 +1984,37 @@
 
           if (s_arg == "scopes")
             {
-              std::list<symbol_table::scope_id> lst = symbol_table::scopes ();
+              std::list<symbol_table::scope_id> lst = symtab.scopes ();
 
               RowVector v (lst.size ());
 
               octave_idx_type k = 0;
 
-              for (const auto& scope_id : lst)
-                v.xelem (k++) = scope_id;
+              for (const auto& sid : lst)
+                v.xelem (k++) = sid;
 
               retval = v;
             }
           else if (s_arg == "functions")
             {
-              symbol_table::dump_functions (octave_stdout);
+              symtab.dump_functions (octave_stdout);
             }
           else
             error ("__dump_symtab_info__: string argument must be \"functions\" or \"scopes\"");
         }
       else
         {
-          int s = arg.xint_value ("__dump_symtab_info__: first argument must be string or scope id");
+          int sid = arg.xint_value ("__dump_symtab_info__: first argument must be string or scope id");
 
-          symbol_table::dump (octave_stdout, s);
+          symtab.dump (octave_stdout, sid);
         }
     }
 
   return retval;
 }
 
-DEFUN (__get_cmdline_fcn_txt__, args, ,
-       doc: /* -*- texinfo -*-
+DEFMETHOD (__get_cmdline_fcn_txt__, interp, args, ,
+           doc: /* -*- texinfo -*-
 @deftypefn {} {} __get_cmdline_fcn_txt__ (@var{name})
 Undocumented internal function.
 @end deftypefn */)
@@ -1892,7 +2024,9 @@
 
   std::string name = args(0).xstring_value ("__get_cmd_line_function_text__: first argument must be function name");
 
-  octave_value ov = symbol_table::find_cmdline_function (name);
+  symbol_table& symtab = interp.get_symbol_table ();
+
+  octave_value ov = symtab.find_cmdline_function (name);
 
   octave_user_function *f = ov.user_function_value ();
 
@@ -1914,19 +2048,21 @@
 
 // FIXME: should we have functions like this in Octave?
 //
-// DEFUN (set_variable, args, , "set_variable (NAME, VALUE)")
+// DEFMETHOD (set_variable, interp, args, , "set_variable (NAME, VALUE)")
 // {
 //   if (args.length () != 2)
 //     print_usage ();
 //
 //   std::string name = args(0).xstring_value ("set_variable: variable NAME must be a string");
 //
-//   symbol_table::assign (name, args(1));
+//   symbol_table& symtab = interp.get_symbol_table ();
+//
+//   symtab.assign (name, args(1));
 //
 //   return ovl ();
 // }
 //
-// DEFUN (variable_value, args, , "VALUE = variable_value (NAME)")
+// DEFMETHOD (variable_value, interp, args, , "VALUE = variable_value (NAME)")
 // {
 //   if (args.length () != 1)
 //     print_usage ();
@@ -1935,7 +2071,9 @@
 //
 //   std::string name = args(0).xstring_value ("variable_value: variable NAME must be a string");
 //
-//   retval = symbol_table::varval (name);
+//   symbol_table& symtab = interp.get_symbol_table ();
+//
+//   retval = symtab.varval (name);
 //
 //   if (retval.is_undefined ())
 //     error ("variable_value: '%s' is not a variable in the current scope",