changeset 24263:3b302b2890d7

disentangle symbol_record, scope, and fcn_info from symbol_table class * fcn-info.cc, fcn-info.h, scope.cc, scope.h, symrec.cc, symrec.h: New files extracted from symtab.h and symtab.cc. * libinterp/corefcn/module.mk: Update. * symrec.cc (symbol_record::symbol_record_rep::xglobal_varref): Don't access private symbol_table internals directly. * scope.h, scope.cc (scope::find, scope::builtin_find, scope::clear_global, scope::clear_global_pattern): Don't access private symbol_table internals directly. * symtab.h, symtab.cc (symbol_table::builtin_find): Don't forward to current scope. Look directly in fcn_info table. (symbol_table::global_varref, symbol_table::fcn_table_find, symbol_table::erase_global, symbol_table::erase_global_pattern): New functions. * scope.h (scope::context_id): New typedef. * symrec.h (symbol_record::context_id): New typedef. * symtab.h (symbol_table::context_id): Update. * symtab.h, symtab.cc (symbol_table::dummy_octave_value): Delete static data member. * symtab.h (symbol_table::context_id): Delete typedef. (symbol_table::symbol_record, symbol_table::scope, symbol_table::fcn_info): New typedefs.
author John W. Eaton <jwe@octave.org>
date Thu, 16 Nov 2017 16:06:31 -0500
parents 554c630c9835
children c372b091f622
files libinterp/corefcn/fcn-info.cc libinterp/corefcn/fcn-info.h libinterp/corefcn/module.mk libinterp/corefcn/scope.cc libinterp/corefcn/scope.h libinterp/corefcn/symrec.cc libinterp/corefcn/symrec.h libinterp/corefcn/symtab.cc libinterp/corefcn/symtab.h
diffstat 9 files changed, 3279 insertions(+), 2989 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/fcn-info.cc	Thu Nov 16 16:06:31 2017 -0500
@@ -0,0 +1,845 @@
+/*
+
+Copyright (C) 1993-2017 John W. Eaton
+Copyright (C) 2009 VZLU Prague, a.s.
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "file-ops.h"
+
+#include "fcn-info.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+#include "load-path.h"
+#include "ov-fcn.h"
+#include "ov-usr-fcn.h"
+#include "parse.h"
+#include "scope.h"
+#include "symrec.h"
+#include "symtab.h"
+
+namespace octave
+{
+  octave_value
+  fcn_info::fcn_info_rep::load_private_function (const std::string& dir_name)
+  {
+    octave_value retval;
+
+    load_path& lp
+      = __get_load_path__ ("fcn_info::fcn_info_rep::load_private_function");
+
+    std::string file_name = lp.find_private_fcn (dir_name, name);
+
+    if (file_name.empty ())
+      return retval;
+
+    octave_value ov_fcn = load_fcn_from_file (file_name, dir_name);
+
+    if (ov_fcn.is_undefined ())
+      return retval;
+
+    octave_function *tmpfcn = ov_fcn.function_value ();
+
+    if (! tmpfcn)
+      return retval;
+
+    std::string class_name;
+
+    size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ());
+
+    if (pos != std::string::npos)
+      {
+        std::string tmp = dir_name.substr (pos+1);
+
+        if (tmp[0] == '@')
+          class_name = tmp.substr (1);
+      }
+
+    tmpfcn->mark_as_private_function (class_name);
+
+    private_functions[dir_name] = ov_fcn;
+
+    return ov_fcn;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::load_class_constructor (void)
+  {
+    octave_value retval;
+
+    std::string dir_name;
+
+    load_path& lp
+      = __get_load_path__ ("fcn_info::fcn_info_rep::load_class_constructor");
+
+    std::string file_name = lp.find_method (name, name, dir_name, package_name);
+
+    if (! file_name.empty ())
+      {
+        octave_value ov_fcn
+          = load_fcn_from_file (file_name, dir_name, name,
+                                        package_name);
+
+        if (ov_fcn.is_defined ())
+          {
+            // Note: ov_fcn may be an octave_classdef_meta object instead
+            // of the actual constructor function.
+
+            retval = ov_fcn;
+
+            class_constructors[name] = retval;
+            class_methods[name] = retval;
+          }
+      }
+    else
+      {
+        // Classdef constructors can be defined anywhere in the path, not
+        // necessarily in @-folders.  Look for a normal function and load it.
+        // If the loaded function is a classdef constructor, store it as such
+        // and restore function_on_path to its previous value.
+
+        octave_value old_function_on_path = function_on_path;
+
+        octave_value maybe_cdef_ctor = find_user_function ();
+
+        if (maybe_cdef_ctor.is_defined ())
+          {
+            octave_function *fcn = maybe_cdef_ctor.function_value (true);
+
+            if (fcn && fcn->is_classdef_constructor ())
+              {
+                retval = maybe_cdef_ctor;
+
+                class_constructors[name] = retval;
+                class_methods[name] = retval;
+
+                function_on_path = old_function_on_path;
+              }
+          }
+      }
+
+    return retval;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::load_class_method (const std::string& dispatch_type)
+  {
+    octave_value retval;
+
+    if (full_name () == dispatch_type)
+      retval = load_class_constructor ();
+    else
+      {
+        cdef_manager& cdm
+          = __get_cdef_manager__ ("fcn_info::fcn_info_rep::load_class_method");
+
+        octave_function *cm = cdm.find_method_symbol (name, dispatch_type);
+
+        if (cm)
+          retval = octave_value (cm);
+
+        if (! retval.is_defined ())
+          {
+            std::string dir_name;
+
+            load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::load_class_method");
+
+            std::string file_name = lp.find_method (dispatch_type, name,
+                                                    dir_name);
+
+            if (! file_name.empty ())
+              {
+                octave_value ov_fcn
+                  = load_fcn_from_file (file_name, dir_name,
+                                                dispatch_type);
+
+                if (ov_fcn.is_defined ())
+                  {
+                    octave_function *tmpfcn = ov_fcn.function_value ();
+
+                    if (tmpfcn && tmpfcn->is_class_method (dispatch_type))
+                      {
+                        retval = ov_fcn;
+
+                        class_methods[dispatch_type] = retval;
+                      }
+                  }
+              }
+
+            if (retval.is_undefined ())
+              {
+                // Search parent classes
+
+                symbol_table& symtab
+                  = __get_symbol_table__ ("fcn_info::fcn_info_rep::load_class_method");
+
+                const std::list<std::string>& plist =
+                  symtab.parent_classes (dispatch_type);
+
+                std::list<std::string>::const_iterator it = plist.begin ();
+
+                while (it != plist.end ())
+                  {
+                    retval = find_method (*it);
+
+                    if (retval.is_defined ())
+                      {
+                        class_methods[dispatch_type] = retval;
+                        break;
+                      }
+
+                    it++;
+                  }
+              }
+
+            if (retval.is_undefined ())
+              {
+                // Search for built-in functions that are declared to
+                // handle specific types.
+
+                if (built_in_function.is_defined ())
+                  {
+                    octave_function *fcn = built_in_function.function_value ();
+
+                    if (fcn && fcn->handles_dispatch_class (dispatch_type))
+                      {
+                        retval = built_in_function;
+
+                        class_methods[dispatch_type] = retval;
+                      }
+                  }
+              }
+          }
+      }
+
+    return retval;
+  }
+}
+
+// :-) 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.
+
+static builtin_type_t (*build_sup_table (void))[btyp_num_types]
+{
+  static builtin_type_t sup_table[btyp_num_types][btyp_num_types];
+  for (int i = 0; i < btyp_num_types; i++)
+    for (int j = 0; j < btyp_num_types; j++)
+      {
+        builtin_type_t ityp = static_cast<builtin_type_t> (i);
+        builtin_type_t jtyp = static_cast<builtin_type_t> (j);
+        // FIXME: Is this really right?
+        bool use_j =
+          (jtyp == btyp_func_handle || ityp == btyp_bool
+           || (btyp_isarray (ityp)
+               && (! btyp_isarray (jtyp)
+                   || (btyp_isinteger (jtyp) && ! btyp_isinteger (ityp))
+                   || ((ityp == btyp_double || ityp == btyp_complex
+                        || ityp == btyp_char)
+                       && (jtyp == btyp_float
+                           || jtyp == btyp_float_complex)))));
+
+        sup_table[i][j] = (use_j ? jtyp : ityp);
+      }
+
+  return sup_table;
+}
+
+namespace octave
+{
+  std::string
+  get_dispatch_type (const octave_value_list& args,
+                     builtin_type_t& builtin_type)
+  {
+    static builtin_type_t (*sup_table)[btyp_num_types] = build_sup_table ();
+    std::string dispatch_type;
+
+    int n = args.length ();
+
+    if (n > 0)
+      {
+        int i = 0;
+        builtin_type = args(0).builtin_type ();
+        if (builtin_type != btyp_unknown)
+          {
+            for (i = 1; i < n; i++)
+              {
+                builtin_type_t bti = args(i).builtin_type ();
+                if (bti != btyp_unknown)
+                  builtin_type = sup_table[builtin_type][bti];
+                else
+                  {
+                    builtin_type = btyp_unknown;
+                    break;
+                  }
+              }
+          }
+
+        if (builtin_type == btyp_unknown)
+          {
+            // There's a non-builtin class in the argument list.
+            dispatch_type = args(i).class_name ();
+
+            symbol_table& symtab = __get_symbol_table__ ("get_dispatch_type");
+
+            for (int j = i+1; j < n; j++)
+              {
+                octave_value arg = args(j);
+
+                if (arg.builtin_type () == btyp_unknown)
+                  {
+                    std::string cname = arg.class_name ();
+
+                    // Only switch to type of ARG if it is marked superior
+                    // to the current DISPATCH_TYPE.
+                    if (! symtab.is_superiorto (dispatch_type, cname)
+                        && symtab.is_superiorto (cname, dispatch_type))
+                      dispatch_type = cname;
+                  }
+              }
+          }
+        else
+          dispatch_type = btyp_class_name[builtin_type];
+      }
+    else
+      builtin_type = btyp_unknown;
+
+    return dispatch_type;
+  }
+
+  std::string
+  get_dispatch_type (const octave_value_list& args)
+  {
+    builtin_type_t builtin_type;
+    return get_dispatch_type (args, builtin_type);
+  }
+
+  // Find function definition according to the following precedence list:
+  //
+  //   private function
+  //   class method
+  //   class constructor
+  //   command-line function
+  //   autoload function
+  //   function on the path
+  //   built-in function
+  //
+  // Matlab documentation states that constructors have higher precedence
+  // than methods, but that does not seem to be the case.
+
+  octave_value
+  fcn_info::fcn_info_rep::find (const octave_value_list& args, bool local_funcs)
+  {
+    octave_value retval = xfind (args, local_funcs);
+
+    if (retval.is_undefined ())
+      {
+        // It is possible that the user created a file on the fly since
+        // the last prompt or chdir, so try updating the load path and
+        // searching again.
+
+        load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::find");
+
+        lp.update ();
+
+        retval = xfind (args, local_funcs);
+      }
+
+    return retval;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::xfind (const octave_value_list& args,
+                                 bool local_funcs)
+  {
+    if (local_funcs)
+      {
+        scope *curr_scope
+          = __get_current_scope__ ("fcn_info::fcn_info_rep::xfind");
+
+        octave_user_function *current_fcn
+          = curr_scope ? curr_scope->function () : nullptr;
+
+        // Local function.
+
+        if (current_fcn)
+          {
+            std::string fcn_file = current_fcn->fcn_file_name ();
+
+            // For anonymous functions we look at the parent scope so that if
+            // they were defined within class methods and use local functions
+            // (helper functions) we can still use those anonymous functions
+
+            if (current_fcn->is_anonymous_function ())
+              {
+                if (fcn_file.empty ()
+                    && curr_scope->parent_scope () != nullptr
+                    && curr_scope->parent_scope ()->function () != nullptr)
+                  fcn_file
+                    = curr_scope->parent_scope ()->function ()->fcn_file_name();
+              }
+
+            if (! fcn_file.empty ())
+              {
+                str_val_iterator r = local_functions.find (fcn_file);
+
+                if (r != local_functions.end ())
+                  {
+                    // We shouldn't need an out-of-date check here since
+                    // local functions may ultimately be called only from
+                    // a primary function or method defined in the same
+                    // file.
+
+                    return r->second;
+                  }
+              }
+          }
+
+        // Private function.
+
+        if (current_fcn)
+          {
+            std::string dir_name = current_fcn->dir_name ();
+
+            if (! dir_name.empty ())
+              {
+                str_val_iterator q = private_functions.find (dir_name);
+
+                if (q == private_functions.end ())
+                  {
+                    octave_value val = load_private_function (dir_name);
+
+                    if (val.is_defined ())
+                      return val;
+                  }
+                else
+                  {
+                    octave_value& fval = q->second;
+
+                    if (fval.is_defined ())
+                      out_of_date_check (fval, "", false);
+
+                    if (fval.is_defined ())
+                      return fval;
+                    else
+                      {
+                        octave_value val = load_private_function (dir_name);
+
+                        if (val.is_defined ())
+                          return val;
+                      }
+                  }
+              }
+          }
+      }
+
+    // Class methods.
+
+    if (! args.empty ())
+      {
+        std::string dispatch_type = get_dispatch_type (args);
+
+        octave_value fcn = find_method (dispatch_type);
+
+        if (fcn.is_defined ())
+          return fcn;
+      }
+
+    // Class constructors.  The class name and function name are the same.
+
+    str_val_iterator q = class_constructors.find (name);
+
+    if (q == class_constructors.end ())
+      {
+        octave_value val = load_class_constructor ();
+
+        if (val.is_defined ())
+          return val;
+      }
+    else
+      {
+        octave_value& fval = q->second;
+
+        if (fval.is_defined ())
+          out_of_date_check (fval, name);
+
+        if (fval.is_defined ())
+          return fval;
+        else
+          {
+            octave_value val = load_class_constructor ();
+
+            if (val.is_defined ())
+              return val;
+          }
+      }
+
+    // Command-line function.
+
+    if (cmdline_function.is_defined ())
+      return cmdline_function;
+
+    // Autoload?
+
+    octave_value fcn = find_autoload ();
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    // Function on the path.
+
+    fcn = find_user_function ();
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    // Package
+
+    fcn = find_package ();
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    // Built-in function (might be undefined).
+
+    return built_in_function;
+  }
+
+  // Find the definition of NAME according to the following precedence
+  // list:
+  //
+  //   built-in function
+  //   function on the path
+  //   autoload function
+  //   command-line function
+  //   private function
+  //   subfunction
+
+  // This function is used to implement the "builtin" function, which
+  // searches for "built-in" functions.  In Matlab, "builtin" only
+  // returns functions that are actually built-in to the interpreter.
+  // But since the list of built-in functions is different in Octave and
+  // Matlab, we also search up the precedence list until we find
+  // something that matches.  Note that we are only searching by name,
+  // so class methods and constructors are skipped.
+
+  octave_value
+  fcn_info::fcn_info_rep::builtin_find (void)
+  {
+    octave_value retval = x_builtin_find ();
+
+    if (! retval.is_defined ())
+      {
+        // It is possible that the user created a file on the fly since
+        // the last prompt or chdir, so try updating the load path and
+        // searching again.
+
+        load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::builtin_find");
+
+        lp.update ();
+
+        retval = x_builtin_find ();
+      }
+
+    return retval;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::x_builtin_find (void)
+  {
+    // Built-in function.
+    if (built_in_function.is_defined ())
+      return built_in_function;
+
+    // Function on the path.
+
+    octave_value fcn = find_user_function ();
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    // Autoload?
+
+    fcn = find_autoload ();
+
+    if (fcn.is_defined ())
+      return fcn;
+
+    // Command-line function.
+
+    if (cmdline_function.is_defined ())
+      return cmdline_function;
+
+    // Private function.
+
+    scope *curr_scope
+      = __get_current_scope__ ("fcn_info::fcn_info_rep::x_builtin_find");
+
+    octave_user_function *current_fcn = curr_scope ? curr_scope->function () : nullptr;
+
+    if (current_fcn)
+      {
+        std::string dir_name = current_fcn->dir_name ();
+
+        if (! dir_name.empty ())
+          {
+            str_val_iterator q = private_functions.find (dir_name);
+
+            if (q == private_functions.end ())
+              {
+                octave_value val = load_private_function (dir_name);
+
+                if (val.is_defined ())
+                  return val;
+              }
+            else
+              {
+                octave_value& fval = q->second;
+
+                if (fval.is_defined ())
+                  out_of_date_check (fval);
+
+                if (fval.is_defined ())
+                  return fval;
+                else
+                  {
+                    octave_value val = load_private_function (dir_name);
+
+                    if (val.is_defined ())
+                      return val;
+                  }
+              }
+          }
+      }
+
+    // Local function.
+
+    if (current_fcn)
+      {
+        std::string fcn_file = current_fcn->fcn_file_name ();
+
+        if (! fcn_file.empty ())
+          {
+            str_val_iterator r = local_functions.find (fcn_file);
+
+            if (r != local_functions.end ())
+              {
+                // We shouldn't need an out-of-date check here since local
+                // functions may ultimately be called only from a primary
+                // function or method defined in the same file.
+
+                return r->second;
+              }
+          }
+      }
+
+    // Subfunction.  I think it only makes sense to check for
+    // subfunctions if we are currently executing a function defined
+    // from a .m file.
+
+    if (curr_scope)
+      {
+        octave_value val = curr_scope->find_subfunction (name);
+
+        if (val.is_defined ())
+          return val;
+      }
+
+    return octave_value ();
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::find_method (const std::string& dispatch_type)
+  {
+    octave_value retval;
+
+    str_val_iterator q = class_methods.find (dispatch_type);
+
+    if (q == class_methods.end ())
+      {
+        octave_value val = load_class_method (dispatch_type);
+
+        if (val.is_defined ())
+          return val;
+      }
+    else
+      {
+        octave_value& fval = q->second;
+
+        if (fval.is_defined ())
+          out_of_date_check (fval, dispatch_type);
+
+        if (fval.is_defined ())
+          return fval;
+        else
+          {
+            octave_value val = load_class_method (dispatch_type);
+
+            if (val.is_defined ())
+              return val;
+          }
+      }
+
+    return retval;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::find_autoload (void)
+  {
+    // Autoloaded function.
+
+    if (autoload_function.is_defined ())
+      out_of_date_check (autoload_function);
+
+    if (! autoload_function.is_defined ())
+      {
+        std::string file_name = lookup_autoload (name);
+
+        if (! file_name.empty ())
+          {
+            size_t pos = file_name.find_last_of (sys::file_ops::dir_sep_chars ());
+
+            std::string dir_name = file_name.substr (0, pos);
+
+            octave_value ov_fcn
+              = load_fcn_from_file (file_name, dir_name, "", "",
+                                            name, true);
+
+            if (ov_fcn.is_defined ())
+              autoload_function = octave_value (ov_fcn);
+          }
+      }
+
+    return autoload_function;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::find_user_function (void)
+  {
+    // Function on the path.
+
+    if (function_on_path.is_defined ())
+      out_of_date_check (function_on_path);
+
+    if (function_on_path.is_undefined ())
+      {
+        std::string dir_name;
+
+        load_path& lp = __get_load_path__ ("fcn_info::fcn_info_rep::find_user_function");
+
+
+        std::string file_name = lp.find_fcn (name, dir_name, package_name);
+
+        if (! file_name.empty ())
+          {
+            octave_value ov_fcn
+              = load_fcn_from_file (file_name, dir_name, "",
+                                            package_name);
+
+            if (ov_fcn.is_defined ())
+              function_on_path = ov_fcn;
+          }
+      }
+
+    return function_on_path;
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::find_package (void)
+  {
+    // FIXME: implement correct way to check out of date package
+    //if (package.is_defined ())
+    //  out_of_date_check (package);
+
+    if (package.is_undefined ())
+      {
+        cdef_manager& cdm
+          = __get_cdef_manager__ ("fcn_info::fcn_info_rep::find_package");
+
+        octave_function *fcn = cdm.find_package_symbol (full_name ());
+
+        if (fcn)
+          package = octave_value (fcn);
+      }
+
+    return package;
+  }
+
+  void
+  fcn_info::fcn_info_rep::install_built_in_dispatch (const std::string& klass)
+  {
+    if (built_in_function.is_defined ())
+      {
+        octave_function *fcn = built_in_function.function_value ();
+
+        if (fcn)
+          {
+            if (fcn->handles_dispatch_class (klass))
+              warning ("install_built_in_dispatch: '%s' already defined for class '%s'",
+                       name.c_str (), klass.c_str ());
+            else
+              fcn->push_dispatch_class (klass);
+          }
+      }
+    else
+      error ("install_built_in_dispatch: '%s' is not a built-in function",
+             name.c_str ());
+  }
+
+  octave_value
+  fcn_info::fcn_info_rep::dump (void) const
+  {
+    std::map<std::string, octave_value> m
+      = {{ "name", full_name () },
+         { "refcount", count.value () },
+         { "package", package.dump () },
+         { "local_functions", dump_function_map (local_functions) },
+         { "private_functions", dump_function_map (private_functions) },
+         { "class_methods", dump_function_map (class_methods) },
+         { "class_constructors", dump_function_map (class_constructors) },
+         { "cmdline_function", cmdline_function.dump () },
+         { "autoload_function", autoload_function.dump () },
+         { "function_on_path", function_on_path.dump () },
+         { "built_in_function", built_in_function.dump () }};
+
+    return octave_value (m);
+  }
+
+  octave_value
+  dump_function_map (const std::map<std::string, octave_value>& fcn_map)
+  {
+    if (fcn_map.empty ())
+      return octave_value (Matrix ());
+
+    std::map<std::string, octave_value> info_map;
+
+    for (const auto& nm_fcn : fcn_map)
+      {
+        std::string nm = nm_fcn.first;
+        const octave_value& fcn = nm_fcn.second;
+        info_map[nm] = fcn.dump ();
+      }
+
+    return octave_value (info_map);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/fcn-info.h	Thu Nov 16 16:06:31 2017 -0500
@@ -0,0 +1,362 @@
+/*
+
+Copyright (C) 1993-2017 John W. Eaton
+Copyright (C) 2009 VZLU Prague
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_fcn_info_h)
+#define octave_fcn_info_h 1
+
+#include "octave-config.h"
+
+#include <list>
+#include <map>
+#include <string>
+
+#include "oct-refcount.h"
+
+#include "ov.h"
+#include "ovl.h"
+
+namespace octave
+{
+  class fcn_info
+  {
+  public:
+
+    typedef std::map<std::string, octave_value>::const_iterator
+    str_val_const_iterator;
+    typedef std::map<std::string, octave_value>::iterator str_val_iterator;
+
+  private:
+
+    class fcn_info_rep
+    {
+    public:
+
+      fcn_info_rep (const std::string& nm)
+        : name (nm), package_name (), local_functions (),
+          private_functions (), class_constructors (), class_methods (),
+          cmdline_function (), autoload_function (), function_on_path (),
+          built_in_function (), count (1)
+      {
+        size_t pos = name.rfind ('.');
+
+        if (pos != std::string::npos)
+          {
+            package_name = name.substr (0, pos);
+            name = name.substr (pos+1);
+          }
+      }
+
+      // No copying!
+
+      fcn_info_rep (const fcn_info_rep&) = delete;
+
+      fcn_info_rep& operator = (const fcn_info_rep&) = delete;
+
+      ~fcn_info_rep (void) = default;
+
+      octave_value install_local_function (const std::string& file_name);
+
+      octave_value load_private_function (const std::string& dir_name);
+
+      octave_value load_class_constructor (void);
+
+      octave_value load_class_method (const std::string& dispatch_type);
+
+      octave_value find (const octave_value_list& args, bool local_funcs);
+
+      octave_value builtin_find (void);
+
+      octave_value find_method (const std::string& dispatch_type);
+
+      octave_value find_autoload (void);
+
+      octave_value find_package (void);
+
+      octave_value find_user_function (void);
+
+      bool is_user_function_defined (void) const
+      {
+        return function_on_path.is_defined ();
+      }
+
+      octave_value find_function (const octave_value_list& args,
+                                  bool local_funcs)
+      {
+        return find (args, local_funcs);
+      }
+
+      void install_cmdline_function (const octave_value& f)
+      {
+        cmdline_function = f;
+      }
+
+      void install_local_function (const octave_value& f,
+                                   const std::string& file_name)
+      {
+        local_functions[file_name] = f;
+      }
+
+      void install_user_function (const octave_value& f)
+      {
+        function_on_path = f;
+      }
+
+      void install_built_in_function (const octave_value& f)
+      {
+        built_in_function = f;
+      }
+
+      void install_built_in_dispatch (const std::string& klass);
+
+      template <typename T>
+      void
+      clear_map (std::map<T, octave_value>& map, bool force = false)
+      {
+        typename std::map<T, octave_value>::iterator p = map.begin ();
+
+        while (p != map.end ())
+          {
+            if (force || ! p->second.islocked ())
+              map.erase (p++);
+            else
+              p++;
+          }
+      }
+
+      void clear_autoload_function (bool force = false)
+      {
+        if (force || ! autoload_function.islocked ())
+          autoload_function = octave_value ();
+      }
+
+      // We also clear command line functions here, as these are both
+      // "user defined"
+      void clear_user_function (bool force = false)
+      {
+        clear_autoload_function (force);
+
+        if (force || ! function_on_path.islocked ())
+          function_on_path = octave_value ();
+
+        if (force || ! cmdline_function.islocked ())
+          cmdline_function = octave_value ();
+      }
+
+      void clear_mex_function (void)
+      {
+        if (function_on_path.is_mex_function ())
+          clear_user_function ();
+      }
+
+      void clear_package (void)
+      {
+        package = octave_value ();
+      }
+
+      void clear (bool force = false)
+      {
+        clear_map (local_functions, force);
+        clear_map (private_functions, force);
+        clear_map (class_constructors, force);
+        clear_map (class_methods, force);
+
+        clear_autoload_function (force);
+        clear_user_function (force);
+        clear_package ();
+      }
+
+      octave_value dump (void) const;
+
+      std::string full_name (void) const
+      {
+        if (package_name.empty ())
+          return name;
+        else
+          return package_name + '.' + name;
+      }
+
+      std::string name;
+
+      std::string package_name;
+
+      // File name to function object.
+      std::map<std::string, octave_value> local_functions;
+
+      // Directory name to function object.
+      std::map<std::string, octave_value> private_functions;
+
+      // Class name to function object.
+      std::map<std::string, octave_value> class_constructors;
+
+      // Dispatch type to function object.
+      std::map<std::string, octave_value> class_methods;
+
+      octave_value cmdline_function;
+
+      octave_value autoload_function;
+
+      octave_value function_on_path;
+
+      octave_value package;
+
+      octave_value built_in_function;
+
+      refcount<size_t> count;
+
+    private:
+
+      octave_value xfind (const octave_value_list& args, bool local_funcs);
+
+      octave_value x_builtin_find (void);
+    };
+
+  public:
+
+    fcn_info (const std::string& nm = "")
+      : rep (new fcn_info_rep (nm)) { }
+
+    fcn_info (const fcn_info& fi) : rep (fi.rep)
+    {
+      rep->count++;
+    }
+
+    fcn_info& operator = (const fcn_info& fi)
+    {
+      if (this != &fi)
+        {
+          if (--rep->count == 0)
+            delete rep;
+
+          rep = fi.rep;
+          rep->count++;
+        }
+
+      return *this;
+    }
+
+    ~fcn_info (void)
+    {
+      if (--rep->count == 0)
+        delete rep;
+    }
+
+    octave_value find (const octave_value_list& args = octave_value_list (),
+                       bool local_funcs = true)
+    {
+      return rep->find (args, local_funcs);
+    }
+
+    octave_value builtin_find (void)
+    {
+      return rep->builtin_find ();
+    }
+
+    octave_value find_method (const std::string& dispatch_type) const
+    {
+      return rep->find_method (dispatch_type);
+    }
+
+    octave_value find_built_in_function (void) const
+    {
+      return rep->built_in_function;
+    }
+
+    octave_value find_cmdline_function (void) const
+    {
+      return rep->cmdline_function;
+    }
+
+    octave_value find_autoload (void)
+    {
+      return rep->find_autoload ();
+    }
+
+    octave_value find_user_function (void)
+    {
+      return rep->find_user_function ();
+    }
+
+    bool is_user_function_defined (void) const
+    {
+      return rep->is_user_function_defined ();
+    }
+
+    octave_value find_function (const octave_value_list& args
+                                = octave_value_list (),
+                                bool local_funcs = true)
+    {
+      return rep->find_function (args, local_funcs);
+    }
+
+    void install_cmdline_function (const octave_value& f)
+    {
+      rep->install_cmdline_function (f);
+    }
+
+    void install_local_function (const octave_value& f,
+                                 const std::string& file_name)
+    {
+      rep->install_local_function (f, file_name);
+    }
+
+    void install_user_function (const octave_value& f)
+    {
+      rep->install_user_function (f);
+    }
+
+    void install_built_in_function (const octave_value& f)
+    {
+      rep->install_built_in_function (f);
+    }
+
+    void install_built_in_dispatch (const std::string& klass)
+    {
+      rep->install_built_in_dispatch (klass);
+    }
+
+    void clear (bool force = false) { rep->clear (force); }
+
+    void clear_user_function (bool force = false)
+    {
+      rep->clear_user_function (force);
+    }
+
+    void clear_autoload_function (bool force = false)
+    {
+      rep->clear_autoload_function (force);
+    }
+
+    void clear_mex_function (void) { rep->clear_mex_function (); }
+
+    octave_value dump (void) const { return rep->dump (); }
+
+  private:
+
+    fcn_info_rep *rep;
+  };
+
+  octave_value
+  dump_function_map (const std::map<std::string, octave_value>& fcn_map);
+}
+
+#endif
--- a/libinterp/corefcn/module.mk	Thu Nov 16 16:12:12 2017 -0500
+++ b/libinterp/corefcn/module.mk	Thu Nov 16 16:06:31 2017 -0500
@@ -34,6 +34,7 @@
   %reldir%/error.h \
   %reldir%/errwarn.h \
   %reldir%/event-queue.h \
+  %reldir%/fcn-info.h \
   %reldir%/file-io.h \
   %reldir%/ft-text-renderer.h \
   %reldir%/gl-render.h \
@@ -78,9 +79,11 @@
   %reldir%/pager.h \
   %reldir%/pr-output.h \
   %reldir%/procstream.h \
+  %reldir%/scope.h \
   %reldir%/sighandlers.h \
   %reldir%/sparse-xdiv.h \
   %reldir%/sparse-xpow.h \
+  %reldir%/symrec.h \
   %reldir%/symtab.h \
   %reldir%/sysdep.h \
   %reldir%/text-renderer.h \
@@ -145,6 +148,7 @@
   %reldir%/error.cc \
   %reldir%/errwarn.cc \
   %reldir%/event-queue.cc \
+  %reldir%/fcn-info.cc \
   %reldir%/fft.cc \
   %reldir%/fft2.cc \
   %reldir%/fftn.cc \
@@ -221,6 +225,7 @@
   %reldir%/rcond.cc \
   %reldir%/regexp.cc \
   %reldir%/schur.cc \
+  %reldir%/scope.cc \
   %reldir%/sighandlers.cc \
   %reldir%/sparse-xdiv.cc \
   %reldir%/sparse-xpow.cc \
@@ -233,6 +238,7 @@
   %reldir%/sub2ind.cc \
   %reldir%/svd.cc \
   %reldir%/sylvester.cc \
+  %reldir%/symrec.cc \
   %reldir%/symtab.cc \
   %reldir%/syscalls.cc \
   %reldir%/sysdep.cc \
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/scope.cc	Thu Nov 16 16:06:31 2017 -0500
@@ -0,0 +1,368 @@
+/*
+
+Copyright (C) 1993-2017 John W. Eaton
+Copyright (C) 2009 VZLU Prague, a.s.
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <sstream>
+
+#include "fcn-info.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+#include "ov-fcn.h"
+#include "ov-usr-fcn.h"
+#include "scope.h"
+#include "symrec.h"
+#include "symtab.h"
+#include "utils.h"
+
+namespace octave
+{
+  octave_value
+  scope::find (const std::string& name, const octave_value_list& args,
+               bool skip_variables, bool local_funcs)
+  {
+    // Variable.
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("scope::find");
+
+    if (! skip_variables)
+      {
+        table_iterator p = m_symbols.find (name);
+
+        if (p != m_symbols.end ())
+          {
+            symbol_record sr = p->second;
+
+            if (sr.is_global ())
+              return symtab.global_varval (name);
+            else
+              {
+                octave_value val = sr.varval ();
+
+                if (val.is_defined ())
+                  return val;
+              }
+          }
+      }
+
+    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 (fcn.is_defined ())
+          return fcn;
+      }
+
+    return symtab.fcn_table_find (name, args, local_funcs);
+  }
+
+  symbol_record&
+  scope::insert (const std::string& name, bool force_add)
+  {
+    table_iterator p = m_symbols.find (name);
+
+    if (p == m_symbols.end ())
+      {
+        symbol_record ret (this, name);
+
+        if (m_is_nested && m_parent && m_parent->look_nonlocal (name, ret))
+          return m_symbols[name] = ret;
+        else
+          {
+            if (m_is_static && ! force_add)
+              ret.mark_added_static ();
+
+            return m_symbols[name] = ret;
+          }
+      }
+    else
+      return p->second;
+  }
+
+  void
+  scope::clear_global (const std::string& name)
+  {
+    table_iterator p = m_symbols.find (name);
+
+    if (p != m_symbols.end ())
+      {
+        symbol_record& sr = p->second;
+
+        if (sr.is_global ())
+          sr.unmark_global ();
+      }
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("scope::clear_global");
+
+    symtab.erase_global (name);
+  }
+
+  void
+  scope::clear_global_pattern (const std::string& pat)
+  {
+    glob_match pattern (pat);
+
+    for (auto& nm_sr : m_symbols)
+      {
+        symbol_record& sr = nm_sr.second;
+
+        if (sr.is_global () && pattern.match (sr.name ()))
+          sr.unmark_global ();
+      }
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("scope::clear_global_pattern");
+
+    symtab.erase_global_pattern (pattern);
+  }
+
+  std::list<workspace_element>
+  scope::workspace_info (void) const
+  {
+    std::list<workspace_element> retval;
+
+    for (const auto& nm_sr : m_symbols)
+      {
+        std::string nm = nm_sr.first;
+        symbol_record sr = nm_sr.second;
+
+        if (! sr.is_hidden ())
+          {
+            octave_value val = sr.varval ();
+
+            if (val.is_defined ())
+              {
+                // FIXME: fix size for objects, see kluge in variables.cc
+                //dim_vector dv = val.dims ();
+                octave_value tmp = val;
+                Matrix sz = tmp.size ();
+                dim_vector dv = dim_vector::alloc (sz.numel ());
+                for (octave_idx_type i = 0; i < dv.ndims (); i++)
+                  dv(i) = sz(i);
+
+                char storage = ' ';
+                if (sr.is_global ())
+                  storage = 'g';
+                else if (sr.is_persistent ())
+                  storage = 'p';
+                else if (sr.is_automatic ())
+                  storage = 'a';
+                else if (sr.is_formal ())
+                  storage = 'f';
+                else if (sr.is_hidden ())
+                  storage = 'h';
+                else if (sr.is_inherited ())
+                  storage = 'i';
+
+                std::ostringstream buf;
+                val.short_disp (buf);
+                std::string short_disp_str = buf.str ();
+
+                workspace_element elt (storage, nm, val.class_name (),
+                                       short_disp_str, dv.str (),
+                                       val.iscomplex ());
+
+                retval.push_back (elt);
+              }
+          }
+      }
+
+    return retval;
+  }
+
+  octave_value
+  scope::dump (void) const
+  {
+    std::map<std::string, octave_value> m
+      = {{ "name", m_name },
+         { "symbols", dump_symbols_map () },
+         { "persistent_variables", m_persistent_symbols },
+         { "subfunctions", dump_function_map (m_subfunctions) }};
+
+    return octave_value (m);
+  }
+
+  octave_value
+  scope::dump_symbols_map (void) const
+  {
+    std::map<std::string, octave_value> info_map;
+
+    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 ();
+      }
+
+    return octave_value (info_map);
+  }
+
+  void
+  scope::install_subfunction (const std::string& name,
+                              const octave_value& fval, bool is_nested)
+  {
+    m_subfunctions[name] = fval;
+
+    // This can be simpler once the scope object is stored in the function
+    // object...
+    octave_user_function *fcn = fval.user_function_value ();
+
+    scope *fcn_scope = fcn->scope ();
+
+    fcn_scope->set_parent (this);
+
+    if (is_nested)
+      {
+        m_children.push_back (fcn_scope);
+
+        fcn->mark_as_nested_function ();
+
+        fcn_scope->m_is_nested = true;
+      }
+
+  }
+
+  octave_value
+  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
+  scope::mark_subfunctions_in_scope_as_private (const std::string& class_name)
+  {
+    for (auto& nm_sf : m_subfunctions)
+      {
+        octave_function *fcn = nm_sf.second.function_value ();
+
+        if (fcn)
+          fcn->mark_as_private_function (class_name);
+      }
+  }
+
+  void
+  scope::set_parent (scope *p)
+  {
+    m_parent = p;
+
+    if (m_parent)
+      {
+        // If m_parent is the top-level scope, there will be no parent
+        // function.
+
+        octave_function *current_fcn = function ();
+
+        if (current_fcn && current_fcn->is_anonymous_function ())
+          {
+            octave_function *parent_fcn = m_parent->function ();
+
+            if (parent_fcn)
+              m_parent_fcn = octave_value (parent_fcn, true);
+          }
+      }
+  }
+
+  void
+  scope::update_nest (void)
+  {
+    if (m_parent)
+      {
+        // fix bad symbol_records
+        for (auto& nm_sr : m_symbols)
+          {
+            symbol_record& ours = nm_sr.second;
+
+            if (! ours.is_formal ()
+                && m_is_nested && m_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");
+              }
+            else
+              ours.set_curr_fcn (m_fcn);
+          }
+
+        // The scopes of nested functions are static.
+        m_is_static = true;
+      }
+    else if (m_children.size ())
+      {
+        // Parents of nested functions have static scopes.
+        m_is_static = true;
+
+        for (auto& nm_sr : m_symbols)
+          nm_sr.second.set_curr_fcn (m_fcn);
+      }
+
+    for (auto& symtab_p : m_children)
+      symtab_p->update_nest ();
+  }
+
+  bool
+  scope::look_nonlocal (const std::string& name, symbol_record& result)
+  {
+    table_iterator p = m_symbols.find (name);
+    if (p == m_symbols.end ())
+      {
+        if (m_is_nested && m_parent)
+          return m_parent->look_nonlocal (name, result);
+      }
+    else if (! p->second.is_automatic ())
+      {
+        result.bind_fwd_rep (p->second);
+        return true;
+      }
+
+    return false;
+  }
+
+  void
+  scope::bind_script_symbols (scope *curr_scope)
+  {
+    for (auto& nm_sr : m_symbols)
+      nm_sr.second.bind_fwd_rep (curr_scope->find_symbol (nm_sr.first));
+  }
+
+  void
+  scope::unbind_script_symbols (void)
+  {
+    for (auto& nm_sr : m_symbols)
+      nm_sr.second.unbind_fwd_rep ();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/scope.h	Thu Nov 16 16:06:31 2017 -0500
@@ -0,0 +1,587 @@
+/*
+
+Copyright (C) 1993-2017 John W. Eaton
+Copyright (C) 2009 VZLU Prague
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_scope_h)
+#define octave_scope_h 1
+
+#include "octave-config.h"
+
+#include <deque>
+#include <limits>
+#include <list>
+#include <map>
+#include <set>
+#include <string>
+
+#include "glob-match.h"
+#include "lo-regexp.h"
+#include "oct-refcount.h"
+
+class tree_argument_list;
+class octave_user_function;
+
+#include "ov.h"
+#include "ovl.h"
+#include "symrec.h"
+#include "workspace-element.h"
+
+namespace octave
+{
+  class scope
+  {
+  public:
+
+    typedef symbol_record::context_id context_id;
+
+    typedef std::map<std::string, symbol_record>::const_iterator
+    table_const_iterator;
+    typedef std::map<std::string, symbol_record>::iterator
+    table_iterator;
+
+    typedef std::map<std::string, octave_value>::const_iterator
+    m_persistent_symbols_const_iterator;
+    typedef std::map<std::string, octave_value>::iterator
+    m_persistent_symbols_iterator;
+
+    typedef std::map<std::string, octave_value>::const_iterator
+    subfunctions_const_iterator;
+    typedef std::map<std::string, octave_value>::iterator
+    subfunctions_iterator;
+
+    scope (const std::string& name = "")
+      : m_name (name), m_symbols (), m_persistent_symbols (), m_subfunctions (),
+        m_fcn (nullptr), m_parent (nullptr), m_parent_fcn (), m_children (), m_is_nested (false),
+        m_is_static (false), m_context (0)
+    { }
+
+    // No copying!
+
+    scope (const scope&) = delete;
+
+    scope& operator = (const scope&) = delete;
+
+    ~scope (void) = default;
+
+    void insert_symbol_record (const symbol_record& sr)
+    {
+      m_symbols[sr.name ()] = sr;
+    }
+
+    bool is_nested (void) const { return m_is_nested; }
+
+    void mark_nested (void) { m_is_nested = true; }
+
+    bool is_static (void) const { return m_is_static; }
+
+    void mark_static (void) { m_is_static = true; }
+
+    scope * parent_scope (void) const { return m_parent; }
+    octave_value parent_fcn (void) const { return m_parent_fcn; }
+
+    scope * dup (void) const
+    {
+      scope *new_sid = new scope ();
+
+      for (const auto& nm_sr : m_symbols)
+        new_sid->insert_symbol_record (nm_sr.second.dup (new_sid));
+
+      new_sid->m_parent = m_parent;
+      new_sid->m_parent_fcn = m_parent_fcn;
+
+      return new_sid;
+    }
+
+    void set_context (context_id context) { m_context = context; }
+
+    context_id current_context (void) const { return m_context; }
+
+    symbol_record find_symbol (const std::string& name)
+    {
+      table_iterator p = m_symbols.find (name);
+
+      if (p == m_symbols.end ())
+        return insert (name);
+      else
+        return p->second;
+    }
+
+    void inherit_internal (scope& donor_scope)
+    {
+      for (auto& nm_sr : m_symbols)
+        {
+          symbol_record& sr = nm_sr.second;
+
+          if (! (sr.is_automatic () || sr.is_formal ()))
+            {
+              std::string nm = sr.name ();
+
+              if (nm != "__retval__")
+                {
+                  octave_value val = donor_scope.varval (nm);
+
+                  if (val.is_defined ())
+                    {
+                      sr.assign (val);
+
+                      sr.mark_inherited ();
+                    }
+                }
+            }
+        }
+    }
+
+    void inherit (scope *donor_scope)
+    {
+      while (donor_scope)
+        {
+          inherit_internal (*donor_scope);
+
+          if (donor_scope->is_nested ())
+            donor_scope = donor_scope->parent_scope ();
+          else
+            break;
+        }
+    }
+
+
+    octave_value
+    find (const std::string& name, const octave_value_list& args,
+          bool skip_variables, bool local_funcs);
+
+    symbol_record&
+    insert (const std::string& name, bool force_add = false);
+
+    void rename (const std::string& old_name, const std::string& new_name)
+    {
+      table_iterator p = m_symbols.find (old_name);
+
+      if (p != m_symbols.end ())
+        {
+          symbol_record sr = p->second;
+
+          sr.rename (new_name);
+
+          m_symbols.erase (p);
+
+          m_symbols[new_name] = sr;
+        }
+    }
+
+    void assign (const std::string& name, const octave_value& value,
+                 bool force_add)
+    {
+      table_iterator p = m_symbols.find (name);
+
+      if (p == m_symbols.end ())
+        {
+          symbol_record& sr = insert (name, force_add);
+
+          sr.assign (value);
+        }
+      else
+        p->second.assign (value);
+    }
+
+    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)
+    {
+      table_iterator p = m_symbols.find (name);
+
+      if (p == m_symbols.end ())
+        {
+          symbol_record& sr = insert (name, true);
+
+          sr.assign (value);
+        }
+      else
+        p->second.assign (value);
+    }
+
+    // Use assign (name, value, force_add) instead.
+    // Delete when deprecated varref functions are removed.
+    octave_value& varref (const std::string& name, bool force_add)
+    {
+      table_iterator p = m_symbols.find (name);
+
+      if (p == m_symbols.end ())
+        {
+          symbol_record& sr = insert (name, force_add);
+
+          return sr.varref ();
+        }
+      else
+        return p->second.varref ();
+    }
+
+    octave_value varval (const std::string& name) const
+    {
+      table_const_iterator p = m_symbols.find (name);
+
+      return (p != m_symbols.end ()
+              ? p->second.varval () : octave_value ());
+    }
+
+    void persistent_assign (const std::string& name, const octave_value& value)
+    {
+      m_persistent_symbols_iterator p = m_persistent_symbols.find (name);
+
+      if (p == m_persistent_symbols.end ())
+        m_persistent_symbols[name] = value;
+      else
+        p->second = value;
+    }
+
+    // Use persistent_assign (name, value) instead.
+    // Delete when deprecated varref functions are removed.
+    octave_value& persistent_varref (const std::string& name)
+    {
+      m_persistent_symbols_iterator p = m_persistent_symbols.find (name);
+
+      return (p == m_persistent_symbols.end ()
+              ? m_persistent_symbols[name] : p->second);
+    }
+
+    octave_value persistent_varval (const std::string& name) const
+    {
+      m_persistent_symbols_const_iterator p = m_persistent_symbols.find (name);
+
+      return (p != m_persistent_symbols.end ()) ? p->second : octave_value ();
+    }
+
+    void erase_persistent (const std::string& name)
+    {
+      m_persistent_symbols_iterator p = m_persistent_symbols.find (name);
+
+      if (p != m_persistent_symbols.end ())
+        m_persistent_symbols.erase (p);
+    }
+
+    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 ();
+        }
+
+      return retval;
+    }
+
+    void push_context (void)
+    {
+      for (auto& nm_sr : m_symbols)
+        nm_sr.second.push_context (this);
+    }
+
+    void pop_context (void)
+    {
+      table_iterator tbl_it = m_symbols.begin ();
+
+      while (tbl_it != m_symbols.end ())
+        {
+          if (tbl_it->second.pop_context (this) == 0)
+            m_symbols.erase (tbl_it++);
+          else
+            tbl_it++;
+        }
+    }
+
+    void clear_variables (void)
+    {
+      for (auto& nm_sr : m_symbols)
+        nm_sr.second.clear (this);
+    }
+
+    void clear_objects (void)
+    {
+      for (auto& nm_sr : m_symbols)
+        {
+          symbol_record& sr = nm_sr.second;
+          octave_value val = sr.varval ();
+          if (val.isobject ())
+            nm_sr.second.clear (this);
+        }
+    }
+
+    void clear_global (const std::string& name);
+
+    void clear_variable (const std::string& name)
+    {
+      table_iterator p = m_symbols.find (name);
+
+      if (p != m_symbols.end ())
+        p->second.clear (this);
+    }
+
+    void clear_global_pattern (const std::string& pat);
+
+    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 () || sr.is_global ())
+            {
+              if (pattern.match (sr.name ()))
+                sr.clear (this);
+            }
+        }
+    }
+
+    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 () || sr.is_global ())
+            {
+              if (pattern.is_match (sr.name ()))
+                sr.clear (this);
+            }
+        }
+    }
+
+    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 ())
+              || (sr.xstorage_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 ())
+                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 ())
+                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 ())
+            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 ());
+    }
+
+    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,
+                              bool is_nested = false);
+
+    void install_nestfunction (const std::string& name,
+                               const octave_value& fval)
+    {
+      install_subfunction (name, fval, true);
+    }
+
+    octave_value find_subfunction (const std::string& name) const;
+
+    void lock_subfunctions (void)
+    {
+      for (auto& nm_sf : m_subfunctions)
+        nm_sf.second.lock ();
+    }
+
+    void unlock_subfunctions (void)
+    {
+      for (auto& nm_sf : m_subfunctions)
+        nm_sf.second.unlock ();
+    }
+
+    std::map<std::string, octave_value> subfunctions (void)
+    {
+      return m_subfunctions;
+    }
+
+    void erase_subfunctions (void)
+    {
+      m_subfunctions.clear ();
+    }
+
+    void mark_subfunctions_in_scope_as_private (const std::string& class_name);
+
+    std::list<workspace_element> workspace_info (void) const;
+
+    octave_value dump (void) const;
+
+    std::string name (void) const { return m_name; }
+
+    void cache_name (const std::string& name) { m_name = name; }
+
+    octave_user_function *function (void) { return m_fcn; }
+
+    void set_function (octave_user_function *fcn) { m_fcn = fcn; }
+
+    void set_parent (scope *p);
+
+    void update_nest (void);
+
+    bool look_nonlocal (const std::string& name, symbol_record& result);
+
+    void bind_script_symbols (scope *curr_scope);
+
+    void unbind_script_symbols (void);
+
+  private:
+
+    // Name for this scope (usually the corresponding filename of the
+    // function corresponding to the scope).
+    std::string m_name;
+
+    // Map from symbol names to symbol info.
+    std::map<std::string, symbol_record> m_symbols;
+
+    // Map from names of persistent variables to values.
+    std::map<std::string, octave_value> m_persistent_symbols;
+
+    // Map from symbol names to subfunctions.
+    std::map<std::string, octave_value> m_subfunctions;
+
+    // The associated user code (may be null).
+    octave_user_function *m_fcn;
+
+    // Parent of nested function (may be null).
+    scope *m_parent;
+    octave_value m_parent_fcn;
+
+    // Child nested functions.
+    std::vector<scope*> m_children;
+
+    // If true, then this scope belongs to a nested function.
+    bool m_is_nested;
+
+    // If true then no variables can be added.
+    bool m_is_static;
+
+    context_id m_context;
+
+    octave_value dump_symbols_map (void) const;
+  };
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/symrec.cc	Thu Nov 16 16:06:31 2017 -0500
@@ -0,0 +1,245 @@
+/*
+
+Copyright (C) 1993-2017 John W. Eaton
+Copyright (C) 2009 VZLU Prague, a.s.
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if defined (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include <sstream>
+
+#include "file-ops.h"
+#include "file-stat.h"
+#include "oct-env.h"
+#include "oct-time.h"
+
+#include "fcn-info.h"
+#include "interpreter-private.h"
+#include "interpreter.h"
+#include "scope.h"
+#include "symrec.h"
+#include "symtab.h"
+
+namespace octave
+{
+  symbol_record::context_id
+  symbol_record::symbol_record_rep::get_decl_scope_context (void) const
+  {
+    return m_decl_scope ? m_decl_scope->current_context () : 0;
+  }
+
+  void
+  symbol_record::symbol_record_rep::clear (scope *sid)
+  {
+    if (m_fwd_rep)
+      {
+        m_fwd_rep->clear (sid);
+        return;
+      }
+
+    if (! (is_hidden () || is_inherited ())
+        && sid == decl_scope ())
+      {
+        if (is_global ())
+          unmark_global ();
+
+        if (is_persistent ())
+          {
+            sid->persistent_assign (name, varval ());
+
+            unmark_persistent ();
+          }
+
+        assign (octave_value ());
+      }
+  }
+
+  void
+  symbol_record::symbol_record_rep::init_persistent (void)
+  {
+    if (m_fwd_rep)
+      {
+        m_fwd_rep->init_persistent ();
+        return;
+      }
+
+    scope *curr_scope
+      = __require_current_scope__ ("symbol_record::symbol_record_rep::init_persistent");
+
+    if (! is_defined ())
+      {
+        mark_persistent ();
+
+        assign (curr_scope->persistent_varval (name));
+      }
+    // FIXME: this causes trouble with recursive calls.
+    // else
+    //   error ("unable to declare existing variable persistent");
+  }
+
+  void
+  symbol_record::symbol_record_rep::erase_persistent (void)
+  {
+    if (m_fwd_rep)
+      {
+        m_fwd_rep->erase_persistent ();
+        return;
+      }
+
+    unmark_persistent ();
+
+    scope *curr_scope
+      = __require_current_scope__ ("symbol_record::symbol_record_rep::erase_persistent");
+
+    curr_scope->erase_persistent (name);
+  }
+
+  symbol_record::symbol_record_rep *
+  symbol_record::symbol_record_rep::dup (scope *new_scope) const
+  {
+    // FIXME: is this the right thing do to?
+    if (m_fwd_rep)
+      return m_fwd_rep->dup (new_scope);
+
+    return new symbol_record_rep (new_scope, name, varval (), storage_class);
+  }
+
+  octave_value
+  symbol_record::symbol_record_rep::dump (void) const
+  {
+    if (m_fwd_rep)
+      return m_fwd_rep->dump ();
+
+    std::map<std::string, octave_value> m
+      = {{ "name", 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 ();
+
+    if (val.is_defined ())
+      m["value"] = val;
+
+    return octave_value (m);
+  }
+
+  octave_value&
+  symbol_record::symbol_record_rep::xglobal_varref (void)
+  {
+    if (m_fwd_rep)
+      return m_fwd_rep->xglobal_varref ();
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("symbol_record::symbol_record_rep::xglobal_varref");
+
+    return symtab.global_varref (name);
+  }
+
+  octave_value&
+  symbol_record::symbol_record_rep::xpersistent_varref (void)
+  {
+    if (m_fwd_rep)
+      return m_fwd_rep->xpersistent_varref ();
+
+    scope *curr_scope
+      = __get_current_scope__ ("symbol_record::symbol_record_rep::xpersistent_varref");
+
+    return (curr_scope
+            ? curr_scope->persistent_varref (name) : dummy_octave_value);
+  }
+
+  octave_value
+  symbol_record::symbol_record_rep::xglobal_varval (void) const
+  {
+    if (m_fwd_rep)
+      return m_fwd_rep->xglobal_varval ();
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("symbol_record::symbol_record_rep::xglobal_varval");
+
+    return symtab.global_varval (name);
+  }
+
+  octave_value
+  symbol_record::symbol_record_rep::xpersistent_varval (void) const
+  {
+    if (m_fwd_rep)
+      return m_fwd_rep->xpersistent_varval ();
+
+    scope *curr_scope
+      = __get_current_scope__ ("symbol_record::symbol_record_rep::xpersistent_varval");
+
+    return curr_scope ? curr_scope->persistent_varval (name) : octave_value ();
+  }
+
+  symbol_record::symbol_record (void)
+    : rep (new symbol_record_rep (__get_current_scope__ ("symbol_record"),
+                                  "", octave_value (), local))
+
+  { }
+
+  octave_value
+  symbol_record::find (const octave_value_list& args) const
+  {
+    octave_value retval;
+
+    symbol_table& symtab
+      = __get_symbol_table__ ("symbol_record::find");
+
+    if (is_global ())
+      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 = symtab.find_function (name (), args);
+
+                if (retval.is_defined ())
+                  return retval;
+#if 0
+                {
+                  rep->finfo = symtab.get_fcn_info (name ());
+                }
+#endif
+              }
+          }
+      }
+
+    return retval;
+  }
+
+  octave_value symbol_record::dummy_octave_value;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/corefcn/symrec.h	Thu Nov 16 16:06:31 2017 -0500
@@ -0,0 +1,783 @@
+/*
+
+Copyright (C) 1993-2017 John W. Eaton
+Copyright (C) 2009 VZLU Prague
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#if ! defined (octave_symrec_h)
+#define octave_symrec_h 1
+
+#include "octave-config.h"
+
+#include <deque>
+#include <list>
+#include <string>
+
+#include "oct-refcount.h"
+
+class octave_user_function;
+
+#include "ov.h"
+#include "ovl.h"
+
+namespace octave
+{
+  class fcn_info;
+  class scope;
+
+  class symbol_record
+  {
+  public:
+
+    typedef size_t context_id;
+
+    // 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;
+
+    // this symbol may NOT become a variable.
+    // (symbol added to a static workspace)
+    static const unsigned int added_static = 128;
+
+  private:
+
+    class symbol_record_rep
+    {
+    public:
+
+      symbol_record_rep (scope *s, const std::string& nm,
+                         const octave_value& v, unsigned int sc)
+        : m_decl_scope (s), curr_fcn (nullptr), name (nm),
+          m_fwd_rep (nullptr), value_stack (),
+          storage_class (sc), /* finfo (), */ valid (true), count (1)
+      {
+        value_stack.push_back (v);
+      }
+
+      // No copying!
+
+      symbol_record_rep (const symbol_record_rep& ov) = delete;
+
+      symbol_record_rep& operator = (const symbol_record_rep&) = delete;
+
+      ~symbol_record_rep (void) = default;
+
+      void assign (const octave_value& value)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->assign (value);
+            return;
+          }
+
+        varref () = value;
+      }
+
+      void assign (octave_value::assign_op op,
+                   const std::string& type,
+                   const std::list<octave_value_list>& idx,
+                   const octave_value& value)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->assign (op, type, idx, value);
+            return;
+          }
+
+        varref().assign (op, type, idx, value);
+      }
+
+      void assign (octave_value::assign_op op, const octave_value& value)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->assign (op, value);
+            return;
+          }
+
+        varref().assign (op, value);
+      }
+
+      void do_non_const_unary_op (octave_value::unary_op op)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->do_non_const_unary_op (op);
+            return;
+          }
+
+        varref().do_non_const_unary_op (op);
+      }
+
+      void do_non_const_unary_op (octave_value::unary_op op,
+                                  const std::string& type,
+                                  const std::list<octave_value_list>& idx)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->do_non_const_unary_op (op, type, idx);
+            return;
+          }
+
+        varref().do_non_const_unary_op (op, type, idx);
+      }
+
+      context_id get_decl_scope_context (void) const;
+
+      octave_value& varref (void)
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->varref ();
+
+        context_id context = m_decl_scope ? get_decl_scope_context () : 0;
+
+        if (is_global ())
+          return xglobal_varref ();
+        else if (is_persistent ())
+          return xpersistent_varref ();
+        else
+          {
+            context_id n = value_stack.size ();
+            while (n++ <= context)
+              value_stack.push_back (octave_value ());
+
+            return value_stack[context];
+          }
+      }
+
+      octave_value varval (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->varval ();
+
+        context_id context = m_decl_scope ? get_decl_scope_context () : 0;
+
+        if (is_global ())
+          return xglobal_varval ();
+        else if (is_persistent ())
+          return xpersistent_varval ();
+        else
+          {
+            if (context < value_stack.size ())
+              return value_stack[context];
+            else
+              return octave_value ();
+          }
+      }
+
+      void push_context (scope *sid)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->push_context (sid);
+            return;
+          }
+
+        if (! (is_persistent () || is_global ())
+            && sid == decl_scope ())
+          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.
+
+      size_t pop_context (scope *sid)
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->pop_context (sid);
+
+        size_t retval = 1;
+
+        if (! (is_persistent () || is_global ())
+            && sid == decl_scope ())
+          {
+            value_stack.pop_back ();
+            retval = value_stack.size ();
+          }
+
+        return retval;
+      }
+
+      void clear (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->clear ();
+            return;
+          }
+
+        clear (decl_scope ());
+      }
+
+      void clear (scope *sid);
+
+      bool is_defined (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_defined ();
+
+        return varval ().is_defined ();
+      }
+
+      bool is_valid (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_valid ();
+
+        return valid;
+      }
+
+      bool is_variable (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_variable ();
+
+        return (! is_local () || is_defined ());
+      }
+
+      bool is_local (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_local ();
+
+        return storage_class & local;
+      }
+
+      bool is_automatic (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_automatic ();
+
+        return storage_class & automatic;
+      }
+
+      bool is_formal (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_formal ();
+
+        return storage_class & formal;
+      }
+
+      bool is_hidden (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_hidden ();
+
+        return storage_class & hidden;
+      }
+
+      bool is_inherited (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_inherited ();
+
+        return storage_class & inherited;
+      }
+
+      bool is_global (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_global ();
+
+        return storage_class & global;
+      }
+
+      bool is_persistent (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_persistent ();
+
+        return storage_class & persistent;
+      }
+
+      bool is_added_static (void) const
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->is_added_static ();
+
+        return storage_class & added_static;
+      }
+
+      void mark_local (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_local ();
+            return;
+          }
+
+        storage_class |= local;
+      }
+
+      void mark_automatic (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_automatic ();
+            return;
+          }
+
+        storage_class |= automatic;
+      }
+
+      void mark_formal (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_formal ();
+            return;
+          }
+
+        storage_class |= formal;
+      }
+
+      void mark_hidden (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_hidden ();
+            return;
+          }
+
+        storage_class |= hidden;
+      }
+
+      void mark_inherited (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_inherited ();
+            return;
+          }
+
+        storage_class |= inherited;
+      }
+
+      void mark_global (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_global ();
+            return;
+          }
+
+        if (is_persistent ())
+          error ("can't make persistent variable %s global", name.c_str ());
+
+        storage_class |= global;
+      }
+
+      void mark_persistent (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_persistent ();
+            return;
+          }
+
+        if (is_global ())
+          error ("can't make global variable %s persistent", name.c_str ());
+
+        storage_class |= persistent;
+      }
+
+      void mark_added_static (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->mark_added_static ();
+            return;
+          }
+
+        storage_class |= added_static;
+      }
+
+      void unmark_local (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_local ();
+            return;
+          }
+
+        storage_class &= ~local;
+      }
+
+      void unmark_automatic (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_automatic ();
+            return;
+          }
+
+        storage_class &= ~automatic;
+      }
+
+      void unmark_formal (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_formal ();
+            return;
+          }
+
+        storage_class &= ~formal;
+      }
+
+      void unmark_hidden (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_hidden ();
+            return;
+          }
+
+        storage_class &= ~hidden;
+      }
+
+      void unmark_inherited (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_inherited ();
+            return;
+          }
+
+        storage_class &= ~inherited;
+      }
+
+      void unmark_global (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_global ();
+            return;
+          }
+
+        storage_class &= ~global;
+      }
+
+      void unmark_persistent (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_persistent ();
+            return;
+          }
+
+        storage_class &= ~persistent;
+      }
+
+      void unmark_added_static (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->unmark_added_static ();
+            return;
+          }
+
+        storage_class &= ~added_static;
+      }
+
+      void init_persistent (void);
+
+      void invalidate (void)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->invalidate ();
+            return;
+          }
+
+        valid = false;
+      }
+
+      void erase_persistent (void);
+
+      scope *decl_scope (void)
+      {
+        if (m_fwd_rep)
+          return m_fwd_rep->decl_scope ();
+
+        return m_decl_scope;
+      }
+
+      void set_curr_fcn (octave_user_function *fcn)
+      {
+        if (m_fwd_rep)
+          {
+            m_fwd_rep->set_curr_fcn (fcn);
+            return;
+          }
+
+        curr_fcn = fcn;
+      }
+
+      // We don't forward more than once, so no need to forward the
+      // next two.
+
+      void bind_fwd_rep (symbol_record_rep *fwd_rep) { m_fwd_rep = fwd_rep; }
+
+      void unbind_fwd_rep (void) { m_fwd_rep = nullptr; }
+
+      symbol_record_rep * dup (scope *new_scope) const;
+
+      octave_value dump (void) const;
+
+      scope *m_decl_scope;
+
+      octave_user_function *curr_fcn;
+
+      std::string name;
+
+      symbol_record_rep *m_fwd_rep;
+
+      std::deque<octave_value> value_stack;
+
+      unsigned int storage_class;
+
+      //      fcn_info *finfo;
+
+      bool valid;
+
+      refcount<size_t> count;
+
+    private:
+
+      octave_value& xglobal_varref (void);
+
+      octave_value& xpersistent_varref (void);
+
+      octave_value xglobal_varval (void) const;
+
+      octave_value xpersistent_varval (void) const;
+    };
+
+  public:
+
+    symbol_record (void);
+
+    symbol_record (scope *s, const std::string& nm = "",
+                   const octave_value& v = octave_value (),
+                   unsigned int sc = local)
+      : rep (new symbol_record_rep (s, nm, v, sc)) { }
+
+    symbol_record (const symbol_record& sr)
+      : rep (sr.rep)
+    {
+      rep->count++;
+    }
+
+    symbol_record& operator = (const symbol_record& sr)
+    {
+      if (this != &sr)
+        {
+          if (--rep->count == 0)
+            delete rep;
+
+          rep = sr.rep;
+          rep->count++;
+        }
+
+      return *this;
+    }
+
+    ~symbol_record (void)
+    {
+      if (--rep->count == 0)
+        delete rep;
+    }
+
+    symbol_record dup (scope *sid) const
+    {
+      return symbol_record (rep->dup (sid));
+    }
+
+    const std::string& name (void) const { return rep->name; }
+
+    void rename (const std::string& new_name) { rep->name = new_name; }
+
+    octave_value
+    find (const octave_value_list& args = octave_value_list ()) const;
+
+    void assign (const octave_value& value)
+    {
+      rep->assign (value);
+    }
+
+    void assign (octave_value::assign_op op,
+                 const std::string& type,
+                 const std::list<octave_value_list>& idx,
+                 const octave_value& value)
+    {
+      rep->assign (op, type, idx, value);
+    }
+
+    void assign (octave_value::assign_op op, const octave_value& value)
+    {
+      rep->assign (op, value);
+    }
+
+    void do_non_const_unary_op (octave_value::unary_op op)
+    {
+      rep->do_non_const_unary_op (op);
+    }
+
+    void do_non_const_unary_op (octave_value::unary_op op,
+                                const std::string& type,
+                                const std::list<octave_value_list>& idx)
+    {
+      rep->do_non_const_unary_op (op, type, idx);
+    }
+
+    // Delete when deprecated varref functions are removed.
+    octave_value& varref (void)
+    {
+      return rep->varref ();
+    }
+
+    octave_value varval (void) const
+    {
+      return rep->varval ();
+    }
+
+    void push_context (scope *sid) { rep->push_context (sid); }
+
+    size_t pop_context (scope *sid) { return rep->pop_context (sid); }
+
+    void clear (void) { rep->clear (); }
+
+    void clear (scope *sid) { rep->clear (sid); }
+
+    bool is_defined (void) const
+    {
+      return rep->is_defined ();
+    }
+
+    bool is_undefined (void) const
+    {
+      return ! rep->is_defined ();
+    }
+
+    bool is_valid (void) const
+    {
+      return rep->is_valid ();
+    }
+
+    bool is_variable (void) const
+    {
+      return rep->is_variable ();
+    }
+
+    bool is_local (void) const { return rep->is_local (); }
+    bool is_automatic (void) const { return rep->is_automatic (); }
+    bool is_formal (void) const { return rep->is_formal (); }
+    bool is_global (void) const { return rep->is_global (); }
+    bool is_hidden (void) const { return rep->is_hidden (); }
+    bool is_inherited (void) const { return rep->is_inherited (); }
+    bool is_persistent (void) const { return rep->is_persistent (); }
+    bool is_added_static (void) const { return rep->is_added_static (); }
+
+    void mark_local (void) { rep->mark_local (); }
+    void mark_automatic (void) { rep->mark_automatic (); }
+    void mark_formal (void) { rep->mark_formal (); }
+    void mark_hidden (void) { rep->mark_hidden (); }
+    void mark_inherited (void) { rep->mark_inherited (); }
+    void mark_global (void) { rep->mark_global (); }
+    void mark_persistent (void) { rep->mark_persistent (); }
+    void mark_added_static (void) { rep->mark_added_static (); }
+
+    void unmark_local (void) { rep->unmark_local (); }
+    void unmark_automatic (void) { rep->unmark_automatic (); }
+    void unmark_formal (void) { rep->unmark_formal (); }
+    void unmark_hidden (void) { rep->unmark_hidden (); }
+    void unmark_inherited (void) { rep->unmark_inherited (); }
+    void unmark_global (void) { rep->unmark_global (); }
+    void unmark_persistent (void) { rep->unmark_persistent (); }
+    void unmark_added_static (void) { rep->unmark_added_static (); }
+
+    void init_persistent (void) { rep->init_persistent (); }
+
+    void erase_persistent (void) { rep->erase_persistent (); }
+
+    void invalidate (void) { rep->invalidate (); }
+
+    scope *decl_scope (void) { return rep->decl_scope (); }
+
+    unsigned int xstorage_class (void) const { return rep->storage_class; }
+
+    void set_curr_fcn (octave_user_function *fcn)
+    {
+      rep->set_curr_fcn (fcn);
+    }
+
+    void bind_fwd_rep (const symbol_record& sr)
+    {
+      rep->bind_fwd_rep (sr.rep);
+    }
+
+    void unbind_fwd_rep (void) { rep->unbind_fwd_rep (); }
+
+    octave_value dump (void) const { return rep->dump (); }
+
+    const symbol_record_rep *xrep (void) const { return rep; }
+
+  private:
+
+    static octave_value dummy_octave_value;
+
+    symbol_record_rep *rep;
+
+    symbol_record (symbol_record_rep *new_rep) : rep (new_rep) { }
+  };
+}
+
+#endif
--- a/libinterp/corefcn/symtab.cc	Thu Nov 16 16:12:12 2017 -0500
+++ b/libinterp/corefcn/symtab.cc	Thu Nov 16 16:06:31 2017 -0500
@@ -35,6 +35,7 @@
 #include "bp-table.h"
 #include "defun.h"
 #include "dirfns.h"
+#include "fcn-info.h"
 #include "input.h"
 #include "interpreter-private.h"
 #include "interpreter.h"
@@ -45,6 +46,8 @@
 #include "pager.h"
 #include "parse.h"
 #include "pt-pr-code.h"
+#include "scope.h"
+#include "symrec.h"
 #include "symtab.h"
 #include "unwind-prot.h"
 #include "utils.h"
@@ -53,208 +56,6 @@
 // since they were last compiled?
 static int Vignore_function_time_stamp = 1;
 
-namespace octave
-{
-  octave_value symbol_table::dummy_octave_value;
-
-  void
-  symbol_table::symbol_record::symbol_record_rep::clear (scope *sid)
-  {
-    if (m_fwd_rep)
-      {
-        m_fwd_rep->clear (sid);
-        return;
-      }
-
-    if (! (is_hidden () || is_inherited ())
-        && sid == decl_scope ())
-      {
-        if (is_global ())
-          unmark_global ();
-
-        if (is_persistent ())
-          {
-            sid->persistent_assign (name, varval ());
-
-            unmark_persistent ();
-          }
-
-        assign (octave_value ());
-      }
-  }
-
-  void
-  symbol_table::symbol_record::symbol_record_rep::init_persistent (void)
-  {
-    if (m_fwd_rep)
-      {
-        m_fwd_rep->init_persistent ();
-        return;
-      }
-
-    symbol_table::scope *scope
-      = __require_current_scope__ ("symbol_table::symbol_record::symbol_record_rep::init_persistent");
-
-    if (! is_defined ())
-      {
-        mark_persistent ();
-
-        assign (scope->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)
-  {
-    if (m_fwd_rep)
-      {
-        m_fwd_rep->erase_persistent ();
-        return;
-      }
-
-    unmark_persistent ();
-
-    symbol_table::scope *scope
-      = __require_current_scope__ ("symbol_table::symbol_record::symbol_record_rep::erase_persistent");
-
-    scope->erase_persistent (name);
-  }
-
-  symbol_table::symbol_record::symbol_record_rep *
-  symbol_table::symbol_record::symbol_record_rep::dup (scope *new_scope) const
-  {
-    // FIXME: is this the right thing do to?
-    if (m_fwd_rep)
-      return m_fwd_rep->dup (new_scope);
-
-    return new symbol_record_rep (new_scope, name, varval (), storage_class);
-  }
-
-  octave_value
-  symbol_table::symbol_record::symbol_record_rep::dump (void) const
-  {
-    if (m_fwd_rep)
-      return m_fwd_rep->dump ();
-
-    std::map<std::string, octave_value> m
-      = {{ "name", 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 ();
-
-    if (val.is_defined ())
-      m["value"] = val;
-
-    return octave_value (m);
-  }
-
-  octave_value&
-  symbol_table::symbol_record::symbol_record_rep::xglobal_varref (void)
-  {
-    if (m_fwd_rep)
-      return m_fwd_rep->xglobal_varref ();
-
-    symbol_table& symtab
-      = __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)
-  {
-    if (m_fwd_rep)
-      return m_fwd_rep->xpersistent_varref ();
-
-    symbol_table::scope *scope
-      = __get_current_scope__ ("symbol_table::symbol_record::symbol_record_rep::xpersistent_varref");
-
-    return scope ? scope->persistent_varref (name) : dummy_octave_value;
-  }
-
-  octave_value
-  symbol_table::symbol_record::symbol_record_rep::xglobal_varval (void) const
-  {
-    if (m_fwd_rep)
-      return m_fwd_rep->xglobal_varval ();
-
-    symbol_table& symtab
-      = __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
-  {
-    if (m_fwd_rep)
-      return m_fwd_rep->xpersistent_varval ();
-
-    symbol_table::scope *scope
-      = __get_current_scope__ ("symbol_table::symbol_record::symbol_record_rep::xpersistent_varval");
-
-    return scope ? scope->persistent_varval (name) : octave_value ();
-  }
-
-  symbol_table::symbol_record::symbol_record (void)
-    : rep (new symbol_record_rep (__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
-      = __get_symbol_table__ ("symbol_table::symbol_record::find");
-
-    if (is_global ())
-      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 = symtab.find_function (name (), args);
-
-                if (retval.is_defined ())
-                  return retval;
-#if 0
-                {
-                  rep->finfo = symtab.get_fcn_info (name ());
-                }
-#endif
-              }
-          }
-      }
-
-    return retval;
-  }
-}
-
 static void
 split_name_with_package (const std::string& name, std::string& fname,
                          std::string& pname)
@@ -492,755 +293,27 @@
     return retval;
   }
 
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::load_private_function
-  (const std::string& dir_name)
-  {
-    octave_value retval;
-
-    load_path& lp
-      = __get_load_path__ ("symbol_table::fcn_info::fcn_info_rep::load_private_function");
-
-    std::string file_name = lp.find_private_fcn (dir_name, name);
-
-    if (file_name.empty ())
-      return retval;
-
-    octave_value ov_fcn = load_fcn_from_file (file_name, dir_name);
-
-    if (ov_fcn.is_undefined ())
-      return retval;
-
-    octave_function *tmpfcn = ov_fcn.function_value ();
-
-    if (! tmpfcn)
-      return retval;
-
-    std::string class_name;
-
-    size_t pos = dir_name.find_last_of (sys::file_ops::dir_sep_chars ());
-
-    if (pos != std::string::npos)
-      {
-        std::string tmp = dir_name.substr (pos+1);
-
-        if (tmp[0] == '@')
-          class_name = tmp.substr (1);
-      }
-
-    tmpfcn->mark_as_private_function (class_name);
-
-    private_functions[dir_name] = ov_fcn;
-
-    return ov_fcn;
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::load_class_constructor (void)
+  void
+  symbol_table::erase_global (const std::string& name)
   {
-    octave_value retval;
-
-    std::string dir_name;
-
-    load_path& lp
-      = __get_load_path__ ("symbol_table::fcn_info::fcn_info_rep::load_class_constructor");
-
-    std::string file_name = lp.find_method (name, name, dir_name, package_name);
-
-    if (! file_name.empty ())
-      {
-        octave_value ov_fcn
-          = load_fcn_from_file (file_name, dir_name, name,
-                                        package_name);
-
-        if (ov_fcn.is_defined ())
-          {
-            // Note: ov_fcn may be an octave_classdef_meta object instead
-            // of the actual constructor function.
-
-            retval = ov_fcn;
-
-            class_constructors[name] = retval;
-            class_methods[name] = retval;
-          }
-      }
-    else
-      {
-        // Classdef constructors can be defined anywhere in the path, not
-        // necessarily in @-folders.  Look for a normal function and load it.
-        // If the loaded function is a classdef constructor, store it as such
-        // and restore function_on_path to its previous value.
-
-        octave_value old_function_on_path = function_on_path;
-
-        octave_value maybe_cdef_ctor = find_user_function ();
-
-        if (maybe_cdef_ctor.is_defined ())
-          {
-            octave_function *fcn = maybe_cdef_ctor.function_value (true);
-
-            if (fcn && fcn->is_classdef_constructor ())
-              {
-                retval = maybe_cdef_ctor;
-
-                class_constructors[name] = retval;
-                class_methods[name] = retval;
-
-                function_on_path = old_function_on_path;
-              }
-          }
-      }
-
-    return retval;
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::load_class_method
-  (const std::string& dispatch_type)
-  {
-    octave_value retval;
-
-    if (full_name () == dispatch_type)
-      retval = load_class_constructor ();
-    else
-      {
-        cdef_manager& cdm
-          = __get_cdef_manager__ ("symbol_table::fcn_info::fcn_info_rep::load_class_method");
-
-        octave_function *cm = cdm.find_method_symbol (name, dispatch_type);
-
-        if (cm)
-          retval = octave_value (cm);
-
-        if (! retval.is_defined ())
-          {
-            std::string dir_name;
-
-            load_path& lp = __get_load_path__ ("symbol_table::fcn_info::fcn_info_rep::load_class_method");
-
-            std::string file_name = lp.find_method (dispatch_type, name,
-                                                    dir_name);
-
-            if (! file_name.empty ())
-              {
-                octave_value ov_fcn
-                  = load_fcn_from_file (file_name, dir_name,
-                                                dispatch_type);
-
-                if (ov_fcn.is_defined ())
-                  {
-                    octave_function *tmpfcn = ov_fcn.function_value ();
-
-                    if (tmpfcn && tmpfcn->is_class_method (dispatch_type))
-                      {
-                        retval = ov_fcn;
-
-                        class_methods[dispatch_type] = retval;
-                      }
-                  }
-              }
-
-            if (retval.is_undefined ())
-              {
-                // Search parent classes
-
-                symbol_table& symtab
-                  = __get_symbol_table__ ("symbol_table::fcn_info::fcn_info_rep::load_class_method");
-
-                const std::list<std::string>& plist =
-                  symtab.parent_classes (dispatch_type);
-
-                std::list<std::string>::const_iterator it = plist.begin ();
-
-                while (it != plist.end ())
-                  {
-                    retval = find_method (*it);
+    global_symbols_iterator q = m_global_symbols.find (name);
 
-                    if (retval.is_defined ())
-                      {
-                        class_methods[dispatch_type] = retval;
-                        break;
-                      }
-
-                    it++;
-                  }
-              }
-
-            if (retval.is_undefined ())
-              {
-                // Search for built-in functions that are declared to
-                // handle specific types.
-
-                if (built_in_function.is_defined ())
-                  {
-                    octave_function *fcn = built_in_function.function_value ();
-
-                    if (fcn && fcn->handles_dispatch_class (dispatch_type))
-                      {
-                        retval = built_in_function;
-
-                        class_methods[dispatch_type] = retval;
-                      }
-                  }
-              }
-          }
-      }
-
-    return retval;
-  }
-}
-
-// :-) 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.
-
-static builtin_type_t (*build_sup_table (void))[btyp_num_types]
-{
-  static builtin_type_t sup_table[btyp_num_types][btyp_num_types];
-  for (int i = 0; i < btyp_num_types; i++)
-    for (int j = 0; j < btyp_num_types; j++)
-      {
-        builtin_type_t ityp = static_cast<builtin_type_t> (i);
-        builtin_type_t jtyp = static_cast<builtin_type_t> (j);
-        // FIXME: Is this really right?
-        bool use_j =
-          (jtyp == btyp_func_handle || ityp == btyp_bool
-           || (btyp_isarray (ityp)
-               && (! btyp_isarray (jtyp)
-                   || (btyp_isinteger (jtyp) && ! btyp_isinteger (ityp))
-                   || ((ityp == btyp_double || ityp == btyp_complex
-                        || ityp == btyp_char)
-                       && (jtyp == btyp_float
-                           || jtyp == btyp_float_complex)))));
-
-        sup_table[i][j] = (use_j ? jtyp : ityp);
-      }
-
-  return sup_table;
-}
-
-namespace octave
-{
-  std::string
-  get_dispatch_type (const octave_value_list& args,
-                     builtin_type_t& builtin_type)
-  {
-    static builtin_type_t (*sup_table)[btyp_num_types] = build_sup_table ();
-    std::string dispatch_type;
-
-    int n = args.length ();
-
-    if (n > 0)
-      {
-        int i = 0;
-        builtin_type = args(0).builtin_type ();
-        if (builtin_type != btyp_unknown)
-          {
-            for (i = 1; i < n; i++)
-              {
-                builtin_type_t bti = args(i).builtin_type ();
-                if (bti != btyp_unknown)
-                  builtin_type = sup_table[builtin_type][bti];
-                else
-                  {
-                    builtin_type = btyp_unknown;
-                    break;
-                  }
-              }
-          }
-
-        if (builtin_type == btyp_unknown)
-          {
-            // There's a non-builtin class in the argument list.
-            dispatch_type = args(i).class_name ();
-
-            symbol_table& symtab = __get_symbol_table__ ("get_dispatch_type");
-
-            for (int j = i+1; j < n; j++)
-              {
-                octave_value arg = args(j);
-
-                if (arg.builtin_type () == btyp_unknown)
-                  {
-                    std::string cname = arg.class_name ();
-
-                    // Only switch to type of ARG if it is marked superior
-                    // to the current DISPATCH_TYPE.
-                    if (! symtab.is_superiorto (dispatch_type, cname)
-                        && symtab.is_superiorto (cname, dispatch_type))
-                      dispatch_type = cname;
-                  }
-              }
-          }
-        else
-          dispatch_type = btyp_class_name[builtin_type];
-      }
-    else
-      builtin_type = btyp_unknown;
-
-    return dispatch_type;
-  }
-
-  std::string
-  get_dispatch_type (const octave_value_list& args)
-  {
-    builtin_type_t builtin_type;
-    return get_dispatch_type (args, builtin_type);
-  }
-
-  // Find function definition according to the following precedence list:
-  //
-  //   private function
-  //   class method
-  //   class constructor
-  //   command-line function
-  //   autoload function
-  //   function on the path
-  //   built-in function
-  //
-  // Matlab documentation states that constructors have higher precedence
-  // than methods, but that does not seem to be the case.
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::find (const octave_value_list& args,
-                                              bool local_funcs)
-  {
-    octave_value retval = xfind (args, local_funcs);
-
-    if (retval.is_undefined ())
-      {
-        // It is possible that the user created a file on the fly since
-        // the last prompt or chdir, so try updating the load path and
-        // searching again.
-
-        load_path& lp = __get_load_path__ ("symbol_table::fcn_info::fcn_info_rep::find");
-
-        lp.update ();
-
-        retval = xfind (args, local_funcs);
-      }
-
-    return retval;
+    if (q != m_global_symbols.end ())
+      m_global_symbols.erase (q);
   }
 
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::xfind (const octave_value_list& args,
-                                               bool local_funcs)
+  void
+  symbol_table::erase_global_pattern (const glob_match& pattern)
   {
-    if (local_funcs)
-      {
-        symbol_table::scope *scope
-          = __get_current_scope__ ("symbol_table::fcn_info::fcn_info_rep::xfind");
-
-        octave_user_function *current_fcn = scope ? scope->function () : nullptr;
-
-        // Local function.
-
-        if (current_fcn)
-          {
-            std::string fcn_file = current_fcn->fcn_file_name ();
-
-            // For anonymous functions we look at the parent scope so that if
-            // they were defined within class methods and use local functions
-            // (helper functions) we can still use those anonymous functions
-
-            if (current_fcn->is_anonymous_function ())
-              {
-                if (fcn_file.empty ()
-                    && scope->parent_scope () != nullptr
-                    && scope->parent_scope ()->function () != nullptr)
-                  fcn_file
-                    = scope->parent_scope ()->function ()->fcn_file_name();
-              }
-
-            if (! fcn_file.empty ())
-              {
-                str_val_iterator r = local_functions.find (fcn_file);
-
-                if (r != local_functions.end ())
-                  {
-                    // We shouldn't need an out-of-date check here since
-                    // local functions may ultimately be called only from
-                    // a primary function or method defined in the same
-                    // file.
-
-                    return r->second;
-                  }
-              }
-          }
-
-        // Private function.
-
-        if (current_fcn)
-          {
-            std::string dir_name = current_fcn->dir_name ();
-
-            if (! dir_name.empty ())
-              {
-                str_val_iterator q = private_functions.find (dir_name);
-
-                if (q == private_functions.end ())
-                  {
-                    octave_value val = load_private_function (dir_name);
-
-                    if (val.is_defined ())
-                      return val;
-                  }
-                else
-                  {
-                    octave_value& fval = q->second;
-
-                    if (fval.is_defined ())
-                      out_of_date_check (fval, "", false);
-
-                    if (fval.is_defined ())
-                      return fval;
-                    else
-                      {
-                        octave_value val = load_private_function (dir_name);
-
-                        if (val.is_defined ())
-                          return val;
-                      }
-                  }
-              }
-          }
-      }
-
-    // Class methods.
-
-    if (! args.empty ())
-      {
-        std::string dispatch_type = get_dispatch_type (args);
-
-        octave_value fcn = find_method (dispatch_type);
-
-        if (fcn.is_defined ())
-          return fcn;
-      }
-
-    // Class constructors.  The class name and function name are the same.
-
-    str_val_iterator q = class_constructors.find (name);
-
-    if (q == class_constructors.end ())
-      {
-        octave_value val = load_class_constructor ();
-
-        if (val.is_defined ())
-          return val;
-      }
-    else
-      {
-        octave_value& fval = q->second;
-
-        if (fval.is_defined ())
-          out_of_date_check (fval, name);
-
-        if (fval.is_defined ())
-          return fval;
-        else
-          {
-            octave_value val = load_class_constructor ();
-
-            if (val.is_defined ())
-              return val;
-          }
-      }
-
-    // Command-line function.
-
-    if (cmdline_function.is_defined ())
-      return cmdline_function;
-
-    // Autoload?
-
-    octave_value fcn = find_autoload ();
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    // Function on the path.
-
-    fcn = find_user_function ();
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    // Package
-
-    fcn = find_package ();
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    // Built-in function (might be undefined).
-
-    return built_in_function;
-  }
-
-  // Find the definition of NAME according to the following precedence
-  // list:
-  //
-  //   built-in function
-  //   function on the path
-  //   autoload function
-  //   command-line function
-  //   private function
-  //   subfunction
-
-  // This function is used to implement the "builtin" function, which
-  // searches for "built-in" functions.  In Matlab, "builtin" only
-  // returns functions that are actually built-in to the interpreter.
-  // But since the list of built-in functions is different in Octave and
-  // Matlab, we also search up the precedence list until we find
-  // something that matches.  Note that we are only searching by name,
-  // so class methods and constructors are skipped.
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::builtin_find (void)
-  {
-    octave_value retval = x_builtin_find ();
-
-    if (! retval.is_defined ())
-      {
-        // It is possible that the user created a file on the fly since
-        // the last prompt or chdir, so try updating the load path and
-        // searching again.
-
-        load_path& lp = __get_load_path__ ("symbol_table::fcn_info::fcn_info_rep::builtin_find");
-
-        lp.update ();
-
-        retval = x_builtin_find ();
-      }
-
-    return retval;
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::x_builtin_find (void)
-  {
-    // Built-in function.
-    if (built_in_function.is_defined ())
-      return built_in_function;
-
-    // Function on the path.
-
-    octave_value fcn = find_user_function ();
-
-    if (fcn.is_defined ())
-      return fcn;
+    global_symbols_iterator q = m_global_symbols.begin ();
 
-    // Autoload?
-
-    fcn = find_autoload ();
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    // Command-line function.
-
-    if (cmdline_function.is_defined ())
-      return cmdline_function;
-
-    // Private function.
-
-    symbol_table::scope *scope
-      = __get_current_scope__ ("symbol_table::fcn_info::fcn_info_rep::x_builtin_find");
-
-    octave_user_function *current_fcn = scope ? scope->function () : nullptr;
-
-    if (current_fcn)
-      {
-        std::string dir_name = current_fcn->dir_name ();
-
-        if (! dir_name.empty ())
-          {
-            str_val_iterator q = private_functions.find (dir_name);
-
-            if (q == private_functions.end ())
-              {
-                octave_value val = load_private_function (dir_name);
-
-                if (val.is_defined ())
-                  return val;
-              }
-            else
-              {
-                octave_value& fval = q->second;
-
-                if (fval.is_defined ())
-                  out_of_date_check (fval);
-
-                if (fval.is_defined ())
-                  return fval;
-                else
-                  {
-                    octave_value val = load_private_function (dir_name);
-
-                    if (val.is_defined ())
-                      return val;
-                  }
-              }
-          }
-      }
-
-    // Local function.
-
-    if (current_fcn)
-      {
-        std::string fcn_file = current_fcn->fcn_file_name ();
-
-        if (! fcn_file.empty ())
-          {
-            str_val_iterator r = local_functions.find (fcn_file);
-
-            if (r != local_functions.end ())
-              {
-                // We shouldn't need an out-of-date check here since local
-                // functions may ultimately be called only from a primary
-                // function or method defined in the same file.
-
-                return r->second;
-              }
-          }
-      }
-
-    // Subfunction.  I think it only makes sense to check for
-    // subfunctions if we are currently executing a function defined
-    // from a .m file.
-
-    symbol_table::scope *curr_scope
-      = __get_current_scope__ ("symbol_table::fcn_info::fcn_info_rep::x_builtin_find");
-
-    if (curr_scope)
-      {
-        octave_value val = curr_scope->find_subfunction (name);
-
-        if (val.is_defined ())
-          return val;
-      }
-
-    return octave_value ();
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::find_method
-  (const std::string& dispatch_type)
-  {
-    octave_value retval;
-
-    str_val_iterator q = class_methods.find (dispatch_type);
-
-    if (q == class_methods.end ())
+    while (q != m_global_symbols.end ())
       {
-        octave_value val = load_class_method (dispatch_type);
-
-        if (val.is_defined ())
-          return val;
-      }
-    else
-      {
-        octave_value& fval = q->second;
-
-        if (fval.is_defined ())
-          out_of_date_check (fval, dispatch_type);
-
-        if (fval.is_defined ())
-          return fval;
+        if (pattern.match (q->first))
+          m_global_symbols.erase (q++);
         else
-          {
-            octave_value val = load_class_method (dispatch_type);
-
-            if (val.is_defined ())
-              return val;
-          }
-      }
-
-    return retval;
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::find_autoload (void)
-  {
-    // Autoloaded function.
-
-    if (autoload_function.is_defined ())
-      out_of_date_check (autoload_function);
-
-    if (! autoload_function.is_defined ())
-      {
-        std::string file_name = lookup_autoload (name);
-
-        if (! file_name.empty ())
-          {
-            size_t pos = file_name.find_last_of (sys::file_ops::dir_sep_chars ());
-
-            std::string dir_name = file_name.substr (0, pos);
-
-            octave_value ov_fcn
-              = load_fcn_from_file (file_name, dir_name, "", "",
-                                            name, true);
-
-            if (ov_fcn.is_defined ())
-              autoload_function = octave_value (ov_fcn);
-          }
+          q++;
       }
-
-    return autoload_function;
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::find_user_function (void)
-  {
-    // Function on the path.
-
-    if (function_on_path.is_defined ())
-      out_of_date_check (function_on_path);
-
-    if (function_on_path.is_undefined ())
-      {
-        std::string dir_name;
-
-        load_path& lp = __get_load_path__ ("symbol_table::fcn_info::fcn_info_rep::find_user_function");
-
-
-        std::string file_name = lp.find_fcn (name, dir_name, package_name);
-
-        if (! file_name.empty ())
-          {
-            octave_value ov_fcn
-              = load_fcn_from_file (file_name, dir_name, "",
-                                            package_name);
-
-            if (ov_fcn.is_defined ())
-              function_on_path = ov_fcn;
-          }
-      }
-
-    return function_on_path;
-  }
-
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::find_package (void)
-  {
-    // FIXME: implement correct way to check out of date package
-    //if (package.is_defined ())
-    //  out_of_date_check (package);
-
-    if (package.is_undefined ())
-      {
-        cdef_manager& cdm
-          = __get_cdef_manager__ ("symbol_table::fcn_info::fcn_info_rep::find_package");
-
-        octave_function *fcn = cdm.find_package_symbol (full_name ());
-
-        if (fcn)
-          package = octave_value (fcn);
-      }
-
-    return package;
   }
 
   // Insert INF_CLASS in the set of class names that are considered
@@ -1288,67 +361,6 @@
     return (q != inferior_classes.end ());
   }
 
-  void
-  symbol_table::fcn_info::fcn_info_rep::install_built_in_dispatch (const std::string& klass)
-  {
-    if (built_in_function.is_defined ())
-      {
-        octave_function *fcn = built_in_function.function_value ();
-
-        if (fcn)
-          {
-            if (fcn->handles_dispatch_class (klass))
-              warning ("install_built_in_dispatch: '%s' already defined for class '%s'",
-                       name.c_str (), klass.c_str ());
-            else
-              fcn->push_dispatch_class (klass);
-          }
-      }
-    else
-      error ("install_built_in_dispatch: '%s' is not a built-in function",
-             name.c_str ());
-  }
-}
-
-static octave_value
-dump_function_map (const std::map<std::string, octave_value>& fcn_map)
-{
-  if (fcn_map.empty ())
-    return octave_value (Matrix ());
-
-  std::map<std::string, octave_value> info_map;
-
-  for (const auto& nm_fcn : fcn_map)
-    {
-      std::string nm = nm_fcn.first;
-      const octave_value& fcn = nm_fcn.second;
-      info_map[nm] = fcn.dump ();
-    }
-
-  return octave_value (info_map);
-}
-
-namespace octave
-{
-  octave_value
-  symbol_table::fcn_info::fcn_info_rep::dump (void) const
-  {
-    std::map<std::string, octave_value> m
-      = {{ "name", full_name () },
-         { "refcount", count.value () },
-         { "package", package.dump () },
-         { "local_functions", dump_function_map (local_functions) },
-         { "private_functions", dump_function_map (private_functions) },
-         { "class_methods", dump_function_map (class_methods) },
-         { "class_constructors", dump_function_map (class_constructors) },
-         { "cmdline_function", cmdline_function.dump () },
-         { "autoload_function", autoload_function.dump () },
-         { "function_on_path", function_on_path.dump () },
-         { "built_in_function", built_in_function.dump () }};
-
-    return octave_value (m);
-  }
-
   octave_value
   symbol_table::find (const std::string& name, const octave_value_list& args,
                       bool skip_variables, bool local_funcs)
@@ -1361,8 +373,46 @@
   octave_value
   symbol_table::builtin_find (const std::string& name)
   {
-    return (m_current_scope
-            ? m_current_scope->builtin_find (name) : octave_value ());
+    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, bool local_funcs)
+  {
+    fcn_table_iterator p = m_fcn_table.find (name);
+
+    if (p != m_fcn_table.end ())
+      return p->second.find (args, local_funcs);
+    else
+      {
+        fcn_info finfo (name);
+
+        octave_value fcn = finfo.find (args, local_funcs);
+
+        if (fcn.is_defined ())
+          m_fcn_table[name] = finfo;
+
+        return fcn;
+      }
+
+    return octave_value ();
   }
 
   octave_value
@@ -1518,393 +568,6 @@
 
     return octave_value (info_map);
   }
-
-  octave_value
-  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
-      = __get_symbol_table__ ("symbol_table::scope::find");
-
-    if (! skip_variables)
-      {
-        table_iterator p = m_symbols.find (name);
-
-        if (p != m_symbols.end ())
-          {
-            symbol_record sr = p->second;
-
-            if (sr.is_global ())
-              return symtab.global_varval (name);
-            else
-              {
-                octave_value val = sr.varval ();
-
-                if (val.is_defined ())
-                  return val;
-              }
-          }
-      }
-
-    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 (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
-      {
-        fcn_info finfo (name);
-
-        octave_value fcn = finfo.find (args, local_funcs);
-
-        if (fcn.is_defined ())
-          symtab.m_fcn_table[name] = finfo;
-
-        return fcn;
-      }
-
-    return retval;
-  }
-
-  octave_value
-  symbol_table::scope::builtin_find (const std::string& name)
-  {
-    octave_value retval;
-
-    symbol_table& symtab
-      = __get_symbol_table__ ("symbol_table::scope::find");
-
-    fcn_table_iterator p = symtab.m_fcn_table.find (name);
-
-    if (p != symtab.m_fcn_table.end ())
-      return p->second.builtin_find ();
-    else
-      {
-        fcn_info finfo (name);
-
-        octave_value fcn = finfo.builtin_find ();
-
-        if (fcn.is_defined ())
-          symtab.m_fcn_table[name] = finfo;
-
-        return fcn;
-      }
-
-    return retval;
-  }
-
-  symbol_table::symbol_record&
-  symbol_table::scope::insert (const std::string& name, bool force_add)
-  {
-    table_iterator p = m_symbols.find (name);
-
-    if (p == m_symbols.end ())
-      {
-        symbol_table::symbol_record ret (this, name);
-
-        if (m_is_nested && m_parent && m_parent->look_nonlocal (name, ret))
-          return m_symbols[name] = ret;
-        else
-          {
-            if (m_is_static && ! force_add)
-              ret.mark_added_static ();
-
-            return m_symbols[name] = ret;
-          }
-      }
-    else
-      return p->second;
-  }
-
-  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
-      = __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
-      = __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::scope::workspace_info (void) const
-  {
-    std::list<workspace_element> retval;
-
-    for (const auto& nm_sr : m_symbols)
-      {
-        std::string nm = nm_sr.first;
-        symbol_record sr = nm_sr.second;
-
-        if (! sr.is_hidden ())
-          {
-            octave_value val = sr.varval ();
-
-            if (val.is_defined ())
-              {
-                // FIXME: fix size for objects, see kluge in variables.cc
-                //dim_vector dv = val.dims ();
-                octave_value tmp = val;
-                Matrix sz = tmp.size ();
-                dim_vector dv = dim_vector::alloc (sz.numel ());
-                for (octave_idx_type i = 0; i < dv.ndims (); i++)
-                  dv(i) = sz(i);
-
-                char storage = ' ';
-                if (sr.is_global ())
-                  storage = 'g';
-                else if (sr.is_persistent ())
-                  storage = 'p';
-                else if (sr.is_automatic ())
-                  storage = 'a';
-                else if (sr.is_formal ())
-                  storage = 'f';
-                else if (sr.is_hidden ())
-                  storage = 'h';
-                else if (sr.is_inherited ())
-                  storage = 'i';
-
-                std::ostringstream buf;
-                val.short_disp (buf);
-                std::string short_disp_str = buf.str ();
-
-                workspace_element elt (storage, nm, val.class_name (),
-                                       short_disp_str, dv.str (),
-                                       val.iscomplex ());
-
-                retval.push_back (elt);
-              }
-          }
-      }
-
-    return retval;
-  }
-
-  octave_value
-  symbol_table::scope::dump (void) const
-  {
-    std::map<std::string, octave_value> m
-      = {{ "name", m_name },
-         { "symbols", dump_symbols_map () },
-         { "persistent_variables", m_persistent_symbols },
-         { "subfunctions", dump_function_map (m_subfunctions) }};
-
-    return octave_value (m);
-  }
-
-  octave_value
-  symbol_table::scope::dump_symbols_map (void) const
-  {
-    std::map<std::string, octave_value> info_map;
-
-    for (const auto& nm_sr : m_symbols)
-      {
-        std::string nm = nm_sr.first;
-        const symbol_table::symbol_record& sr = nm_sr.second;
-        info_map[nm] = sr.dump ();
-      }
-
-    return octave_value (info_map);
-  }
-
-  void
-  symbol_table::scope::install_subfunction (const std::string& name,
-                                            const octave_value& fval,
-                                            bool is_nested)
-  {
-    m_subfunctions[name] = fval;
-
-    // This can be simpler once the scope object is stored in the function
-    // object...
-    octave_user_function *fcn = fval.user_function_value ();
-
-    scope *fcn_scope = fcn->scope ();
-
-    fcn_scope->set_parent (this);
-
-    if (is_nested)
-      {
-        m_children.push_back (fcn_scope);
-
-        fcn->mark_as_nested_function ();
-
-        fcn_scope->m_is_nested = true;
-      }
-
-  }
-
-  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::scope::mark_subfunctions_in_scope_as_private (const std::string& class_name)
-  {
-    for (auto& nm_sf : m_subfunctions)
-      {
-        octave_function *fcn = nm_sf.second.function_value ();
-
-        if (fcn)
-          fcn->mark_as_private_function (class_name);
-      }
-  }
-
-  void
-  symbol_table::scope::set_parent (scope *p)
-  {
-    m_parent = p;
-
-    if (m_parent)
-      {
-        // If m_parent is the top-level scope, there will be no parent
-        // function.
-
-        octave_function *current_fcn = function ();
-
-        if (current_fcn && current_fcn->is_anonymous_function ())
-          {
-            octave_function *parent_fcn = m_parent->function ();
-
-            if (parent_fcn)
-              m_parent_fcn = octave_value (parent_fcn, true);
-          }
-      }
-  }
-
-  void
-  symbol_table::scope::update_nest (void)
-  {
-    if (m_parent)
-      {
-        // fix bad symbol_records
-        for (auto& nm_sr : m_symbols)
-          {
-            symbol_record& ours = nm_sr.second;
-
-            if (! ours.is_formal ()
-                && m_is_nested && m_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");
-              }
-            else
-              ours.set_curr_fcn (m_fcn);
-          }
-
-        // The scopes of nested functions are static.
-        m_is_static = true;
-      }
-    else if (m_children.size ())
-      {
-        // Parents of nested functions have static scopes.
-        m_is_static = true;
-
-        for (auto& nm_sr : m_symbols)
-          nm_sr.second.set_curr_fcn (m_fcn);
-      }
-
-    for (auto& symtab_p : m_children)
-      symtab_p->update_nest ();
-  }
-
-  bool
-  symbol_table::scope::look_nonlocal (const std::string& name,
-                                      symbol_table::symbol_record& result)
-  {
-    table_iterator p = m_symbols.find (name);
-    if (p == m_symbols.end ())
-      {
-        if (m_is_nested && m_parent)
-          return m_parent->look_nonlocal (name, result);
-      }
-    else if (! p->second.is_automatic ())
-      {
-        result.bind_fwd_rep (p->second);
-        return true;
-      }
-
-    return false;
-  }
-
-  void
-  symbol_table::scope::bind_script_symbols (scope *curr_scope)
-  {
-    for (auto& nm_sr : m_symbols)
-      nm_sr.second.bind_fwd_rep (curr_scope->find_symbol (nm_sr.first));
-  }
-
-  void
-  symbol_table::scope::unbind_script_symbols (void)
-  {
-    for (auto& nm_sr : m_symbols)
-      nm_sr.second.unbind_fwd_rep ();
-  }
 }
 
 DEFUN (ignore_function_time_stamp, args, nargout,
@@ -1995,7 +658,7 @@
 {
   octave::symbol_table& symtab = interp.get_symbol_table ();
 
-  octave::symbol_table::scope *scope = symtab.current_scope ();
+  octave::scope *scope = symtab.current_scope ();
 
   std::string nm = scope ? scope->name () : "<unknown>";
 
@@ -2023,7 +686,7 @@
     {
       std::string fname = args(0).xstring_value ("__dump_symtab_info__: argument must be a function name");
 
-      octave::symbol_table::fcn_info *finfo = symtab.get_fcn_info (fname);
+      octave::fcn_info *finfo = symtab.get_fcn_info (fname);
 
       if (finfo)
         return finfo->dump ();
--- a/libinterp/corefcn/symtab.h	Thu Nov 16 16:12:12 2017 -0500
+++ b/libinterp/corefcn/symtab.h	Thu Nov 16 16:06:31 2017 -0500
@@ -40,8 +40,11 @@
 class tree_argument_list;
 class octave_user_function;
 
+#include "fcn-info.h"
 #include "ov.h"
 #include "ovl.h"
+#include "scope.h"
+#include "symrec.h"
 #include "workspace-element.h"
 
 namespace octave
@@ -50,1064 +53,11 @@
   {
   public:
 
-    static octave_value dummy_octave_value;
-
-    typedef size_t context_id;
-
-    class scope;
-
-    class fcn_info;
-
-    class symbol_record
-    {
-    public:
-
-      // 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;
-
-      // this symbol may NOT become a variable.
-      // (symbol added to a static workspace)
-      static const unsigned int added_static = 128;
-
-    private:
-
-      class symbol_record_rep
-      {
-      public:
-
-        symbol_record_rep (scope *s, const std::string& nm,
-                           const octave_value& v, unsigned int sc)
-          : m_decl_scope (s), curr_fcn (nullptr), name (nm),
-            m_fwd_rep (nullptr), value_stack (),
-            storage_class (sc), /* finfo (), */ valid (true), count (1)
-        {
-          value_stack.push_back (v);
-        }
-
-        // No copying!
-
-        symbol_record_rep (const symbol_record_rep& ov) = delete;
-
-        symbol_record_rep& operator = (const symbol_record_rep&) = delete;
-
-        ~symbol_record_rep (void) = default;
-
-        void assign (const octave_value& value)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->assign (value);
-              return;
-            }
-
-          varref () = value;
-        }
-
-        void assign (octave_value::assign_op op,
-                     const std::string& type,
-                     const std::list<octave_value_list>& idx,
-                     const octave_value& value)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->assign (op, type, idx, value);
-              return;
-            }
-
-          varref().assign (op, type, idx, value);
-        }
-
-        void assign (octave_value::assign_op op, const octave_value& value)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->assign (op, value);
-              return;
-            }
-
-          varref().assign (op, value);
-        }
-
-        void do_non_const_unary_op (octave_value::unary_op op)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->do_non_const_unary_op (op);
-              return;
-            }
-
-          varref().do_non_const_unary_op (op);
-        }
-
-        void do_non_const_unary_op (octave_value::unary_op op,
-                                    const std::string& type,
-                                    const std::list<octave_value_list>& idx)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->do_non_const_unary_op (op, type, idx);
-              return;
-            }
-
-          varref().do_non_const_unary_op (op, type, idx);
-        }
-
-        octave_value& varref (void)
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->varref ();
-
-          context_id context
-            = m_decl_scope ? m_decl_scope->current_context () : 0;
-
-          if (is_global ())
-            return xglobal_varref ();
-          else if (is_persistent ())
-            return xpersistent_varref ();
-          else
-            {
-              context_id n = value_stack.size ();
-              while (n++ <= context)
-                value_stack.push_back (octave_value ());
-
-              return value_stack[context];
-            }
-        }
-
-        octave_value varval (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->varval ();
-
-          context_id context
-            = m_decl_scope ? m_decl_scope->current_context () : 0;
-
-          if (is_global ())
-            return xglobal_varval ();
-          else if (is_persistent ())
-            return xpersistent_varval ();
-          else
-            {
-              if (context < value_stack.size ())
-                return value_stack[context];
-              else
-                return octave_value ();
-            }
-        }
-
-        void push_context (scope *sid)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->push_context (sid);
-              return;
-            }
-
-          if (! (is_persistent () || is_global ())
-              && sid == decl_scope ())
-            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.
-
-        size_t pop_context (scope *sid)
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->pop_context (sid);
-
-          size_t retval = 1;
-
-          if (! (is_persistent () || is_global ())
-              && sid == decl_scope ())
-            {
-              value_stack.pop_back ();
-              retval = value_stack.size ();
-            }
-
-          return retval;
-        }
-
-        void clear (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->clear ();
-              return;
-            }
-
-          clear (decl_scope ());
-        }
-
-        void clear (scope *sid);
-
-        bool is_defined (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_defined ();
-
-          return varval ().is_defined ();
-        }
-
-        bool is_valid (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_valid ();
-
-          return valid;
-        }
-
-        bool is_variable (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_variable ();
-
-          return (! is_local () || is_defined ());
-        }
-
-        bool is_local (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_local ();
-
-          return storage_class & local;
-        }
-
-        bool is_automatic (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_automatic ();
-
-          return storage_class & automatic;
-        }
-
-        bool is_formal (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_formal ();
-
-          return storage_class & formal;
-        }
-
-        bool is_hidden (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_hidden ();
-
-          return storage_class & hidden;
-        }
-
-        bool is_inherited (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_inherited ();
-
-          return storage_class & inherited;
-        }
-
-        bool is_global (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_global ();
-
-          return storage_class & global;
-        }
-
-        bool is_persistent (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_persistent ();
-
-          return storage_class & persistent;
-        }
-
-        bool is_added_static (void) const
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->is_added_static ();
-
-          return storage_class & added_static;
-        }
-
-        void mark_local (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_local ();
-              return;
-            }
-
-          storage_class |= local;
-        }
-
-        void mark_automatic (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_automatic ();
-              return;
-            }
-
-          storage_class |= automatic;
-        }
-
-        void mark_formal (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_formal ();
-              return;
-            }
-
-          storage_class |= formal;
-        }
-
-        void mark_hidden (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_hidden ();
-              return;
-            }
-
-          storage_class |= hidden;
-        }
-
-        void mark_inherited (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_inherited ();
-              return;
-            }
-
-          storage_class |= inherited;
-        }
-
-        void mark_global (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_global ();
-              return;
-            }
-
-          if (is_persistent ())
-            error ("can't make persistent variable %s global", name.c_str ());
-
-          storage_class |= global;
-        }
-
-        void mark_persistent (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_persistent ();
-              return;
-            }
-
-          if (is_global ())
-            error ("can't make global variable %s persistent", name.c_str ());
-
-          storage_class |= persistent;
-        }
-
-        void mark_added_static (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->mark_added_static ();
-              return;
-            }
-
-          storage_class |= added_static;
-        }
-
-        void unmark_local (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_local ();
-              return;
-            }
-
-          storage_class &= ~local;
-        }
-
-        void unmark_automatic (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_automatic ();
-              return;
-            }
-
-          storage_class &= ~automatic;
-        }
-
-        void unmark_formal (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_formal ();
-              return;
-            }
-
-          storage_class &= ~formal;
-        }
-
-        void unmark_hidden (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_hidden ();
-              return;
-            }
-
-          storage_class &= ~hidden;
-        }
-
-        void unmark_inherited (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_inherited ();
-              return;
-            }
-
-          storage_class &= ~inherited;
-        }
-
-        void unmark_global (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_global ();
-              return;
-            }
-
-          storage_class &= ~global;
-        }
-
-        void unmark_persistent (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_persistent ();
-              return;
-            }
-
-          storage_class &= ~persistent;
-        }
-
-        void unmark_added_static (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->unmark_added_static ();
-              return;
-            }
-
-          storage_class &= ~added_static;
-        }
-
-        void init_persistent (void);
-
-        void invalidate (void)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->invalidate ();
-              return;
-            }
-
-          valid = false;
-        }
-
-        void erase_persistent (void);
-
-        scope *decl_scope (void)
-        {
-          if (m_fwd_rep)
-            return m_fwd_rep->decl_scope ();
-
-          return m_decl_scope;
-        }
-
-        void set_curr_fcn (octave_user_function *fcn)
-        {
-          if (m_fwd_rep)
-            {
-              m_fwd_rep->set_curr_fcn (fcn);
-              return;
-            }
-
-          curr_fcn = fcn;
-        }
-
-        // We don't forward more than once, so no need to forward the
-        // next two.
+    typedef octave::symbol_record symbol_record;
+    typedef octave::scope scope;
+    typedef octave::fcn_info fcn_info;
 
-        void bind_fwd_rep (symbol_record_rep *fwd_rep) { m_fwd_rep = fwd_rep; }
-
-        void unbind_fwd_rep (void) { m_fwd_rep = nullptr; }
-
-        symbol_record_rep * dup (scope *new_scope) const;
-
-        octave_value dump (void) const;
-
-        scope *m_decl_scope;
-
-        octave_user_function *curr_fcn;
-
-        std::string name;
-
-        symbol_record_rep *m_fwd_rep;
-
-        std::deque<octave_value> value_stack;
-
-        unsigned int storage_class;
-
-        //      fcn_info *finfo;
-
-        bool valid;
-
-        refcount<size_t> count;
-
-      private:
-
-        octave_value& xglobal_varref (void);
-
-        octave_value& xpersistent_varref (void);
-
-        octave_value xglobal_varval (void) const;
-
-        octave_value xpersistent_varval (void) const;
-      };
-
-    public:
-
-      symbol_record (void);
-
-      symbol_record (scope *s, const std::string& nm = "",
-                     const octave_value& v = octave_value (),
-                     unsigned int sc = local)
-        : rep (new symbol_record_rep (s, nm, v, sc)) { }
-
-      symbol_record (const symbol_record& sr)
-        : rep (sr.rep)
-      {
-        rep->count++;
-      }
-
-      symbol_record& operator = (const symbol_record& sr)
-      {
-        if (this != &sr)
-          {
-            if (--rep->count == 0)
-              delete rep;
-
-            rep = sr.rep;
-            rep->count++;
-          }
-
-        return *this;
-      }
-
-      ~symbol_record (void)
-      {
-        if (--rep->count == 0)
-          delete rep;
-      }
-
-      symbol_record dup (scope *sid) const
-      {
-        return symbol_record (rep->dup (sid));
-      }
-
-      const std::string& name (void) const { return rep->name; }
-
-      void rename (const std::string& new_name) { rep->name = new_name; }
-
-      octave_value
-      find (const octave_value_list& args = octave_value_list ()) const;
-
-      void assign (const octave_value& value)
-      {
-        rep->assign (value);
-      }
-
-      void assign (octave_value::assign_op op,
-                   const std::string& type,
-                   const std::list<octave_value_list>& idx,
-                   const octave_value& value)
-      {
-        rep->assign (op, type, idx, value);
-      }
-
-      void assign (octave_value::assign_op op, const octave_value& value)
-      {
-        rep->assign (op, value);
-      }
-
-      void do_non_const_unary_op (octave_value::unary_op op)
-      {
-        rep->do_non_const_unary_op (op);
-      }
-
-      void do_non_const_unary_op (octave_value::unary_op op,
-                                  const std::string& type,
-                                  const std::list<octave_value_list>& idx)
-      {
-        rep->do_non_const_unary_op (op, type, idx);
-      }
-
-      // Delete when deprecated varref functions are removed.
-      octave_value& varref (void)
-      {
-        return rep->varref ();
-      }
-
-      octave_value varval (void) const
-      {
-        return rep->varval ();
-      }
-
-      void push_context (scope *sid) { rep->push_context (sid); }
-
-      size_t pop_context (scope *sid) { return rep->pop_context (sid); }
-
-      void clear (void) { rep->clear (); }
-
-      void clear (scope *sid) { rep->clear (sid); }
-
-      bool is_defined (void) const
-      {
-        return rep->is_defined ();
-      }
-
-      bool is_undefined (void) const
-      {
-        return ! rep->is_defined ();
-      }
-
-      bool is_valid (void) const
-      {
-        return rep->is_valid ();
-      }
-
-      bool is_variable (void) const
-      {
-        return rep->is_variable ();
-      }
-
-      bool is_local (void) const { return rep->is_local (); }
-      bool is_automatic (void) const { return rep->is_automatic (); }
-      bool is_formal (void) const { return rep->is_formal (); }
-      bool is_global (void) const { return rep->is_global (); }
-      bool is_hidden (void) const { return rep->is_hidden (); }
-      bool is_inherited (void) const { return rep->is_inherited (); }
-      bool is_persistent (void) const { return rep->is_persistent (); }
-      bool is_added_static (void) const { return rep->is_added_static (); }
-
-      void mark_local (void) { rep->mark_local (); }
-      void mark_automatic (void) { rep->mark_automatic (); }
-      void mark_formal (void) { rep->mark_formal (); }
-      void mark_hidden (void) { rep->mark_hidden (); }
-      void mark_inherited (void) { rep->mark_inherited (); }
-      void mark_global (void) { rep->mark_global (); }
-      void mark_persistent (void) { rep->mark_persistent (); }
-      void mark_added_static (void) { rep->mark_added_static (); }
-
-      void unmark_local (void) { rep->unmark_local (); }
-      void unmark_automatic (void) { rep->unmark_automatic (); }
-      void unmark_formal (void) { rep->unmark_formal (); }
-      void unmark_hidden (void) { rep->unmark_hidden (); }
-      void unmark_inherited (void) { rep->unmark_inherited (); }
-      void unmark_global (void) { rep->unmark_global (); }
-      void unmark_persistent (void) { rep->unmark_persistent (); }
-      void unmark_added_static (void) { rep->unmark_added_static (); }
-
-      void init_persistent (void) { rep->init_persistent (); }
-
-      void erase_persistent (void) { rep->erase_persistent (); }
-
-      void invalidate (void) { rep->invalidate (); }
-
-      scope *decl_scope (void) { return rep->decl_scope (); }
-
-      unsigned int xstorage_class (void) const { return rep->storage_class; }
-
-      void set_curr_fcn (octave_user_function *fcn)
-      {
-        rep->set_curr_fcn (fcn);
-      }
-
-      void bind_fwd_rep (const symbol_record& sr)
-      {
-        rep->bind_fwd_rep (sr.rep);
-      }
-
-      void unbind_fwd_rep (void) { rep->unbind_fwd_rep (); }
-
-      octave_value dump (void) const { return rep->dump (); }
-
-      const symbol_record_rep *xrep (void) const { return rep; }
-
-    private:
-
-      symbol_record_rep *rep;
-
-      symbol_record (symbol_record_rep *new_rep) : rep (new_rep) { }
-    };
-
-    class fcn_info
-    {
-    public:
-
-      typedef std::map<std::string, octave_value>::const_iterator
-      str_val_const_iterator;
-      typedef std::map<std::string, octave_value>::iterator str_val_iterator;
-
-    private:
-
-      class fcn_info_rep
-      {
-      public:
-
-        fcn_info_rep (const std::string& nm)
-          : name (nm), package_name (), local_functions (),
-            private_functions (), class_constructors (), class_methods (),
-            cmdline_function (), autoload_function (), function_on_path (),
-            built_in_function (), count (1)
-        {
-          size_t pos = name.rfind ('.');
-
-          if (pos != std::string::npos)
-            {
-              package_name = name.substr (0, pos);
-              name = name.substr (pos+1);
-            }
-        }
-
-        // No copying!
-
-        fcn_info_rep (const fcn_info_rep&) = delete;
-
-        fcn_info_rep& operator = (const fcn_info_rep&) = delete;
-
-        ~fcn_info_rep (void) = default;
-
-        octave_value install_local_function (const std::string& file_name);
-
-        octave_value load_private_function (const std::string& dir_name);
-
-        octave_value load_class_constructor (void);
-
-        octave_value load_class_method (const std::string& dispatch_type);
-
-        octave_value find (const octave_value_list& args, bool local_funcs);
-
-        octave_value builtin_find (void);
-
-        octave_value find_method (const std::string& dispatch_type);
-
-        octave_value find_autoload (void);
-
-        octave_value find_package (void);
-
-        octave_value find_user_function (void);
-
-        bool is_user_function_defined (void) const
-        {
-          return function_on_path.is_defined ();
-        }
-
-        octave_value find_function (const octave_value_list& args,
-                                    bool local_funcs)
-        {
-          return find (args, local_funcs);
-        }
-
-        void install_cmdline_function (const octave_value& f)
-        {
-          cmdline_function = f;
-        }
-
-        void install_local_function (const octave_value& f,
-                                     const std::string& file_name)
-        {
-          local_functions[file_name] = f;
-        }
-
-        void install_user_function (const octave_value& f)
-        {
-          function_on_path = f;
-        }
-
-        void install_built_in_function (const octave_value& f)
-        {
-          built_in_function = f;
-        }
-
-        void install_built_in_dispatch (const std::string& klass);
-
-        template <typename T>
-        void
-        clear_map (std::map<T, octave_value>& map, bool force = false)
-        {
-          typename std::map<T, octave_value>::iterator p = map.begin ();
-
-          while (p != map.end ())
-            {
-              if (force || ! p->second.islocked ())
-                map.erase (p++);
-              else
-                p++;
-            }
-        }
-
-        void clear_autoload_function (bool force = false)
-        {
-          if (force || ! autoload_function.islocked ())
-            autoload_function = octave_value ();
-        }
-
-        // We also clear command line functions here, as these are both
-        // "user defined"
-        void clear_user_function (bool force = false)
-        {
-          clear_autoload_function (force);
-
-          if (force || ! function_on_path.islocked ())
-            function_on_path = octave_value ();
-
-          if (force || ! cmdline_function.islocked ())
-            cmdline_function = octave_value ();
-        }
-
-        void clear_mex_function (void)
-        {
-          if (function_on_path.is_mex_function ())
-            clear_user_function ();
-        }
-
-        void clear_package (void)
-        {
-          package = octave_value ();
-        }
-
-        void clear (bool force = false)
-        {
-          clear_map (local_functions, force);
-          clear_map (private_functions, force);
-          clear_map (class_constructors, force);
-          clear_map (class_methods, force);
-
-          clear_autoload_function (force);
-          clear_user_function (force);
-          clear_package ();
-        }
-
-        octave_value dump (void) const;
-
-        std::string full_name (void) const
-        {
-          if (package_name.empty ())
-            return name;
-          else
-            return package_name + '.' + name;
-        }
-
-        std::string name;
-
-        std::string package_name;
-
-        // File name to function object.
-        std::map<std::string, octave_value> local_functions;
-
-        // Directory name to function object.
-        std::map<std::string, octave_value> private_functions;
-
-        // Class name to function object.
-        std::map<std::string, octave_value> class_constructors;
-
-        // Dispatch type to function object.
-        std::map<std::string, octave_value> class_methods;
-
-        octave_value cmdline_function;
-
-        octave_value autoload_function;
-
-        octave_value function_on_path;
-
-        octave_value package;
-
-        octave_value built_in_function;
-
-        refcount<size_t> count;
-
-      private:
-
-        octave_value xfind (const octave_value_list& args, bool local_funcs);
-
-        octave_value x_builtin_find (void);
-      };
-
-    public:
-
-      fcn_info (const std::string& nm = "")
-        : rep (new fcn_info_rep (nm)) { }
-
-      fcn_info (const fcn_info& fi) : rep (fi.rep)
-      {
-        rep->count++;
-      }
-
-      fcn_info& operator = (const fcn_info& fi)
-      {
-        if (this != &fi)
-          {
-            if (--rep->count == 0)
-              delete rep;
-
-            rep = fi.rep;
-            rep->count++;
-          }
-
-        return *this;
-      }
-
-      ~fcn_info (void)
-      {
-        if (--rep->count == 0)
-          delete rep;
-      }
-
-      octave_value find (const octave_value_list& args = octave_value_list (),
-                         bool local_funcs = true)
-      {
-        return rep->find (args, local_funcs);
-      }
-
-      octave_value builtin_find (void)
-      {
-        return rep->builtin_find ();
-      }
-
-      octave_value find_method (const std::string& dispatch_type) const
-      {
-        return rep->find_method (dispatch_type);
-      }
-
-      octave_value find_built_in_function (void) const
-      {
-        return rep->built_in_function;
-      }
-
-      octave_value find_cmdline_function (void) const
-      {
-        return rep->cmdline_function;
-      }
-
-      octave_value find_autoload (void)
-      {
-        return rep->find_autoload ();
-      }
-
-      octave_value find_user_function (void)
-      {
-        return rep->find_user_function ();
-      }
-
-      bool is_user_function_defined (void) const
-      {
-        return rep->is_user_function_defined ();
-      }
-
-      octave_value find_function (const octave_value_list& args
-                                  = octave_value_list (),
-                                  bool local_funcs = true)
-      {
-        return rep->find_function (args, local_funcs);
-      }
-
-      void install_cmdline_function (const octave_value& f)
-      {
-        rep->install_cmdline_function (f);
-      }
-
-      void install_local_function (const octave_value& f,
-                                   const std::string& file_name)
-      {
-        rep->install_local_function (f, file_name);
-      }
-
-      void install_user_function (const octave_value& f)
-      {
-        rep->install_user_function (f);
-      }
-
-      void install_built_in_function (const octave_value& f)
-      {
-        rep->install_built_in_function (f);
-      }
-
-      void install_built_in_dispatch (const std::string& klass)
-      {
-        rep->install_built_in_dispatch (klass);
-      }
-
-      void clear (bool force = false) { rep->clear (force); }
-
-      void clear_user_function (bool force = false)
-      {
-        rep->clear_user_function (force);
-      }
-
-      void clear_autoload_function (bool force = false)
-      {
-        rep->clear_autoload_function (force);
-      }
-
-      void clear_mex_function (void) { rep->clear_mex_function (); }
-
-      octave_value dump (void) const { return rep->dump (); }
-
-    private:
-
-      fcn_info_rep *rep;
-    };
+    typedef scope::context_id context_id;
 
     symbol_table (void)
       : m_global_symbols (), m_fcn_table (), m_class_precedence_table (),
@@ -1192,8 +142,6 @@
             bool skip_variables = false,
             bool local_funcs = true);
 
-    octave_value builtin_find (const std::string& name);
-
     void assign (const std::string& name, const octave_value& value, bool force_add)
     {
       if (m_current_scope)
@@ -1225,6 +173,14 @@
         p->second = value;
     }
 
+    octave_value& global_varref (const std::string& name)
+    {
+      global_symbols_iterator p = m_global_symbols.find (name);
+
+      return (p == m_global_symbols.end ()
+              ? m_global_symbols[name] : p->second);
+    }
+
     octave_value global_varval (const std::string& name) const
     {
       global_symbols_const_iterator p = m_global_symbols.find (name);
@@ -1303,6 +259,13 @@
               ? p->second.find_autoload () : octave_value ());
     }
 
+    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 (),
+                    bool local_funcs = true);
+
     octave_value
       find_function (const std::string& name,
                      const octave_value_list& args = octave_value_list (),
@@ -1512,6 +475,10 @@
         (p++)->second.clear_mex_function ();
     }
 
+    void erase_global (const std::string& name);
+
+    void erase_global_pattern (const glob_match& pattern);
+
     bool set_class_relationship (const std::string& sup_class,
                                  const std::string& inf_class);
 
@@ -1783,542 +750,6 @@
       return p != m_fcn_table.end () ? &p->second : nullptr;
     }
 
-    class scope
-    {
-    public:
-
-      typedef std::map<std::string, symbol_table::symbol_record>::const_iterator
-      table_const_iterator;
-      typedef std::map<std::string, symbol_table::symbol_record>::iterator
-      table_iterator;
-
-      typedef std::map<std::string, octave_value>::const_iterator
-      m_persistent_symbols_const_iterator;
-      typedef std::map<std::string, octave_value>::iterator
-      m_persistent_symbols_iterator;
-
-      typedef std::map<std::string, octave_value>::const_iterator
-      subfunctions_const_iterator;
-      typedef std::map<std::string, octave_value>::iterator subfunctions_iterator;
-
-      scope (const std::string& name = "")
-        : m_name (name), m_symbols (), m_persistent_symbols (), m_subfunctions (),
-          m_fcn (nullptr), m_parent (nullptr), m_parent_fcn (), m_children (), m_is_nested (false),
-          m_is_static (false), m_context (0)
-      { }
-
-      // No copying!
-
-      scope (const scope&) = delete;
-
-      scope& operator = (const scope&) = delete;
-
-      ~scope (void) = default;
-
-      void insert_symbol_record (const symbol_table::symbol_record& sr)
-      {
-        m_symbols[sr.name ()] = sr;
-      }
-
-      bool is_nested (void) const { return m_is_nested; }
-
-      void mark_nested (void) { m_is_nested = true; }
-
-      bool is_static (void) const { return m_is_static; }
-
-      void mark_static (void) { m_is_static = true; }
-
-      scope * parent_scope (void) const { return m_parent; }
-      octave_value parent_fcn (void) const { return m_parent_fcn; }
-
-      scope * dup (void) const
-      {
-        scope *new_sid = new scope ();
-
-        for (const auto& nm_sr : m_symbols)
-          new_sid->insert_symbol_record (nm_sr.second.dup (new_sid));
-
-        new_sid->m_parent = m_parent;
-        new_sid->m_parent_fcn = m_parent_fcn;
-
-        return new_sid;
-      }
-
-      void set_context (context_id context) { m_context = context; }
-
-      context_id current_context (void) const { return m_context; }
-
-      symbol_table::symbol_record find_symbol (const std::string& name)
-      {
-        table_iterator p = m_symbols.find (name);
-
-        if (p == m_symbols.end ())
-          return insert (name);
-        else
-          return p->second;
-      }
-
-      void inherit_internal (scope& donor_scope)
-      {
-        for (auto& nm_sr : m_symbols)
-          {
-            symbol_table::symbol_record& sr = nm_sr.second;
-
-            if (! (sr.is_automatic () || sr.is_formal ()))
-              {
-                std::string nm = sr.name ();
-
-                if (nm != "__retval__")
-                  {
-                    octave_value val = donor_scope.varval (nm);
-
-                    if (val.is_defined ())
-                      {
-                        sr.assign (val);
-
-                        sr.mark_inherited ();
-                      }
-                  }
-              }
-          }
-      }
-
-      void inherit (scope *donor_scope)
-      {
-        while (donor_scope)
-          {
-            inherit_internal (*donor_scope);
-
-            if (donor_scope->is_nested ())
-              donor_scope = donor_scope->parent_scope ();
-            else
-              break;
-          }
-      }
-
-
-      octave_value
-      find (const std::string& name, const octave_value_list& args,
-            bool skip_variables, bool local_funcs);
-
-      octave_value builtin_find (const std::string& name);
-
-      symbol_table::symbol_record&
-      insert (const std::string& name, bool force_add = false);
-
-      void rename (const std::string& old_name, const std::string& new_name)
-      {
-        table_iterator p = m_symbols.find (old_name);
-
-        if (p != m_symbols.end ())
-          {
-            symbol_table::symbol_record sr = p->second;
-
-            sr.rename (new_name);
-
-            m_symbols.erase (p);
-
-            m_symbols[new_name] = sr;
-          }
-      }
-
-      void assign (const std::string& name, const octave_value& value,
-                   bool force_add)
-      {
-        table_iterator p = m_symbols.find (name);
-
-        if (p == m_symbols.end ())
-          {
-            symbol_table::symbol_record& sr = insert (name, force_add);
-
-            sr.assign (value);
-          }
-        else
-          p->second.assign (value);
-      }
-
-      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)
-      {
-        table_iterator p = m_symbols.find (name);
-
-        if (p == m_symbols.end ())
-          {
-            symbol_table::symbol_record& sr = insert (name, true);
-
-            sr.assign (value);
-          }
-        else
-          p->second.assign (value);
-      }
-
-      // Use assign (name, value, force_add) instead.
-      // Delete when deprecated varref functions are removed.
-      octave_value& varref (const std::string& name, bool force_add)
-      {
-        table_iterator p = m_symbols.find (name);
-
-        if (p == m_symbols.end ())
-          {
-            symbol_table::symbol_record& sr = insert (name, force_add);
-
-            return sr.varref ();
-          }
-        else
-          return p->second.varref ();
-      }
-
-      octave_value varval (const std::string& name) const
-      {
-        table_const_iterator p = m_symbols.find (name);
-
-        return (p != m_symbols.end ()
-                ? p->second.varval () : octave_value ());
-      }
-
-      void persistent_assign (const std::string& name, const octave_value& value)
-      {
-        m_persistent_symbols_iterator p = m_persistent_symbols.find (name);
-
-        if (p == m_persistent_symbols.end ())
-          m_persistent_symbols[name] = value;
-        else
-          p->second = value;
-      }
-
-      // Use persistent_assign (name, value) instead.
-      // Delete when deprecated varref functions are removed.
-      octave_value& persistent_varref (const std::string& name)
-      {
-        m_persistent_symbols_iterator p = m_persistent_symbols.find (name);
-
-        return (p == m_persistent_symbols.end ()
-                ? m_persistent_symbols[name] : p->second);
-      }
-
-      octave_value persistent_varval (const std::string& name) const
-      {
-        m_persistent_symbols_const_iterator p = m_persistent_symbols.find (name);
-
-        return (p != m_persistent_symbols.end ()) ? p->second : octave_value ();
-      }
-
-      void erase_persistent (const std::string& name)
-      {
-        m_persistent_symbols_iterator p = m_persistent_symbols.find (name);
-
-        if (p != m_persistent_symbols.end ())
-          m_persistent_symbols.erase (p);
-      }
-
-      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_table::symbol_record& sr = p->second;
-
-            retval = sr.is_variable ();
-          }
-
-        return retval;
-      }
-
-      void push_context (void)
-      {
-        for (auto& nm_sr : m_symbols)
-          nm_sr.second.push_context (this);
-      }
-
-      void pop_context (void)
-      {
-        table_iterator tbl_it = m_symbols.begin ();
-
-        while (tbl_it != m_symbols.end ())
-          {
-            if (tbl_it->second.pop_context (this) == 0)
-              m_symbols.erase (tbl_it++);
-            else
-              tbl_it++;
-          }
-      }
-
-      void clear_variables (void)
-      {
-        for (auto& nm_sr : m_symbols)
-          nm_sr.second.clear (this);
-      }
-
-      void clear_objects (void)
-      {
-        for (auto& nm_sr : m_symbols)
-          {
-            symbol_table::symbol_record& sr = nm_sr.second;
-            octave_value val = sr.varval ();
-            if (val.isobject ())
-              nm_sr.second.clear (this);
-          }
-      }
-
-      void clear_global (const std::string& name);
-
-      void clear_variable (const std::string& name)
-      {
-        table_iterator p = m_symbols.find (name);
-
-        if (p != m_symbols.end ())
-          p->second.clear (this);
-      }
-
-      void clear_global_pattern (const std::string& pat);
-
-      void clear_variable_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_defined () || sr.is_global ())
-              {
-                if (pattern.match (sr.name ()))
-                  sr.clear (this);
-              }
-          }
-      }
-
-      void clear_variable_regexp (const std::string& pat)
-      {
-        octave::regexp pattern (pat);
-
-        for (auto& nm_sr : m_symbols)
-          {
-            symbol_table::symbol_record& sr = nm_sr.second;
-
-            if (sr.is_defined () || sr.is_global ())
-              {
-                if (pattern.is_match (sr.name ()))
-                  sr.clear (this);
-              }
-          }
-      }
-
-      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_table::symbol_record>
-      all_variables (bool defined_only = true,
-                     unsigned int exclude = symbol_table::symbol_record::hidden) const
-      {
-        std::list<symbol_table::symbol_record> retval;
-
-        for (const auto& nm_sr : m_symbols)
-          {
-            const symbol_table::symbol_record& sr = nm_sr.second;
-
-            if ((defined_only && ! sr.is_defined ())
-                || (sr.xstorage_class () & exclude))
-              continue;
-
-            retval.push_back (sr);
-          }
-
-        return retval;
-      }
-
-      std::list<symbol_table::symbol_record>
-      glob (const std::string& pattern, bool vars_only = false) const
-      {
-        std::list<symbol_table::symbol_record> retval;
-
-        glob_match pat (pattern);
-
-        for (const auto& nm_sr : m_symbols)
-          {
-            if (pat.match (nm_sr.first))
-              {
-                const symbol_table::symbol_record& sr = nm_sr.second;
-
-                if (vars_only && ! sr.is_variable ())
-                  continue;
-
-                retval.push_back (sr);
-              }
-          }
-
-        return retval;
-      }
-
-      std::list<symbol_table::symbol_record>
-      regexp (const std::string& pattern, bool vars_only = false) const
-      {
-        std::list<symbol_table::symbol_record> retval;
-
-        octave::regexp pat (pattern);
-
-        for (const auto& nm_sr : m_symbols)
-          {
-            if (pat.is_match (nm_sr.first))
-              {
-                const symbol_table::symbol_record& sr = nm_sr.second;
-
-                if (vars_only && ! sr.is_variable ())
-                  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 ())
-              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 ());
-      }
-
-      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,
-                                bool is_nested = false);
-
-      void install_nestfunction (const std::string& name,
-                                 const octave_value& fval)
-      {
-        install_subfunction (name, fval, true);
-      }
-
-      octave_value find_subfunction (const std::string& name) const;
-
-      void lock_subfunctions (void)
-      {
-        for (auto& nm_sf : m_subfunctions)
-          nm_sf.second.lock ();
-      }
-
-      void unlock_subfunctions (void)
-      {
-        for (auto& nm_sf : m_subfunctions)
-          nm_sf.second.unlock ();
-      }
-
-      std::map<std::string, octave_value> subfunctions (void)
-      {
-        return m_subfunctions;
-      }
-
-      void erase_subfunctions (void)
-      {
-        m_subfunctions.clear ();
-      }
-
-      void mark_subfunctions_in_scope_as_private (const std::string& class_name);
-
-      std::list<workspace_element> workspace_info (void) const;
-
-      octave_value dump (void) const;
-
-      std::string name (void) const { return m_name; }
-
-      void cache_name (const std::string& name) { m_name = name; }
-
-      octave_user_function *function (void) { return m_fcn; }
-
-      void set_function (octave_user_function *fcn) { m_fcn = fcn; }
-
-      void set_parent (scope *p);
-
-      void update_nest (void);
-
-      bool look_nonlocal (const std::string& name,
-                          symbol_table::symbol_record& result);
-
-      void bind_script_symbols (scope *curr_scope);
-
-      void unbind_script_symbols (void);
-
-    private:
-
-      // Name for this scope (usually the corresponding filename of the
-      // function corresponding to the scope).
-      std::string m_name;
-
-      // Map from symbol names to symbol info.
-      std::map<std::string, symbol_table::symbol_record> m_symbols;
-
-      // Map from names of persistent variables to values.
-      std::map<std::string, octave_value> m_persistent_symbols;
-
-      // Map from symbol names to subfunctions.
-      std::map<std::string, octave_value> m_subfunctions;
-
-      // The associated user code (may be null).
-      octave_user_function *m_fcn;
-
-      // Parent of nested function (may be null).
-      scope *m_parent;
-      octave_value m_parent_fcn;
-
-      // Child nested functions.
-      std::vector<scope*> m_children;
-
-      // If true, then this scope belongs to a nested function.
-      bool m_is_nested;
-
-      // If true then no variables can be added.
-      bool m_is_static;
-
-      context_id m_context;
-
-      octave_value dump_symbols_map (void) const;
-    };
-
   private:
 
     typedef std::map<std::string, octave_value>::const_iterator