changeset 28425:a5be4fc661d6 stable

hide specific stack frame and stack frame walker classes * stack-frame.h, stack-frame.cc (compiled_fcn_stack_frame, script_stack_frame, user_fcn_stack_frame, scope_stack_frame): Move class declarations from header to source file. (stack_frame::create): New static member functions that can create specific stack frame types and return pointers to generic stack_frame objects. (stack_frame::who): New function. * call-stack.cc (call_stack::push): Use new stack_frame::create methods instead of creating specific stack frame objects. (call_stack::do_who_two): Simply forward to stack_frame::who method. * stack-frame-walker.h, syminfo-accumulator.h: Delete. Move class declarations and definitions to stack-frame.cc. * libinterp/corefcn/module.mk: Update.
author John W. Eaton <jwe@octave.org>
date Fri, 24 Apr 2020 14:21:07 -0400
parents da4017484f19
children 9a3deb17b4ea
files libinterp/corefcn/call-stack.cc libinterp/corefcn/module.mk libinterp/corefcn/stack-frame-walker.h libinterp/corefcn/stack-frame.cc libinterp/corefcn/stack-frame.h libinterp/corefcn/syminfo-accumulator.h
diffstat 6 files changed, 878 insertions(+), 903 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/call-stack.cc	Tue Jun 09 14:10:41 2020 -0700
+++ b/libinterp/corefcn/call-stack.cc	Fri Apr 24 14:21:07 2020 -0400
@@ -44,9 +44,7 @@
 #include "pager.h"
 #include "parse.h"
 #include "stack-frame.h"
-#include "stack-frame-walker.h"
 #include "syminfo.h"
-#include "syminfo-accumulator.h"
 #include "symrec.h"
 #include "symscope.h"
 #include "variables.h"
@@ -378,7 +376,7 @@
     stack_frame *slink = get_static_link (prev_frame);
 
     stack_frame *new_frame
-      = new scope_stack_frame (m_evaluator, scope, m_curr_frame, slink);
+      = stack_frame::create (m_evaluator, scope, m_curr_frame, slink);
 
     m_cs.push_back (new_frame);
   }
@@ -396,8 +394,8 @@
     stack_frame *slink = get_static_link (prev_frame);
 
     stack_frame *new_frame
-      = new user_fcn_stack_frame (m_evaluator, fcn, up_frame, m_curr_frame,
-                                  slink, closure_frames);
+      = stack_frame::create (m_evaluator, fcn, up_frame, m_curr_frame,
+                             slink, closure_frames);
 
     m_cs.push_back (new_frame);
   }
@@ -414,8 +412,8 @@
     stack_frame *slink = get_static_link (prev_frame);
 
     stack_frame *new_frame
-      = new script_stack_frame (m_evaluator, script, up_frame, m_curr_frame,
-                                slink);
+      = stack_frame::create (m_evaluator, script, up_frame, m_curr_frame,
+                             slink);
 
     m_cs.push_back (new_frame);
   }
@@ -432,7 +430,7 @@
     stack_frame *slink = get_static_link (prev_frame);
 
     stack_frame *new_frame
-      = new compiled_fcn_stack_frame (m_evaluator, fcn, m_curr_frame, slink);
+      = stack_frame::create (m_evaluator, fcn, m_curr_frame, slink);
 
     m_cs.push_back (new_frame);
   }
@@ -976,39 +974,9 @@
                                        bool have_regexp, bool return_list,
                                        bool verbose, const std::string& msg)
   {
-    symbol_info_accumulator sym_inf_accum (patterns, have_regexp);
-
-    m_cs[m_curr_frame]->accept (sym_inf_accum);
-
-    if (return_list)
-      {
-        if (verbose)
-          return sym_inf_accum.map_value ();
-        else
-          return Cell (string_vector (sym_inf_accum.names ()));
-      }
-    else if (! sym_inf_accum.is_empty ())
-      {
-
-        if (msg.empty ())
-          octave_stdout << "Variables visible from the current scope:\n";
-        else
-          octave_stdout << msg;
-
-        if (verbose)
-          sym_inf_accum.display (octave_stdout,
-                                 m_evaluator.whos_line_format ());
-        else
-          {
-            octave_stdout << "\n";
-            string_vector names (sym_inf_accum.names ());
-            names.list_in_columns (octave_stdout);
-          }
-
-        octave_stdout << "\n";
-      }
-
-    return octave_value ();
+    return m_cs[m_curr_frame]->who (patterns, have_regexp, return_list,
+                                    verbose, m_evaluator.whos_line_format (),
+                                    msg);
   }
 
   octave_value call_stack::do_global_who_two (const string_vector& patterns,
--- a/libinterp/corefcn/module.mk	Tue Jun 09 14:10:41 2020 -0700
+++ b/libinterp/corefcn/module.mk	Fri Apr 24 14:21:07 2020 -0400
@@ -82,9 +82,7 @@
   %reldir%/sparse-xdiv.h \
   %reldir%/sparse-xpow.h \
   %reldir%/stack-frame.h \
-  %reldir%/stack-frame-walker.h \
   %reldir%/syminfo.h \
-  %reldir%/syminfo-accumulator.h \
   %reldir%/symrec.h \
   %reldir%/symscope.h \
   %reldir%/symtab.h \
--- a/libinterp/corefcn/stack-frame-walker.h	Tue Jun 09 14:10:41 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,68 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2019-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if ! defined (octave_stack_frame_walker_h)
-#define octave_stack_frame_walker_h 1
-
-#include "octave-config.h"
-
-namespace octave
-{
-  class compiled_fcn_stack_frame;
-  class script_stack_frame;
-  class user_fcn_stack_frame;
-  class scope_stack_frame;
-
-  class stack_frame_walker
-  {
-  protected:
-
-    stack_frame_walker (void) { }
-
-    virtual ~stack_frame_walker (void) = default;
-
-  public:
-
-    // No copying!
-
-    stack_frame_walker (const stack_frame_walker&) = delete;
-
-    stack_frame_walker& operator = (const stack_frame_walker&) = delete;
-
-    virtual void
-    visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0;
-
-    virtual void
-    visit_script_stack_frame (script_stack_frame&) = 0;
-
-    virtual void
-    visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0;
-
-    virtual void
-    visit_scope_stack_frame (scope_stack_frame&) = 0;
-  };
-}
-
-#endif
--- a/libinterp/corefcn/stack-frame.cc	Tue Jun 09 14:10:41 2020 -0700
+++ b/libinterp/corefcn/stack-frame.cc	Fri Apr 24 14:21:07 2020 -0400
@@ -42,15 +42,491 @@
 #include "parse.h"
 #include "pt-eval.h"
 #include "stack-frame.h"
-#include "stack-frame-walker.h"
 #include "syminfo.h"
-#include "syminfo-accumulator.h"
 #include "symrec.h"
 #include "symscope.h"
 #include "variables.h"
 
 namespace octave
 {
+  class compiled_fcn_stack_frame : public stack_frame
+  {
+  public:
+
+    compiled_fcn_stack_frame (void) = delete;
+
+    compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn,
+                              size_t index, stack_frame *static_link)
+      : stack_frame (tw, index, static_link, static_link->access_link ()),
+        m_fcn (fcn)
+    { }
+
+    compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default;
+
+    compiled_fcn_stack_frame&
+    operator = (const compiled_fcn_stack_frame& elt) = delete;
+
+    ~compiled_fcn_stack_frame (void) = default;
+
+    compiled_fcn_stack_frame * dup (void) const;
+
+    bool is_compiled_fcn_frame (void) const { return true; }
+
+    symbol_scope get_scope (void) const
+    {
+      return m_static_link->get_scope ();
+    }
+
+    octave_function * function (void) const { return m_fcn; }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      return m_static_link->lookup_symbol (name);
+    }
+
+    symbol_record insert_symbol (const std::string& name)
+    {
+      return m_static_link->insert_symbol (name);
+    }
+
+    stack_frame::scope_flags scope_flag (const symbol_record& sym) const
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->scope_flag (sym);
+    }
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_static_link->set_auto_fcn_var (avt, val);
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_static_link->get_auto_fcn_var (avt);
+    }
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using stack_frame::varval;
+    using stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->varval (sym);
+    }
+
+    octave_value& varref (const symbol_record& sym)
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      return m_static_link->varref (sym);
+    }
+
+    void mark_scope (const symbol_record& sym, scope_flags flag)
+    {
+      // Look in closest stack frame that contains values (either the
+      // top scope, or a user-defined function or script).
+
+      m_static_link->mark_scope (sym, flag);
+    }
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // Compiled function object associated with this stack frame.
+    // Should always be a built-in, .oct or .mex file function and
+    // should always be valid.
+    octave_function *m_fcn;
+  };
+
+  // Scripts have a symbol_scope object to store the set of variables
+  // in the script, but values for those variables are stored in the
+  // stack frame corresponding to the nearest calling function or in
+  // the top-level scope (the evaluation stack frame).
+  //
+  // Accessing values in a scope requires a mapping from the index of
+  // the variable for the script scope to the list of values in the
+  // evaluation frame(s).  The frame offset tells us how many access
+  // links we must follow to find the stack frame that holds the
+  // value.  The value offset is the index into the vector of values
+  // in that stack frame that we should use to find the value.
+  //
+  // Frame and value offsets are set in this stack frame when it is
+  // created using information from the script and enclosing scopes.
+  //
+  // If a script is invoked in a nested function context, the frame
+  // offsets for individual values may be different.  Some may be
+  // accessed from the invoking function and some may come from a
+  // parent function.
+
+  class script_stack_frame : public stack_frame
+  {
+  public:
+
+    script_stack_frame (void) = delete;
+
+    script_stack_frame (tree_evaluator& tw, octave_user_script *script,
+                        unwind_protect *up_frame, size_t index,
+                        stack_frame *static_link);
+
+    script_stack_frame (const script_stack_frame& elt) = default;
+
+    script_stack_frame& operator = (const script_stack_frame& elt) = delete;
+
+    ~script_stack_frame (void) = default;
+
+    script_stack_frame * dup (void) const;
+
+    bool is_user_script_frame (void) const { return true; }
+
+    static stack_frame * get_access_link (stack_frame *static_link);
+
+    static size_t get_num_symbols (octave_user_script *script);
+
+    void set_script_offsets (void);
+
+    void set_script_offsets_internal (const std::map<std::string,
+                                                     symbol_record>& symbols);
+
+    void resize_and_update_script_offsets (const symbol_record& sym);
+
+    symbol_scope get_scope (void) const { return m_script->scope (); }
+
+    octave_function * function (void) const { return m_script; }
+
+    unwind_protect *
+    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
+
+    symbol_record lookup_symbol (const std::string& name) const;
+
+    symbol_record insert_symbol (const std::string&);
+
+    size_t size (void) const { return m_lexical_frame_offsets.size (); }
+
+    void resize (size_t size)
+    {
+      m_lexical_frame_offsets.resize (size, 0);
+      m_value_offsets.resize (size, 0);
+    }
+
+    void get_val_offsets_with_insert (const symbol_record& sym,
+                                      size_t& frame_offset,
+                                      size_t& data_offset);
+
+    bool get_val_offsets_internal (const symbol_record& sym,
+                                   size_t& frame_offset,
+                                   size_t& data_offset) const;
+
+    bool get_val_offsets (const symbol_record& sym, size_t& frame_offset,
+                          size_t& data_offset) const;
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_access_link->set_auto_fcn_var (avt, val);
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_access_link->get_auto_fcn_var (avt);
+    }
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using stack_frame::varval;
+    using stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // Script object associated with this stack frame.  Should always
+    // be valid.
+    octave_user_script *m_script;
+
+    // The nearest unwind protect frame that was active when this
+    // stack frame was created.  Should always be valid.
+    unwind_protect *m_unwind_protect_frame;
+
+    // Mapping between the symbols in the symbol_scope object of the
+    // script to the stack frame in which the script is executed.  The
+    // frame offsets may be greater than one if the script is executed
+    // in a nested function context.
+
+    std::vector<size_t> m_lexical_frame_offsets;
+    std::vector<size_t> m_value_offsets;
+  };
+
+  // Base class for values and offsets shared by user_fcn and scope
+  // frames.
+
+  class base_value_stack_frame : public stack_frame
+  {
+  public:
+
+    base_value_stack_frame (void) = delete;
+
+    base_value_stack_frame (tree_evaluator& tw, size_t num_symbols,
+                            size_t index, stack_frame *static_link,
+                            stack_frame *access_link)
+      : stack_frame (tw, index, static_link, access_link),
+        m_values (num_symbols, octave_value ()),
+        m_flags (num_symbols, LOCAL),
+        m_auto_vars (NUM_AUTO_VARS, octave_value ())
+    { }
+
+    base_value_stack_frame (const base_value_stack_frame& elt) = default;
+
+    base_value_stack_frame&
+    operator = (const base_value_stack_frame& elt) = delete;
+
+    ~base_value_stack_frame (void) = default;
+
+    size_t size (void) const
+    {
+      return m_values.size ();
+    }
+
+    void resize (size_t size)
+    {
+      m_values.resize (size, octave_value ());
+      m_flags.resize (size, LOCAL);
+    }
+
+    stack_frame::scope_flags get_scope_flag (size_t data_offset) const
+    {
+      return m_flags.at (data_offset);
+    }
+
+    void set_scope_flag (size_t data_offset, scope_flags flag)
+    {
+      m_flags.at (data_offset) = flag;
+    }
+
+    octave_value get_auto_fcn_var (auto_var_type avt) const
+    {
+      return m_auto_vars.at (avt);
+    }
+
+    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
+    {
+      m_auto_vars.at (avt) = val;
+    }
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using stack_frame::varval;
+    using stack_frame::varref;
+
+    octave_value varval (size_t data_offset) const
+    {
+      return m_values.at (data_offset);
+    }
+
+    octave_value& varref (size_t data_offset)
+    {
+      return m_values.at (data_offset);
+    }
+
+    void display (bool follow = true) const;
+
+  protected:
+
+    // Variable values.  This array is indexed by the data_offset
+    // value stored in the symbol_record objects of the scope
+    // associated with this stack frame.
+    std::vector<octave_value> m_values;
+
+    // The type of each variable (local, global, persistent) of each
+    // value.  This array is indexed by the data_offset value stored
+    // in the symbol_record objects of the scope associated with this
+    // stack frame.  Local values are found in the M_VALUES array.
+    // Global values are stored in the tree_evaluator object that contains
+    // the stack frame.  Persistent values are stored in the function
+    // scope corresponding to the stack frame.
+    std::vector<scope_flags> m_flags;
+
+    // A fixed list of Automatic variables created for this function.
+    // The elements of this vector correspond to the auto_var_type
+    // enum.
+    std::vector<octave_value> m_auto_vars;
+  };
+
+  // User-defined functions have a symbol_scope object to store the set
+  // of variables in the function and values are stored in the stack
+  // frame corresponding to the invocation of the function or one of
+  // its parents.  The frame offset tells us how many access links we
+  // must follow to find the stack frame that holds the value.  The
+  // value offset is the index into the vector of values in that stack
+  // frame that we should use to find the value.
+  //
+  // Frame and value offsets are determined when the corresponding
+  // function is parsed.
+
+  class user_fcn_stack_frame : public base_value_stack_frame
+  {
+  public:
+
+    user_fcn_stack_frame (void) = delete;
+
+    user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
+                          unwind_protect *up_frame, size_t index,
+                          stack_frame *static_link,
+                          stack_frame *access_link = nullptr)
+      : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
+                                (access_link
+                                 ? access_link
+                                 : get_access_link (fcn, static_link))),
+        m_fcn (fcn), m_unwind_protect_frame (up_frame)
+    { }
+
+    user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default;
+
+    user_fcn_stack_frame&
+    operator = (const user_fcn_stack_frame& elt) = delete;
+
+    ~user_fcn_stack_frame (void) = default;
+
+    user_fcn_stack_frame * dup (void) const;
+
+    bool is_user_fcn_frame (void) const { return true; }
+
+    static stack_frame *
+    get_access_link (octave_user_function *fcn, stack_frame *static_link);
+
+    static size_t get_num_symbols (octave_user_function *fcn)
+    {
+      symbol_scope fcn_scope = fcn->scope ();
+
+      return fcn_scope.num_symbols ();
+    }
+
+    void clear_values (void);
+
+    symbol_scope get_scope (void) const { return m_fcn->scope (); }
+
+    octave_function * function (void) const { return m_fcn; }
+
+    unwind_protect *
+    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
+
+    symbol_record lookup_symbol (const std::string& name) const;
+
+    symbol_record insert_symbol (const std::string&);
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using base_value_stack_frame::varval;
+    using base_value_stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // User-defined object associated with this stack frame.  Should
+    // always be valid.
+    octave_user_function *m_fcn;
+
+    // The nearest unwind protect frame that was active when this
+    // stack frame was created.  Should always be valid.
+    unwind_protect *m_unwind_protect_frame;
+  };
+
+  // Pure scope stack frames (primarily the top-level workspace) have
+  // a set of variables and values are stored in the stack frame.  All
+  // variable accesses are direct as there are no parent stack frames.
+  //
+  // Value offsets are determined when the corresponding variable is
+  // entered into the symbol_scope object corresponding to the frame.
+
+  class scope_stack_frame : public base_value_stack_frame
+  {
+  public:
+
+    scope_stack_frame (void) = delete;
+
+    scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope,
+                       size_t index, stack_frame *static_link)
+      : base_value_stack_frame (tw, scope.num_symbols (), index,
+                                static_link, nullptr),
+        m_scope (scope)
+    { }
+
+    scope_stack_frame (const scope_stack_frame& elt) = default;
+
+    scope_stack_frame& operator = (const scope_stack_frame& elt) = delete;
+
+    ~scope_stack_frame (void) = default;
+
+    scope_stack_frame * dup (void) const;
+
+    bool is_scope_frame (void) const { return true; }
+
+    symbol_scope get_scope (void) const { return m_scope; }
+
+    symbol_record lookup_symbol (const std::string& name) const
+    {
+      return m_scope.lookup_symbol (name);
+    }
+
+    symbol_record insert_symbol (const std::string&);
+
+    scope_flags scope_flag (const symbol_record& sym) const;
+
+    // We only need to override one of each of these functions.  The
+    // using declaration will avoid warnings about partially-overloaded
+    // virtual functions.
+    using base_value_stack_frame::varval;
+    using base_value_stack_frame::varref;
+
+    octave_value varval (const symbol_record& sym) const;
+
+    octave_value& varref (const symbol_record& sym);
+
+    void mark_scope (const symbol_record& sym, scope_flags flag);
+
+    void display (bool follow = true) const;
+
+    void accept (stack_frame_walker& sfw);
+
+  private:
+
+    // The scope object associated with this stack frame.
+    symbol_scope m_scope;
+  };
+
   // FIXME: There should probably be a display method for the script,
   // fcn, and scope objects and the script and function objects should
   // be responsible for displaying the scopes they contain.
@@ -78,6 +554,35 @@
       }
   }
 
+  class stack_frame_walker
+  {
+  protected:
+
+    stack_frame_walker (void) { }
+
+    virtual ~stack_frame_walker (void) = default;
+
+  public:
+
+    // No copying!
+
+    stack_frame_walker (const stack_frame_walker&) = delete;
+
+    stack_frame_walker& operator = (const stack_frame_walker&) = delete;
+
+    virtual void
+    visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame&) = 0;
+
+    virtual void
+    visit_script_stack_frame (script_stack_frame&) = 0;
+
+    virtual void
+    visit_user_fcn_stack_frame (user_fcn_stack_frame&) = 0;
+
+    virtual void
+    visit_scope_stack_frame (scope_stack_frame&) = 0;
+  };
+
   class symbol_cleaner : public stack_frame_walker
   {
   public:
@@ -237,6 +742,302 @@
     std::set<std::string> m_cleared_names;
   };
 
+  class symbol_info_accumulator : public stack_frame_walker
+  {
+  public:
+
+    symbol_info_accumulator (const std::string& pattern,
+                             bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (pattern), m_match_all (false),
+        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
+        m_found_names ()
+    { }
+
+    symbol_info_accumulator (const string_vector& patterns,
+                             bool have_regexp = false)
+      : stack_frame_walker (), m_patterns (patterns), m_match_all (false),
+        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
+        m_found_names ()
+    { }
+
+    symbol_info_accumulator (bool match_all = true, bool first_only = true)
+      : stack_frame_walker (), m_patterns (), m_match_all (match_all),
+        m_first_only (first_only), m_have_regexp (false),
+        m_sym_inf_list (), m_found_names ()
+    { }
+
+    symbol_info_accumulator (const symbol_info_accumulator&) = delete;
+
+    symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete;
+
+    ~symbol_info_accumulator (void) = default;
+
+    bool is_empty  (void) const
+    {
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          if (! lst.empty ())
+            return false;
+        }
+
+      return true;
+    }
+
+    std::list<std::string> names (void) const
+    {
+      std::list<std::string> retval;
+
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          std::list<std::string> nm_list = lst.names ();
+
+          for (const auto& nm : nm_list)
+            retval.push_back (nm);
+        }
+
+      return retval;
+    }
+
+    symbol_info_list symbol_info (void) const
+    {
+      symbol_info_list retval;
+
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          const symbol_info_list& lst = nm_sil.second;
+
+          for (const auto& syminf : lst)
+            retval.push_back (syminf);
+        }
+
+      return retval;
+    }
+
+    octave_map map_value (void) const
+    {
+      octave_map retval;
+
+      // FIXME: is there a better way to concatenate structures?
+
+      size_t n_frames = m_sym_inf_list.size ();
+
+      OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames);
+
+      size_t j = 0;
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          std::string scope_name = nm_sil.first;
+          const symbol_info_list& lst = nm_sil.second;
+
+          map_list[j] = lst.map_value (scope_name, n_frames-j);
+
+          j++;
+        }
+
+      return octave_map::cat (-1, n_frames, map_list);
+    }
+
+    void display (std::ostream& os, const std::string& format) const
+    {
+      for (const auto& nm_sil : m_sym_inf_list)
+        {
+          os << "\nvariables in scope: " << nm_sil.first << "\n\n";
+
+          const symbol_info_list& lst = nm_sil.second;
+
+          lst.display (os, format);
+        }
+    }
+
+    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
+    {
+      // This one follows static link always.  Hmm, should the access
+      // link for a compiled_fcn_stack_frame be the same as the static
+      // link?
+
+      stack_frame *slink = frame.static_link ();
+
+      if (slink)
+        slink->accept (*this);
+    }
+
+    void visit_script_stack_frame (script_stack_frame& frame)
+    {
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
+    {
+      append_list (frame);
+
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+    void visit_scope_stack_frame (scope_stack_frame& frame)
+    {
+      append_list (frame);
+
+      stack_frame *alink = frame.access_link ();
+
+      if (alink)
+        alink->accept (*this);
+    }
+
+  private:
+
+    typedef std::pair<std::string, symbol_info_list> syminf_list_elt;
+
+    // FIXME: the following is too complex and duplicates too much
+    // code.  Maybe it should be split up so we have separate classes
+    // that do each job that is needed?
+
+    std::list<symbol_record>
+    filter (stack_frame& frame, const std::list<symbol_record>& symbols)
+    {
+      std::list<symbol_record> new_symbols;
+
+      if (m_match_all)
+        {
+          for (const auto& sym : symbols)
+            {
+              if (frame.is_defined (sym))
+                {
+                  std::string name = sym.name ();
+
+                  if (m_first_only
+                      && m_found_names.find (name) != m_found_names.end ())
+                    continue;
+
+                  m_found_names.insert (name);
+
+                  new_symbols.push_back (sym);
+                }
+            }
+        }
+      else if (m_have_regexp)
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              regexp pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  std::string name = sym.name ();
+
+                  if (pat.is_match (name) && frame.is_defined (sym))
+                    {
+                      if (m_first_only
+                          && m_found_names.find (name) != m_found_names.end ())
+                        continue;
+
+                      m_found_names.insert (name);
+
+                      new_symbols.push_back (sym);
+                    }
+                }
+            }
+        }
+      else
+        {
+          octave_idx_type npatterns = m_patterns.numel ();
+
+          for (octave_idx_type j = 0; j < npatterns; j++)
+            {
+              std::string pattern = m_patterns[j];
+
+              glob_match pat (pattern);
+
+              for (const auto& sym : symbols)
+                {
+                  std::string name = sym.name ();
+
+                  if (pat.match (name) && frame.is_defined (sym))
+                    {
+                      if (m_first_only
+                          && m_found_names.find (name) == m_found_names.end ())
+                        continue;
+
+                      m_found_names.insert (name);
+
+                      new_symbols.push_back (sym);
+                    }
+                }
+            }
+        }
+
+      return new_symbols;
+    }
+
+    void append_list (stack_frame& frame)
+    {
+      symbol_scope scope = frame.get_scope ();
+
+      std::list<symbol_record> symbols = scope.symbol_list ();
+
+      if (m_match_all || ! m_patterns.empty ())
+        symbols = filter (frame, symbols);
+
+      symbol_info_list syminf_list = frame.make_symbol_info_list (symbols);
+
+      m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list));
+    }
+
+    string_vector m_patterns;
+
+    bool m_match_all;
+    bool m_first_only;
+    bool m_have_regexp;
+
+    std::list<std::pair<std::string, symbol_info_list>> m_sym_inf_list;
+
+    std::set<std::string> m_found_names;
+  };
+
+  stack_frame * stack_frame::create (tree_evaluator& tw, octave_function *fcn,
+                                     size_t index, stack_frame *static_link)
+  {
+    return new compiled_fcn_stack_frame (tw, fcn, index, static_link);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     octave_user_script *script,
+                                     unwind_protect *up_frame, size_t index,
+                                     stack_frame *static_link)
+  {
+    return new script_stack_frame (tw, script, up_frame, index, static_link);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     octave_user_function *fcn,
+                                     unwind_protect *up_frame, size_t index,
+                                     stack_frame *static_link,
+                                     stack_frame *access_link)
+  {
+    return new user_fcn_stack_frame (tw, fcn, up_frame, index, static_link,
+                                     access_link);
+  }
+
+  stack_frame * stack_frame::create (tree_evaluator& tw,
+                                     const symbol_scope& scope, size_t index,
+                                     stack_frame *static_link)
+  {
+    return new scope_stack_frame (tw, scope, index, static_link);
+  }
+
   // This function is only implemented for user_fcn stack frames and
   // only called for those objects using unwind_protect and the
   // call_stack::clear_current_frame_values function.  Anything else
@@ -268,6 +1069,45 @@
     return symbol_stats;
   }
 
+  octave_value stack_frame::who (const string_vector& patterns,
+                                 bool have_regexp, bool return_list,
+                                 bool verbose, const std::string& whos_line_fmt,
+                                 const std::string& msg)
+  {
+    symbol_info_accumulator sym_inf_accum (patterns, have_regexp);
+
+    accept (sym_inf_accum);
+
+    if (return_list)
+      {
+        if (verbose)
+          return sym_inf_accum.map_value ();
+        else
+          return Cell (string_vector (sym_inf_accum.names ()));
+      }
+    else if (! sym_inf_accum.is_empty ())
+      {
+
+        if (msg.empty ())
+          octave_stdout << "Variables visible from the current scope:\n";
+        else
+          octave_stdout << msg;
+
+        if (verbose)
+          sym_inf_accum.display (octave_stdout, whos_line_fmt);
+        else
+          {
+            octave_stdout << "\n";
+            string_vector names (sym_inf_accum.names ());
+            names.list_in_columns (octave_stdout);
+          }
+
+        octave_stdout << "\n";
+      }
+
+    return octave_value ();
+  }
+
   // Return first occurrence of variables in current stack frame and any
   // parent frames reachable through access links.
 
--- a/libinterp/corefcn/stack-frame.h	Tue Jun 09 14:10:41 2020 -0700
+++ b/libinterp/corefcn/stack-frame.h	Fri Apr 24 14:21:07 2020 -0400
@@ -104,11 +104,6 @@
   class symbol_info_list;
   class unwind_protect;
 
-  class compiled_fcn_stack_frame;
-  class script_stack_frame;
-  class user_fcn_stack_frame;
-  class scope_stack_frame;
-
   class stack_frame_walker;
 
   class stack_frame
@@ -150,6 +145,28 @@
         m_dispatch_class ()
     { }
 
+    // Compiled function.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_function *fcn, size_t index,
+            stack_frame *static_link);
+
+    // Script.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_user_script *script,
+            unwind_protect *up_frame, size_t index,
+            stack_frame *static_link);
+
+    // User-defined function.
+    static stack_frame *
+    create (tree_evaluator& tw, octave_user_function *fcn,
+            unwind_protect *up_frame, size_t index,
+            stack_frame *static_link, stack_frame *access_link = nullptr);
+
+    // Scope.
+    static stack_frame *
+    create (tree_evaluator& tw, const symbol_scope& scope, size_t index,
+            stack_frame *static_link);
+
     stack_frame (const stack_frame& elt) = default;
 
     stack_frame& operator = (const stack_frame& elt) = delete;
@@ -218,6 +235,11 @@
     symbol_info_list
     make_symbol_info_list (const std::list<symbol_record>& symrec_list) const;
 
+    octave_value who (const string_vector& patterns, bool have_regexp,
+                      bool return_list, bool verbose,
+                      const std::string& whos_line_fmt,
+                      const std::string& msg);
+
     symbol_info_list all_variables (void);
 
     std::list<std::string> variable_names (void) const;
@@ -545,484 +567,6 @@
     // in the call stack.
     std::string m_dispatch_class;
   };
-
-  class compiled_fcn_stack_frame : public stack_frame
-  {
-  public:
-
-    compiled_fcn_stack_frame (void) = delete;
-
-    compiled_fcn_stack_frame (tree_evaluator& tw, octave_function *fcn,
-                              size_t index, stack_frame *static_link)
-      : stack_frame (tw, index, static_link, static_link->access_link ()),
-        m_fcn (fcn)
-    { }
-
-    compiled_fcn_stack_frame (const compiled_fcn_stack_frame& elt) = default;
-
-    compiled_fcn_stack_frame&
-    operator = (const compiled_fcn_stack_frame& elt) = delete;
-
-    ~compiled_fcn_stack_frame (void) = default;
-
-    compiled_fcn_stack_frame * dup (void) const;
-
-    bool is_compiled_fcn_frame (void) const { return true; }
-
-    symbol_scope get_scope (void) const
-    {
-      return m_static_link->get_scope ();
-    }
-
-    octave_function * function (void) const { return m_fcn; }
-
-    symbol_record lookup_symbol (const std::string& name) const
-    {
-      return m_static_link->lookup_symbol (name);
-    }
-
-    symbol_record insert_symbol (const std::string& name)
-    {
-      return m_static_link->insert_symbol (name);
-    }
-
-    stack_frame::scope_flags scope_flag (const symbol_record& sym) const
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      return m_static_link->scope_flag (sym);
-    }
-
-    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
-    {
-      m_static_link->set_auto_fcn_var (avt, val);
-    }
-
-    octave_value get_auto_fcn_var (auto_var_type avt) const
-    {
-      return m_static_link->get_auto_fcn_var (avt);
-    }
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using stack_frame::varval;
-    using stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      return m_static_link->varval (sym);
-    }
-
-    octave_value& varref (const symbol_record& sym)
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      return m_static_link->varref (sym);
-    }
-
-    void mark_scope (const symbol_record& sym, scope_flags flag)
-    {
-      // Look in closest stack frame that contains values (either the
-      // top scope, or a user-defined function or script).
-
-      m_static_link->mark_scope (sym, flag);
-    }
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // Compiled function object associated with this stack frame.
-    // Should always be a built-in, .oct or .mex file function and
-    // should always be valid.
-    octave_function *m_fcn;
-  };
-
-  // Scripts have a symbol_scope object to store the set of variables
-  // in the script, but values for those variables are stored in the
-  // stack frame corresponding to the nearest calling function or in
-  // the top-level scope (the evaluation stack frame).
-  //
-  // Accessing values in a scope requires a mapping from the index of
-  // the variable for the script scope to the list of values in the
-  // evaluation frame(s).  The frame offset tells us how many access
-  // links we must follow to find the stack frame that holds the
-  // value.  The value offset is the index into the vector of values
-  // in that stack frame that we should use to find the value.
-  //
-  // Frame and value offsets are set in this stack frame when it is
-  // created using information from the script and enclosing scopes.
-  //
-  // If a script is invoked in a nested function context, the frame
-  // offsets for individual values may be different.  Some may be
-  // accessed from the invoking function and some may come from a
-  // parent function.
-
-  class script_stack_frame : public stack_frame
-  {
-  public:
-
-    script_stack_frame (void) = delete;
-
-    script_stack_frame (tree_evaluator& tw, octave_user_script *script,
-                        unwind_protect *up_frame, size_t index,
-                        stack_frame *static_link);
-
-    script_stack_frame (const script_stack_frame& elt) = default;
-
-    script_stack_frame& operator = (const script_stack_frame& elt) = delete;
-
-    ~script_stack_frame (void) = default;
-
-    script_stack_frame * dup (void) const;
-
-    bool is_user_script_frame (void) const { return true; }
-
-    static stack_frame * get_access_link (stack_frame *static_link);
-
-    static size_t get_num_symbols (octave_user_script *script);
-
-    void set_script_offsets (void);
-
-    void set_script_offsets_internal (const std::map<std::string,
-                                                     symbol_record>& symbols);
-
-    void resize_and_update_script_offsets (const symbol_record& sym);
-
-    symbol_scope get_scope (void) const { return m_script->scope (); }
-
-    octave_function * function (void) const { return m_script; }
-
-    unwind_protect *
-    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
-
-    symbol_record lookup_symbol (const std::string& name) const;
-
-    symbol_record insert_symbol (const std::string&);
-
-    size_t size (void) const { return m_lexical_frame_offsets.size (); }
-
-    void resize (size_t size)
-    {
-      m_lexical_frame_offsets.resize (size, 0);
-      m_value_offsets.resize (size, 0);
-    }
-
-    void get_val_offsets_with_insert (const symbol_record& sym,
-                                      size_t& frame_offset,
-                                      size_t& data_offset);
-
-    bool get_val_offsets_internal (const symbol_record& sym,
-                                   size_t& frame_offset,
-                                   size_t& data_offset) const;
-
-    bool get_val_offsets (const symbol_record& sym, size_t& frame_offset,
-                          size_t& data_offset) const;
-
-    scope_flags scope_flag (const symbol_record& sym) const;
-
-    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
-    {
-      m_access_link->set_auto_fcn_var (avt, val);
-    }
-
-    octave_value get_auto_fcn_var (auto_var_type avt) const
-    {
-      return m_access_link->get_auto_fcn_var (avt);
-    }
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using stack_frame::varval;
-    using stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const;
-
-    octave_value& varref (const symbol_record& sym);
-
-    void mark_scope (const symbol_record& sym, scope_flags flag);
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // Script object associated with this stack frame.  Should always
-    // be valid.
-    octave_user_script *m_script;
-
-    // The nearest unwind protect frame that was active when this
-    // stack frame was created.  Should always be valid.
-    unwind_protect *m_unwind_protect_frame;
-
-    // Mapping between the symbols in the symbol_scope object of the
-    // script to the stack frame in which the script is executed.  The
-    // frame offsets may be greater than one if the script is executed
-    // in a nested function context.
-
-    std::vector<size_t> m_lexical_frame_offsets;
-    std::vector<size_t> m_value_offsets;
-  };
-
-  // Base class for values and offsets shared by user_fcn and scope
-  // frames.
-
-  class base_value_stack_frame : public stack_frame
-  {
-  public:
-
-    base_value_stack_frame (void) = delete;
-
-    base_value_stack_frame (tree_evaluator& tw, size_t num_symbols,
-                            size_t index, stack_frame *static_link,
-                            stack_frame *access_link)
-      : stack_frame (tw, index, static_link, access_link),
-        m_values (num_symbols, octave_value ()),
-        m_flags (num_symbols, LOCAL),
-        m_auto_vars (NUM_AUTO_VARS, octave_value ())
-    { }
-
-    base_value_stack_frame (const base_value_stack_frame& elt) = default;
-
-    base_value_stack_frame&
-    operator = (const base_value_stack_frame& elt) = delete;
-
-    ~base_value_stack_frame (void) = default;
-
-    size_t size (void) const
-    {
-      return m_values.size ();
-    }
-
-    void resize (size_t size)
-    {
-      m_values.resize (size, octave_value ());
-      m_flags.resize (size, LOCAL);
-    }
-
-    stack_frame::scope_flags get_scope_flag (size_t data_offset) const
-    {
-      return m_flags.at (data_offset);
-    }
-
-    void set_scope_flag (size_t data_offset, scope_flags flag)
-    {
-      m_flags.at (data_offset) = flag;
-    }
-
-    octave_value get_auto_fcn_var (auto_var_type avt) const
-    {
-      return m_auto_vars.at (avt);
-    }
-
-    void set_auto_fcn_var (auto_var_type avt, const octave_value& val)
-    {
-      m_auto_vars.at (avt) = val;
-    }
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using stack_frame::varval;
-    using stack_frame::varref;
-
-    octave_value varval (size_t data_offset) const
-    {
-      return m_values.at (data_offset);
-    }
-
-    octave_value& varref (size_t data_offset)
-    {
-      return m_values.at (data_offset);
-    }
-
-    void display (bool follow = true) const;
-
-  protected:
-
-    // Variable values.  This array is indexed by the data_offset
-    // value stored in the symbol_record objects of the scope
-    // associated with this stack frame.
-    std::vector<octave_value> m_values;
-
-    // The type of each variable (local, global, persistent) of each
-    // value.  This array is indexed by the data_offset value stored
-    // in the symbol_record objects of the scope associated with this
-    // stack frame.  Local values are found in the M_VALUES array.
-    // Global values are stored in the tree_evaluator object that contains
-    // the stack frame.  Persistent values are stored in the function
-    // scope corresponding to the stack frame.
-    std::vector<scope_flags> m_flags;
-
-    // A fixed list of Automatic variables created for this function.
-    // The elements of this vector correspond to the auto_var_type
-    // enum.
-    std::vector<octave_value> m_auto_vars;
-  };
-
-  // User-defined functions have a symbol_scope object to store the set
-  // of variables in the function and values are stored in the stack
-  // frame corresponding to the invocation of the function or one of
-  // its parents.  The frame offset tells us how many access links we
-  // must follow to find the stack frame that holds the value.  The
-  // value offset is the index into the vector of values in that stack
-  // frame that we should use to find the value.
-  //
-  // Frame and value offsets are determined when the corresponding
-  // function is parsed.
-
-  class user_fcn_stack_frame : public base_value_stack_frame
-  {
-  public:
-
-    user_fcn_stack_frame (void) = delete;
-
-    user_fcn_stack_frame (tree_evaluator& tw, octave_user_function *fcn,
-                          unwind_protect *up_frame, size_t index,
-                          stack_frame *static_link,
-                          stack_frame *access_link = nullptr)
-      : base_value_stack_frame (tw, get_num_symbols (fcn), index, static_link,
-                                (access_link
-                                 ? access_link
-                                 : get_access_link (fcn, static_link))),
-        m_fcn (fcn), m_unwind_protect_frame (up_frame)
-    { }
-
-    user_fcn_stack_frame (const user_fcn_stack_frame& elt) = default;
-
-    user_fcn_stack_frame&
-    operator = (const user_fcn_stack_frame& elt) = delete;
-
-    ~user_fcn_stack_frame (void) = default;
-
-    user_fcn_stack_frame * dup (void) const;
-
-    bool is_user_fcn_frame (void) const { return true; }
-
-    static stack_frame *
-    get_access_link (octave_user_function *fcn, stack_frame *static_link);
-
-    static size_t get_num_symbols (octave_user_function *fcn)
-    {
-      symbol_scope fcn_scope = fcn->scope ();
-
-      return fcn_scope.num_symbols ();
-    }
-
-    void clear_values (void);
-
-    symbol_scope get_scope (void) const { return m_fcn->scope (); }
-
-    octave_function * function (void) const { return m_fcn; }
-
-    unwind_protect *
-    unwind_protect_frame (void) const { return m_unwind_protect_frame; }
-
-    symbol_record lookup_symbol (const std::string& name) const;
-
-    symbol_record insert_symbol (const std::string&);
-
-    scope_flags scope_flag (const symbol_record& sym) const;
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using base_value_stack_frame::varval;
-    using base_value_stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const;
-
-    octave_value& varref (const symbol_record& sym);
-
-    void mark_scope (const symbol_record& sym, scope_flags flag);
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // User-defined object associated with this stack frame.  Should
-    // always be valid.
-    octave_user_function *m_fcn;
-
-    // The nearest unwind protect frame that was active when this
-    // stack frame was created.  Should always be valid.
-    unwind_protect *m_unwind_protect_frame;
-  };
-
-  // Pure scope stack frames (primarily the top-level workspace) have
-  // a set of variables and values are stored in the stack frame.  All
-  // variable accesses are direct as there are no parent stack frames.
-  //
-  // Value offsets are determined when the corresponding variable is
-  // entered into the symbol_scope object corresponding to the frame.
-
-  class scope_stack_frame : public base_value_stack_frame
-  {
-  public:
-
-    scope_stack_frame (void) = delete;
-
-    scope_stack_frame (tree_evaluator& tw, const symbol_scope& scope,
-                       size_t index, stack_frame *static_link)
-      : base_value_stack_frame (tw, scope.num_symbols (), index,
-                                static_link, nullptr),
-        m_scope (scope)
-    { }
-
-    scope_stack_frame (const scope_stack_frame& elt) = default;
-
-    scope_stack_frame& operator = (const scope_stack_frame& elt) = delete;
-
-    ~scope_stack_frame (void) = default;
-
-    scope_stack_frame * dup (void) const;
-
-    bool is_scope_frame (void) const { return true; }
-
-    symbol_scope get_scope (void) const { return m_scope; }
-
-    symbol_record lookup_symbol (const std::string& name) const
-    {
-      return m_scope.lookup_symbol (name);
-    }
-
-    symbol_record insert_symbol (const std::string&);
-
-    scope_flags scope_flag (const symbol_record& sym) const;
-
-    // We only need to override one of each of these functions.  The
-    // using declaration will avoid warnings about partially-overloaded
-    // virtual functions.
-    using base_value_stack_frame::varval;
-    using base_value_stack_frame::varref;
-
-    octave_value varval (const symbol_record& sym) const;
-
-    octave_value& varref (const symbol_record& sym);
-
-    void mark_scope (const symbol_record& sym, scope_flags flag);
-
-    void display (bool follow = true) const;
-
-    void accept (stack_frame_walker& sfw);
-
-  private:
-
-    // The scope object associated with this stack frame.
-    symbol_scope m_scope;
-  };
 }
 
 #endif
--- a/libinterp/corefcn/syminfo-accumulator.h	Tue Jun 09 14:10:41 2020 -0700
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,307 +0,0 @@
-////////////////////////////////////////////////////////////////////////
-//
-// Copyright (C) 2019-2020 The Octave Project Developers
-//
-// See the file COPYRIGHT.md in the top-level directory of this
-// distribution or <https://octave.org/copyright/>.
-//
-// This file is part of Octave.
-//
-// Octave is free software: you can redistribute it and/or modify it
-// under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Octave is distributed in the hope that it will be useful, but
-// WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Octave; see the file COPYING.  If not, see
-// <https://www.gnu.org/licenses/>.
-//
-////////////////////////////////////////////////////////////////////////
-
-#if ! defined (octave_syminfo_accumulator_h)
-#define octave_syminfo_accumulator_h 1
-
-#include "octave-config.h"
-
-#include <string>
-
-#include "oct-map.h"
-#include "ov.h"
-#include "stack-frame.h"
-#include "syminfo.h"
-#include "symrec.h"
-
-namespace octave
-{
-  class symbol_info_accumulator : public stack_frame_walker
-  {
-  public:
-
-    symbol_info_accumulator (const std::string& pattern,
-                             bool have_regexp = false)
-      : stack_frame_walker (), m_patterns (pattern), m_match_all (false),
-        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
-        m_found_names ()
-    { }
-
-    symbol_info_accumulator (const string_vector& patterns,
-                             bool have_regexp = false)
-      : stack_frame_walker (), m_patterns (patterns), m_match_all (false),
-        m_first_only (false), m_have_regexp (have_regexp), m_sym_inf_list (),
-        m_found_names ()
-    { }
-
-    symbol_info_accumulator (bool match_all = true, bool first_only = true)
-      : stack_frame_walker (), m_patterns (), m_match_all (match_all),
-        m_first_only (first_only), m_have_regexp (false),
-        m_sym_inf_list (), m_found_names ()
-    { }
-
-    symbol_info_accumulator (const symbol_info_accumulator&) = delete;
-
-    symbol_info_accumulator& operator = (const symbol_info_accumulator&) = delete;
-
-    ~symbol_info_accumulator (void) = default;
-
-    bool is_empty  (void) const
-    {
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          const symbol_info_list& lst = nm_sil.second;
-
-          if (! lst.empty ())
-            return false;
-        }
-
-      return true;
-    }
-
-    std::list<std::string> names (void) const
-    {
-      std::list<std::string> retval;
-
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          const symbol_info_list& lst = nm_sil.second;
-
-          std::list<std::string> nm_list = lst.names ();
-
-          for (const auto& nm : nm_list)
-            retval.push_back (nm);
-        }
-
-      return retval;
-    }
-
-    symbol_info_list symbol_info (void) const
-    {
-      symbol_info_list retval;
-
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          const symbol_info_list& lst = nm_sil.second;
-
-          for (const auto& syminf : lst)
-            retval.push_back (syminf);
-        }
-
-      return retval;
-    }
-
-    octave_map map_value (void) const
-    {
-      octave_map retval;
-
-      // FIXME: is there a better way to concatenate structures?
-
-      size_t n_frames = m_sym_inf_list.size ();
-
-      OCTAVE_LOCAL_BUFFER (octave_map, map_list, n_frames);
-
-      size_t j = 0;
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          std::string scope_name = nm_sil.first;
-          const symbol_info_list& lst = nm_sil.second;
-
-          map_list[j] = lst.map_value (scope_name, n_frames-j);
-
-          j++;
-        }
-
-      return octave_map::cat (-1, n_frames, map_list);
-    }
-
-    void display (std::ostream& os, const std::string& format) const
-    {
-      for (const auto& nm_sil : m_sym_inf_list)
-        {
-          os << "\nvariables in scope: " << nm_sil.first << "\n\n";
-
-          const symbol_info_list& lst = nm_sil.second;
-
-          lst.display (os, format);
-        }
-    }
-
-    void visit_compiled_fcn_stack_frame (compiled_fcn_stack_frame& frame)
-    {
-      // This one follows static link always.  Hmm, should the access
-      // link for a compiled_fcn_stack_frame be the same as the static
-      // link?
-
-      stack_frame *slink = frame.static_link ();
-
-      if (slink)
-        slink->accept (*this);
-    }
-
-    void visit_script_stack_frame (script_stack_frame& frame)
-    {
-      stack_frame *alink = frame.access_link ();
-
-      if (alink)
-        alink->accept (*this);
-    }
-
-    void visit_user_fcn_stack_frame (user_fcn_stack_frame& frame)
-    {
-      append_list (frame);
-
-      stack_frame *alink = frame.access_link ();
-
-      if (alink)
-        alink->accept (*this);
-    }
-
-    void visit_scope_stack_frame (scope_stack_frame& frame)
-    {
-      append_list (frame);
-
-      stack_frame *alink = frame.access_link ();
-
-      if (alink)
-        alink->accept (*this);
-    }
-
-  private:
-
-    typedef std::pair<std::string, symbol_info_list> syminf_list_elt;
-
-    // FIXME: the following is too complex and duplicates too much
-    // code.  Maybe it should be split up so we have separate classes
-    // that do each job that is needed?
-
-    std::list<symbol_record>
-    filter (stack_frame& frame, const std::list<symbol_record>& symbols)
-    {
-      std::list<symbol_record> new_symbols;
-
-      if (m_match_all)
-        {
-          for (const auto& sym : symbols)
-            {
-              if (frame.is_defined (sym))
-                {
-                  std::string name = sym.name ();
-
-                  if (m_first_only
-                      && m_found_names.find (name) != m_found_names.end ())
-                    continue;
-
-                  m_found_names.insert (name);
-
-                  new_symbols.push_back (sym);
-                }
-            }
-        }
-      else if (m_have_regexp)
-        {
-          octave_idx_type npatterns = m_patterns.numel ();
-
-          for (octave_idx_type j = 0; j < npatterns; j++)
-            {
-              std::string pattern = m_patterns[j];
-
-              regexp pat (pattern);
-
-              for (const auto& sym : symbols)
-                {
-                  std::string name = sym.name ();
-
-                  if (pat.is_match (name) && frame.is_defined (sym))
-                    {
-                      if (m_first_only
-                          && m_found_names.find (name) != m_found_names.end ())
-                        continue;
-
-                      m_found_names.insert (name);
-
-                      new_symbols.push_back (sym);
-                    }
-                }
-            }
-        }
-      else
-        {
-          octave_idx_type npatterns = m_patterns.numel ();
-
-          for (octave_idx_type j = 0; j < npatterns; j++)
-            {
-              std::string pattern = m_patterns[j];
-
-              glob_match pat (pattern);
-
-              for (const auto& sym : symbols)
-                {
-                  std::string name = sym.name ();
-
-                  if (pat.match (name) && frame.is_defined (sym))
-                    {
-                      if (m_first_only
-                          && m_found_names.find (name) == m_found_names.end ())
-                        continue;
-
-                      m_found_names.insert (name);
-
-                      new_symbols.push_back (sym);
-                    }
-                }
-            }
-        }
-
-      return new_symbols;
-    }
-
-    void append_list (stack_frame& frame)
-    {
-      symbol_scope scope = frame.get_scope ();
-
-      std::list<symbol_record> symbols = scope.symbol_list ();
-
-      if (m_match_all || ! m_patterns.empty ())
-        symbols = filter (frame, symbols);
-
-      symbol_info_list syminf_list = frame.make_symbol_info_list (symbols);
-
-      m_sym_inf_list.push_back (syminf_list_elt (scope.name (), syminf_list));
-    }
-
-    string_vector m_patterns;
-
-    bool m_match_all;
-    bool m_first_only;
-    bool m_have_regexp;
-
-    std::list<std::pair<std::string, symbol_info_list>> m_sym_inf_list;
-
-    std::set<std::string> m_found_names;
-  };
-}
-
-#endif