# HG changeset patch
# User Petter Tomner <tomner@kth.se>
# Date 1641747256 -3600
#      Sun Jan 09 17:54:16 2022 +0100
# Node ID f70c5dd1bb09ae461b778641d3f890ece372b886
# Parent  3849d8664922d67027e772c2873131c169e76a66
WIP: fold walker och bryt ut tree_index_expression till
     tree_simple_index_expression. behövs mer fold

diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/module.mk
--- a/libinterp/parse-tree/module.mk	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/module.mk	Sun Jan 09 17:54:16 2022 +0100
@@ -75,6 +75,7 @@
   %reldir%/pt-except.cc \
   %reldir%/pt-exp.cc \
   %reldir%/pt-fcn-handle.cc \
+  %reldir%/pt-fold.cc \
   %reldir%/pt-id.cc \
   %reldir%/pt-idx.cc \
   %reldir%/pt-loop.cc \
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/oct-parse.yy
--- a/libinterp/parse-tree/oct-parse.yy	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/oct-parse.yy	Sun Jan 09 17:54:16 2022 +0100
@@ -80,6 +80,8 @@
 #include "utils.h"
 #include "variables.h"
 
+#include "pt-fold.h"
+
 // oct-parse.h must be included after pt-all.h
 #include "oct-parse.h"
 
@@ -1524,6 +1526,8 @@
 
                         if (! parser.validate_primary_fcn ())
                           YYABORT;
+
+                        parser.fold_primary_fcn ();
                       }
                     else
                       {
@@ -1535,6 +1539,8 @@
 
                         if (! parser.validate_primary_fcn ())
                           YYABORT;
+
+                        parser.fold_primary_fcn ();
                       }
 
                     $$ = nullptr;
@@ -5890,6 +5896,19 @@
     return true;
   }
 
+  void
+  base_parser::fold_primary_fcn (void)
+  {
+    octave_user_code *code = m_primary_fcn.user_code_value ();
+
+    if (code)
+      {
+        tree_fold fold;
+
+        code->accept (fold);
+      }
+  }
+
   // Maybe print a warning if an assignment expression is used as the
   // test in a logical expression.
 
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/parse.h
--- a/libinterp/parse-tree/parse.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/parse.h	Sun Jan 09 17:54:16 2022 +0100
@@ -727,6 +727,8 @@
     // semantic errors.
     OCTINTERP_API bool validate_primary_fcn (void);
 
+    OCTINTERP_API void fold_primary_fcn (void);
+
     OCTINTERP_API bool finish_input (tree_statement_list *lst,
                                      bool at_eof = false);
 
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-binop.h
--- a/libinterp/parse-tree/pt-binop.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-binop.h	Sun Jan 09 17:54:16 2022 +0100
@@ -46,6 +46,7 @@
   class tree_binary_expression : public tree_expression
   {
   public:
+    friend class tree_fold;
 
     tree_binary_expression (tree_expression *a, tree_expression *b,
                             int l = -1, int c = -1,
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-eval.cc
--- a/libinterp/parse-tree/pt-eval.cc	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-eval.cc	Sun Jan 09 17:54:16 2022 +0100
@@ -3627,6 +3627,12 @@
   }
 
   void
+  tree_evaluator::visit_simple_index_expression (tree_simple_index_expression&)
+  {
+    panic_impossible ();
+  }
+
+  void
   tree_evaluator::visit_matrix (tree_matrix&)
   {
     panic_impossible ();
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-eval.h
--- a/libinterp/parse-tree/pt-eval.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-eval.h	Sun Jan 09 17:54:16 2022 +0100
@@ -275,6 +275,8 @@
 
     void visit_index_expression (tree_index_expression&);
 
+    void visit_simple_index_expression (tree_simple_index_expression&);
+
     void visit_matrix (tree_matrix&);
 
     void visit_cell (tree_cell&);
@@ -832,7 +834,8 @@
     octave_value
     string_fill_char (const octave_value_list& args, int nargout);
 
-    void final_index_error (index_exception& ie, const tree_expression *expr);
+    void final_index_error (index_exception& ie, const tree_expression *expr)
+      __attribute__ ((noreturn));
 
     octave_value do_who (int argc, const string_vector& argv,
                          bool return_list, bool verbose = false);
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-fold.cc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/pt-fold.cc	Sun Jan 09 17:54:16 2022 +0100
@@ -0,0 +1,839 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2017-2022 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 (HAVE_CONFIG_H)
+#  include "config.h"
+#endif
+
+#include "pt-all.h"
+#include "tiny_vector.h"
+
+#include "pt-fold.h"
+
+#include <iostream>
+
+#define FOLD(obj, member, type)                                                \
+  do {                                                                  \
+    if (!obj.member) break;                                             \
+    tree *tmp = obj.member->fold ();                                    \
+    if (tmp != obj.member)                                              \
+      {                                                                 \
+        delete obj.member;                                              \
+        obj.member = static_cast<type*> (tmp);                          \
+      }                                                                 \
+  } while ((0));
+
+namespace octave
+{
+  void tree_fold::visit_anon_fcn_handle (tree_anon_fcn_handle& afh)
+  {
+    tree_parameter_list *parameter_list = afh.parameter_list ();
+
+    if (parameter_list)
+      parameter_list->accept (*this);
+
+    tree_expression *expression = afh.expression ();
+
+    if (expression)
+      expression->accept (*this);
+  }
+
+  void tree_fold::visit_argument_list (tree_argument_list& lst)
+  {
+    auto p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_expression *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_arguments_block (tree_arguments_block& blk)
+  {
+    tree_args_block_attribute_list *attribute_list = blk.attribute_list ();
+
+    if (attribute_list)
+      attribute_list->accept (*this);
+
+    tree_args_block_validation_list *validation_list = blk.validation_list ();
+
+    if (validation_list)
+      validation_list->accept (*this);
+  }
+
+  void tree_fold::visit_args_block_attribute_list (tree_args_block_attribute_list& lst)
+  {
+    tree_identifier *attribute = lst.attribute ();
+
+    if (attribute)
+      attribute->accept (*this);
+  }
+
+  void tree_fold::visit_args_block_validation_list (tree_args_block_validation_list& lst)
+  {
+    for (auto *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_arg_validation (tree_arg_validation& val)
+  {
+    tree_expression *arg_name = val.identifier_expression ();
+
+    if (arg_name)
+      arg_name->accept (*this);
+
+    tree_arg_size_spec *size_spec = val.size_spec ();
+
+    if (size_spec)
+      size_spec->accept (*this);
+
+    tree_identifier *class_name = val.class_name ();
+
+    if (class_name)
+      class_name->accept (*this);
+
+    tree_arg_validation_fcns *validation_fcns = val.validation_fcns ();
+
+    if (validation_fcns)
+      validation_fcns->accept (*this);
+
+    tree_expression *default_value = val.initializer_expression ();
+
+    if (default_value)
+      default_value->accept (*this);
+  }
+
+  void tree_fold::visit_arg_size_spec (tree_arg_size_spec& spec)
+  {
+    tree_argument_list *size_args = spec.size_args ();
+
+    if (size_args)
+      size_args->accept (*this);
+  }
+
+  void tree_fold::visit_arg_validation_fcns (tree_arg_validation_fcns& spec)
+  {
+    tree_argument_list *fcn_args = spec.fcn_args ();
+
+    if (fcn_args)
+      fcn_args->accept (*this);
+  }
+
+  void tree_fold::visit_binary_expression (tree_binary_expression& expr)
+  {
+    tree_expression *op1 = expr.lhs ();
+
+    if (op1)
+      op1->accept (*this);
+
+    tree_expression *op2 = expr.rhs ();
+
+    if (op2)
+      op2->accept (*this);
+
+    FOLD (expr, m_lhs, tree_expression);
+    FOLD (expr, m_rhs, tree_expression);
+  }
+
+  void tree_fold::visit_boolean_expression (tree_boolean_expression& expr)
+  {
+    visit_binary_expression (expr);
+  }
+
+  void tree_fold::
+  visit_compound_binary_expression (tree_compound_binary_expression& expr)
+  {
+    tree_expression *op1 = expr.clhs ();
+
+    if (op1)
+      op1->accept (*this);
+
+    tree_expression *op2 = expr.crhs ();
+
+    if (op2)
+      op2->accept (*this);
+  }
+
+  void tree_fold::visit_break_command (tree_break_command&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_colon_expression (tree_colon_expression& expr)
+  {
+    tree_expression *op1 = expr.base ();
+
+    if (op1)
+      op1->accept (*this);
+
+    tree_expression *op3 = expr.increment ();
+
+    if (op3)
+      op3->accept (*this);
+
+    tree_expression *op2 = expr.limit ();
+
+    if (op2)
+      op2->accept (*this);
+  }
+
+  void tree_fold::visit_continue_command (tree_continue_command&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_decl_command (tree_decl_command& cmd)
+  {
+    tree_decl_init_list *init_list = cmd.initializer_list ();
+
+    if (init_list)
+      init_list->accept (*this);
+  }
+
+  void tree_fold::visit_decl_elt (tree_decl_elt& cmd)
+  {
+    tree_identifier *id = cmd.ident ();
+
+    if (id)
+      id->accept (*this);
+
+    tree_expression *expr = cmd.expression ();
+
+    if (expr)
+      expr->accept (*this);
+  }
+
+  void tree_fold::visit_decl_init_list (tree_decl_init_list& lst)
+  {
+    // FIXME: tree_decl_elt is not derived from tree, so should it
+    // really have an accept method?
+
+    for (tree_decl_elt *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_simple_for_command (tree_simple_for_command& cmd)
+  {
+    tree_expression *lhs = cmd.left_hand_side ();
+
+    if (lhs)
+      lhs->accept (*this);
+
+    tree_expression *expr = cmd.control_expr ();
+
+    if (expr)
+      expr->accept (*this);
+
+    tree_expression *maxproc = cmd.maxproc_expr ();
+
+    if (maxproc)
+      maxproc->accept (*this);
+
+    tree_statement_list *list = cmd.body ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_complex_for_command (tree_complex_for_command& cmd)
+  {
+    tree_argument_list *lhs = cmd.left_hand_side ();
+
+    if (lhs)
+      lhs->accept (*this);
+
+    tree_expression *expr = cmd.control_expr ();
+
+    if (expr)
+      expr->accept (*this);
+
+    tree_statement_list *list = cmd.body ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_spmd_command (tree_spmd_command& cmd)
+  {
+    tree_statement_list *body = cmd.body ();
+
+    if (body)
+      body->accept (*this);
+  }
+
+  void tree_fold::visit_octave_user_script (octave_user_script& fcn)
+  {
+    tree_statement_list *cmd_list = fcn.body ();
+
+    if (cmd_list)
+      cmd_list->accept (*this);
+  }
+
+  void tree_fold::visit_octave_user_function (octave_user_function& fcn)
+  {
+    tree_statement_list *cmd_list = fcn.body ();
+
+    if (cmd_list)
+      cmd_list->accept (*this);
+  }
+
+  void tree_fold::visit_function_def (tree_function_def& fdef)
+  {
+    octave_value fcn = fdef.function ();
+
+    octave_function *f = fcn.function_value ();
+
+    if (f)
+      f->accept (*this);
+  }
+
+  void tree_fold::visit_identifier (tree_identifier&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_if_clause (tree_if_clause& cmd)
+  {
+    tree_expression *expr = cmd.condition ();
+
+    if (expr)
+      expr->accept (*this);
+
+    tree_statement_list *list = cmd.commands ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_if_command (tree_if_command& cmd)
+  {
+    tree_if_command_list *list = cmd.cmd_list ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_if_command_list (tree_if_command_list& lst)
+  {
+    auto p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_if_clause *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_switch_case (tree_switch_case& cs)
+  {
+    tree_expression *label = cs.case_label ();
+
+    if (label)
+      label->accept (*this);
+
+    tree_statement_list *list = cs.commands ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_switch_case_list (tree_switch_case_list& lst)
+  {
+    auto p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_switch_case *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_switch_command (tree_switch_command& cmd)
+  {
+    tree_expression *expr = cmd.switch_value ();
+
+    if (expr)
+      expr->accept (*this);
+
+    tree_switch_case_list *list = cmd.case_list ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_index_expression (tree_index_expression& expr)
+  {
+    tree_expression *e = expr.expression ();
+
+    if (e)
+      e->accept (*this);
+
+    tiny_vector<tree_argument_list *> arg_lists = expr.arg_lists ();
+    std::list<std::shared_ptr<string_vector>> arg_names = expr.arg_names ();
+    std::list<tree_expression *> dyn_fields = expr.dyn_fields ();
+
+    auto p_arg_lists = arg_lists.begin ();
+    auto p_arg_names = arg_names.begin ();
+    auto p_dyn_fields = dyn_fields.begin ();
+
+    std::string type_tags = expr.type_tags ();
+    int n = type_tags.length ();
+
+    for (int i = 0; i < n; i++)
+      {
+        switch (type_tags[i])
+          {
+          case '(':
+          case '{':
+            {
+              tree_argument_list *l = *p_arg_lists;
+              if (l)
+                l->accept (*this);
+            }
+            break;
+
+          case '.':
+            {
+              std::string fn = (**p_arg_names)(0);
+              if (fn.empty ())
+                {
+                  tree_expression *df = *p_dyn_fields;
+                  if (df)
+                    df->accept (*this);
+                }
+            }
+            break;
+
+          default:
+            panic_impossible ();
+          }
+
+        p_arg_lists++;
+        p_arg_names++;
+        p_dyn_fields++;
+      }
+  }
+
+  void tree_fold::visit_matrix (tree_matrix& lst)
+  {
+    auto p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_argument_list *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_cell (tree_cell& lst)
+  {
+    auto p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_argument_list *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_multi_assignment (tree_multi_assignment& expr)
+  {
+    tree_argument_list *lhs = expr.left_hand_side ();
+
+    if (lhs)
+      lhs->accept (*this);
+
+    tree_expression *rhs = expr.right_hand_side ();
+
+    if (rhs)
+      rhs->accept (*this);
+  }
+
+  void tree_fold::visit_no_op_command (tree_no_op_command&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_constant (tree_constant&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_fcn_handle (tree_fcn_handle&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_parameter_list (tree_parameter_list& lst)
+  {
+    auto p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_decl_elt *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_postfix_expression (tree_postfix_expression& expr)
+  {
+    tree_expression *e = expr.operand ();
+
+    if (e)
+      e->accept (*this);
+  }
+
+  void tree_fold::visit_prefix_expression (tree_prefix_expression& expr)
+  {
+    tree_expression *e = expr.operand ();
+
+    if (e)
+      e->accept (*this);
+  }
+
+  void tree_fold::visit_return_command (tree_return_command&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_simple_assignment (tree_simple_assignment& expr)
+  {
+    tree_expression *lhs = expr.left_hand_side ();
+
+    if (lhs)
+      lhs->accept (*this);
+
+    tree_expression *rhs = expr.right_hand_side ();
+
+    if (rhs)
+      rhs->accept (*this);
+  }
+
+  void tree_fold::visit_statement (tree_statement& stmt)
+  {
+    tree_command *cmd = stmt.command ();
+
+    if (cmd)
+      cmd->accept (*this);
+    else
+      {
+        tree_expression *expr = stmt.expression ();
+
+        if (expr)
+          expr->accept (*this);
+      }
+  }
+
+
+  void tree_fold::visit_statement_null (tree_statement_null&)
+  {
+    // Do nothing
+  }
+
+  void tree_fold::visit_statement_cmd (tree_statement_cmd& stmt)
+  {
+    tree_command *cmd = stmt.command ();
+
+    if (cmd)
+      cmd->accept (*this);
+  }
+
+  void tree_fold::visit_statement_expression (tree_statement_expression& stmt)
+  {
+    tree_expression *expr = stmt.expression ();
+
+    if (expr)
+      expr->accept (*this);
+
+  }
+
+  void tree_fold::visit_statement_list (tree_statement_list& lst)
+  {
+    for (tree_statement *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_try_catch_command (tree_try_catch_command& cmd)
+  {
+    tree_statement_list *try_code = cmd.body ();
+
+    if (try_code)
+      try_code->accept (*this);
+
+    tree_identifier *expr_id = cmd.identifier ();
+
+    if (expr_id)
+      expr_id->accept (*this);
+
+    tree_statement_list *catch_code = cmd.cleanup ();
+
+    if (catch_code)
+      catch_code->accept (*this);
+  }
+
+  void tree_fold::visit_unwind_protect_command (tree_unwind_protect_command& cmd)
+  {
+    tree_statement_list *unwind_protect_code = cmd.body ();
+
+    if (unwind_protect_code)
+      unwind_protect_code->accept (*this);
+
+    tree_statement_list *cleanup_code = cmd.cleanup ();
+
+    if (cleanup_code)
+      cleanup_code->accept (*this);
+  }
+
+  void tree_fold::visit_while_command (tree_while_command& cmd)
+  {
+    tree_expression *expr = cmd.condition ();
+
+    if (expr)
+      expr->accept (*this);
+
+    tree_statement_list *list = cmd.body ();
+
+    if (list)
+      list->accept (*this);
+  }
+
+  void tree_fold::visit_do_until_command (tree_do_until_command& cmd)
+  {
+    tree_statement_list *list = cmd.body ();
+
+    if (list)
+      list->accept (*this);
+
+    tree_expression *expr = cmd.condition ();
+
+    if (expr)
+      expr->accept (*this);
+  }
+
+  void tree_fold::visit_superclass_ref (tree_superclass_ref&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_metaclass_query (tree_metaclass_query&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_classdef_attribute (tree_classdef_attribute& attr)
+  {
+    tree_identifier *id = attr.ident ();
+
+    if (id)
+      id->accept (*this);
+
+    tree_expression *expr = attr.expression ();
+
+    if (expr)
+      expr->accept (*this);
+  }
+
+  void tree_fold::visit_classdef_attribute_list (tree_classdef_attribute_list& lst)
+  {
+    for (auto *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef_superclass (tree_classdef_superclass&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_classdef_superclass_list (tree_classdef_superclass_list& lst)
+  {
+    for (auto *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef_property (tree_classdef_property&)
+  {
+    // FIXME: Should we operate on the tree_arg_validation object or the
+    // identifier and expression parts separately?
+  }
+
+  void tree_fold::visit_classdef_property_list (tree_classdef_property_list& lst)
+  {
+    for (auto *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef_properties_block (tree_classdef_properties_block& blk)
+  {
+    tree_classdef_property_list *property_list = blk.element_list ();
+
+    if (property_list)
+      property_list->accept (*this);
+  }
+
+  void tree_fold::visit_classdef_methods_list (tree_classdef_methods_list& lst)
+  {
+    for (auto ov_meth : lst)
+      {
+        octave_user_function *meth = ov_meth.user_function_value ();
+
+        if (meth)
+          meth->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef_methods_block (tree_classdef_methods_block& blk)
+  {
+    tree_classdef_methods_list *methods_list = blk.element_list ();
+
+    if (methods_list)
+      methods_list->accept (*this);
+  }
+
+  void tree_fold::visit_classdef_event (tree_classdef_event&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_classdef_events_list (tree_classdef_events_list& lst)
+  {
+    for (auto *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef_events_block (tree_classdef_events_block& blk)
+  {
+    tree_classdef_events_list *events_list = blk.element_list ();
+
+    if (events_list)
+      events_list->accept (*this);
+  }
+
+  void tree_fold::visit_classdef_enum (tree_classdef_enum&)
+  {
+    // Nothing to do.
+  }
+
+  void tree_fold::visit_classdef_enum_list (tree_classdef_enum_list& lst)
+  {
+    for (auto *elt : lst)
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef_enum_block (tree_classdef_enum_block& blk)
+  {
+    tree_classdef_enum_list *enum_list = blk.element_list ();
+
+    if (enum_list)
+      enum_list->accept (*this);
+  }
+
+  void tree_fold::visit_classdef_body (tree_classdef_body& body)
+  {
+    for (auto *elt : body.properties_list ())
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+
+    for (auto *elt : body.methods_list ())
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+
+
+    for (auto *elt : body.events_list ())
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+
+    for (auto *elt : body.enum_list ())
+      {
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void tree_fold::visit_classdef (tree_classdef& cdef)
+  {
+    tree_classdef_attribute_list *attribute_list = cdef.attribute_list ();
+
+    if (attribute_list)
+      attribute_list->accept (*this);
+
+    tree_identifier *ident = cdef.ident ();
+
+    if (ident)
+      ident->accept (*this);
+
+    tree_classdef_superclass_list *superclass_list = cdef.superclass_list ();
+
+    if (superclass_list)
+      superclass_list->accept (*this);
+
+    tree_classdef_body *body = cdef.body ();
+
+    if (body)
+      body->accept (*this);
+  }
+}
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-fold.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/pt-fold.h	Sun Jan 09 17:54:16 2022 +0100
@@ -0,0 +1,199 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 1996-2022 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_pt_fold_h)
+#define octave_pt_fold_h 1
+
+#include "octave-config.h"
+
+#include "pt-walk.h"
+
+class octave_user_script;
+class octave_user_function;
+
+namespace octave
+{
+
+  // Fold tree nodes into smaller nodes.
+  //
+  // Nothing to do with constant folding
+
+  class tree_fold : public tree_walker
+  {
+  public:
+
+    tree_fold (void) { }
+
+    virtual ~tree_fold (void) = default;
+
+    // No copying!
+
+    tree_fold (const tree_fold&) = delete;
+
+    tree_fold& operator = (const tree_fold&) = delete;
+
+    virtual void visit_anon_fcn_handle (tree_anon_fcn_handle&);
+
+    virtual void visit_argument_list (tree_argument_list&);
+
+    virtual void visit_arguments_block (tree_arguments_block&);
+
+    virtual void visit_args_block_attribute_list (tree_args_block_attribute_list&);
+
+    virtual void visit_args_block_validation_list (tree_args_block_validation_list&);
+
+    virtual void visit_arg_validation (tree_arg_validation&);
+
+    virtual void visit_arg_size_spec (tree_arg_size_spec&);
+
+    virtual void visit_arg_validation_fcns (tree_arg_validation_fcns&);
+
+    virtual void visit_binary_expression (tree_binary_expression&);
+
+    virtual void visit_boolean_expression (tree_boolean_expression&);
+
+    virtual void visit_compound_binary_expression (tree_compound_binary_expression&);
+
+    virtual void visit_break_command (tree_break_command&);
+
+    virtual void visit_colon_expression (tree_colon_expression&);
+
+    virtual void visit_continue_command (tree_continue_command&);
+
+    virtual void visit_decl_command (tree_decl_command&);
+
+    virtual void visit_decl_elt (tree_decl_elt&);
+
+    virtual void visit_decl_init_list (tree_decl_init_list&);
+
+    virtual void visit_simple_for_command (tree_simple_for_command&);
+
+    virtual void visit_complex_for_command (tree_complex_for_command&);
+
+    virtual void visit_spmd_command (tree_spmd_command&);
+
+    virtual void visit_octave_user_script (octave_user_script&);
+
+    virtual void visit_octave_user_function (octave_user_function&);
+
+    virtual void visit_function_def (tree_function_def&);
+
+    virtual void visit_identifier (tree_identifier&);
+
+    virtual void visit_if_clause (tree_if_clause&);
+
+    virtual void visit_if_command (tree_if_command&);
+
+    virtual void visit_if_command_list (tree_if_command_list&);
+
+    virtual void visit_switch_case (tree_switch_case&);
+
+    virtual void visit_switch_case_list (tree_switch_case_list&);
+
+    virtual void visit_switch_command (tree_switch_command&);
+
+    virtual void visit_index_expression (tree_index_expression&);
+
+    virtual void visit_matrix (tree_matrix&);
+
+    virtual void visit_cell (tree_cell&);
+
+    virtual void visit_multi_assignment (tree_multi_assignment&);
+
+    virtual void visit_no_op_command (tree_no_op_command&);
+
+    virtual void visit_constant (tree_constant&);
+
+    virtual void visit_fcn_handle (tree_fcn_handle&);
+
+    virtual void visit_parameter_list (tree_parameter_list&);
+
+    virtual void visit_postfix_expression (tree_postfix_expression&);
+
+    virtual void visit_prefix_expression (tree_prefix_expression&);
+
+    virtual void visit_return_command (tree_return_command&);
+
+    virtual void visit_simple_assignment (tree_simple_assignment&);
+
+    virtual void visit_statement (tree_statement&);
+
+    virtual void visit_statement_cmd (tree_statement_cmd&);
+
+    virtual void visit_statement_expression (tree_statement_expression&);
+
+    virtual void visit_statement_null (tree_statement_null&);
+
+    virtual void visit_statement_list (tree_statement_list&);
+
+    virtual void visit_try_catch_command (tree_try_catch_command&);
+
+    virtual void visit_unwind_protect_command (tree_unwind_protect_command&);
+
+    virtual void visit_while_command (tree_while_command&);
+
+    virtual void visit_do_until_command (tree_do_until_command&);
+
+    virtual void visit_superclass_ref (tree_superclass_ref&);
+
+    virtual void visit_metaclass_query (tree_metaclass_query&);
+
+    virtual void visit_classdef_attribute (tree_classdef_attribute&);
+
+    virtual void visit_classdef_attribute_list (tree_classdef_attribute_list&);
+
+    virtual void visit_classdef_superclass (tree_classdef_superclass&);
+
+    virtual void visit_classdef_superclass_list (tree_classdef_superclass_list&);
+
+    virtual void visit_classdef_property (tree_classdef_property&);
+
+    virtual void visit_classdef_property_list (tree_classdef_property_list&);
+
+    virtual void visit_classdef_properties_block (tree_classdef_properties_block&);
+
+    virtual void visit_classdef_methods_list (tree_classdef_methods_list&);
+
+    virtual void visit_classdef_methods_block (tree_classdef_methods_block&);
+
+    virtual void visit_classdef_event (tree_classdef_event&);
+
+    virtual void visit_classdef_events_list (tree_classdef_events_list&);
+
+    virtual void visit_classdef_events_block (tree_classdef_events_block&);
+
+    virtual void visit_classdef_enum (tree_classdef_enum&);
+
+    virtual void visit_classdef_enum_list (tree_classdef_enum_list&);
+
+    virtual void visit_classdef_enum_block (tree_classdef_enum_block&);
+
+    virtual void visit_classdef_body (tree_classdef_body&);
+
+    virtual void visit_classdef (tree_classdef&);
+  };
+}
+
+#endif
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-id.cc
--- a/libinterp/parse-tree/pt-id.cc	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-id.cc	Sun Jan 09 17:54:16 2022 +0100
@@ -117,6 +117,14 @@
     return new_id;
   }
 
+  void
+  tree_identifier::clear_cached_function ()
+  {
+    m_cached_function = octave_value {};
+    m_n_updated.reset ();
+    m_cached_args.clear ();
+  }
+
   octave_value_list
   tree_identifier::evaluate_n (tree_evaluator& tw, int nargout)
   {
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-id.h
--- a/libinterp/parse-tree/pt-id.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-id.h	Sun Jan 09 17:54:16 2022 +0100
@@ -129,12 +129,9 @@
     octave_value cached_function () const { return m_cached_function; }
     void set_cached_function (octave_value ov, const octave_value_list &args);
 
-    void clear_cached_function ()
-    {
-      m_cached_function = octave_value {};
-      m_n_updated.reset ();
-      m_cached_args.clear ();
-    }
+    // We dont want this inlined in idx.cc so we put in the .cc file even
+    // though it is short
+    void clear_cached_function ();
 
     std::weak_ptr<int> get_lp_n_updated () const { return m_n_updated; }
 
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-idx.cc
--- a/libinterp/parse-tree/pt-idx.cc	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-idx.cc	Sun Jan 09 17:54:16 2022 +0100
@@ -139,6 +139,70 @@
     return m_expr->name ();
   }
 
+  const std::string &
+  tree_simple_index_expression::name (void) const
+  {
+    return m_expr->name ();
+  }
+
+  octave_function *
+  tree_simple_index_expression::find_fcn (tree_evaluator& tw,
+                                          octave_value_list &first_args)
+  {
+    tree_identifier *id = static_cast<tree_identifier *> (m_expr);
+
+    interpreter& interp = tw.get_interpreter ();
+    symbol_table& symtab = interp.get_symbol_table ();
+
+    octave_value val = symtab.find_function (id->name (), first_args);
+
+    if (val.is_function ())
+      {
+        id->set_cached_function (val, first_args);
+        return val.function_value (true);
+      }
+
+    return nullptr;
+  }
+
+  octave_value_list
+  tree_simple_index_expression::
+  handle_superclass (tree_evaluator& tw,
+                     octave_value base_expr_val,
+                     std::list<octave_value_list> &idx_list,
+                     int nargout)
+  {
+    // FIXME: we want this to only be a superclass constructor
+    // call Should we actually make a check for this or are all
+    // other types of calls already dealt with?
+
+    octave_function *fcn = base_expr_val.function_value ();
+
+    if (fcn)
+      {
+        try
+          {
+            // FIXME: is it possible for the IDX_LIST to have
+            // more than one element here?  Do we need to check?
+
+            octave_value_list final_args;
+
+            final_args = idx_list.front ();
+
+            // FIXME: Do we ever need the names of the arguments
+            // passed to FCN here?
+
+            return fcn->call (tw, nargout, final_args);
+          }
+        catch (index_exception& ie)
+          {
+            tw.final_index_error (ie, m_expr);
+          }
+      }
+
+    return octave_value_list ();
+  }
+
   std::string
   tree_index_expression::get_struct_index (
    tree_evaluator& tw,
@@ -164,6 +228,31 @@
     return fn;
   }
 
+  tree * tree_index_expression::fold ()
+  {
+    if (m_args.size () > 1 ||
+        m_type[0] != '(' ||
+        m_word_list_cmd ||
+        (m_dyn_field.size () && *m_dyn_field.begin ()) || // Non-null dyn field
+        m_arg_nm.size () > 1 ||
+        !m_expr->is_identifier ())
+      return this;
+
+    // Fold to a tree_simple_index_expression
+    auto *folded =
+      new tree_simple_index_expression (m_expr,
+                                        m_args.size () ? m_args[0] : nullptr,
+                                        *m_arg_nm.begin (),
+                                        line (), column ());
+
+    // Protect the members from the dtor
+    m_expr = nullptr;
+    if (m_args.size ())
+      m_args[0] = nullptr;
+
+    return static_cast<tree*> (folded);
+  }
+
   octave_lvalue
   tree_index_expression::lvalue (tree_evaluator& tw)
   {
@@ -237,6 +326,59 @@
     return retval;
   }
 
+  octave_lvalue
+  tree_simple_index_expression::lvalue (tree_evaluator& tw)
+  {
+    std::list<octave_value_list> idx;
+
+    int n = m_arg ? 1 : 0;
+
+    octave_lvalue retval = m_expr->lvalue (tw);
+
+    unwind_action
+      act ([&tw] (const octave_value& val,
+                  const std::string& index_type,
+                  const std::list<octave_value_list>& index_list)
+           {
+             tw.set_indexed_object (val);
+             tw.set_index_list (index_type, index_list);
+           }, tw.indexed_object (), tw.index_type (), tw.index_list ());
+
+    if (n)
+      {
+        tw.set_indexed_object (retval.value ());
+        tw.clear_index_list ();
+
+
+        octave_value_list tidx = tw.make_value_list (m_arg, *m_arg_nm);
+
+        tw.append_index_list ('(', tidx);
+        idx.push_back (tidx);
+
+        if (idx.back ().empty ())
+          error ("invalid empty index list");
+      }
+
+    retval.set_index ("(", idx);
+
+    return retval;
+  }
+
+  tree_simple_index_expression *
+  tree_simple_index_expression::dup (symbol_scope& scope) const
+  {
+    tree_simple_index_expression *new_idx_expr
+      = new tree_simple_index_expression (line (), column ());
+
+    new_idx_expr->m_expr = m_expr ? m_expr->dup (scope) : nullptr;
+
+    new_idx_expr->m_arg = m_arg ? m_arg->dup (scope) : nullptr;
+
+    new_idx_expr->m_arg_nm = m_arg_nm;
+
+    return new_idx_expr;
+  }
+
   tree_index_expression *
   tree_index_expression::dup (symbol_scope& scope) const
   {
@@ -480,18 +622,7 @@
       // Note: need new scope so that the following unwind action will
       // happen before we perform the final indexing for objects (for
       // example).
-      /*
-      unwind_action
-        act ([&tw] (const octave_value& val,
-                    const std::string& index_type,
-                    const std::list<octave_value_list>& index_list)
-             {
-               tw.set_indexed_object (val);
-               tw.set_index_list (index_type, index_list);
-             },
-             tw.indexed_object (),
-             tw.index_type (), tw.index_list ());
-*/
+
       unwind_reset_tw_indexed_obj obj {&tw, tw.indexed_object (),
                                        tw.index_type (), tw.index_list ()};
 
@@ -701,7 +832,169 @@
 
     return retval;
   }
-}
+
+  octave_value_list
+  tree_simple_index_expression::evaluate_n (tree_evaluator& tw, int nargout)
+  {
+
+    {
+      bool is_var = tw.is_variable (m_expr);
+
+      if (! is_var)
+        {
+          octave_value_list first_args;
+
+          if (m_arg && m_arg->length () > 0)
+            {
+              unwind_reset_tw_lvalue_list obj {&tw, tw.lvalue_list ()};
+
+              tw.set_lvalue_list (nullptr);
+
+              first_args = tw.convert_to_const_vector (m_arg);
+
+              first_args.stash_name_tags (m_arg_nm);
+            }
+
+          octave_function *fcn = nullptr;
+
+          tree_identifier *id = static_cast<tree_identifier *> (m_expr);
+
+          bool using_cache = false;
+          if (OCTAVE_LIKELY (id->has_cached_function (first_args)))
+            {
+              std::weak_ptr<int> w = id->get_lp_n_updated();
+
+              if (std::shared_ptr<int> s = w.lock ())
+                {
+                  octave_value val = id->cached_function ();
+                  fcn = val.function_value (true);
+                  using_cache = true;
+                }
+              else
+                {
+                  id->clear_cached_function ();
+                }
+            }
+
+          if (OCTAVE_UNLIKELY (!using_cache))
+            {
+              fcn = find_fcn (tw, first_args);
+            }
+
+          if (OCTAVE_LIKELY (fcn))
+            {
+              try
+                {
+                  octave_value_list retval_fcn = fcn->call (tw, nargout,
+                                                            first_args);
+                  first_args = octave_value_list ();
+                  return retval_fcn;
+                }
+              catch (index_exception& ie)
+                {
+                  tw.final_index_error (ie, m_expr);
+                }
+            }
+        }
+    }
+
+    octave_value base_expr_val = m_expr->evaluate (tw);
+
+    // If we are indexing an object or looking at something like
+    //
+    //   classname.static_function (args, ...);
+    //
+    // then we'll just build a complete index list for one big subsref
+    // call.  If the expression we are indexing is a classname then
+    // base_expr_val will be an octave_classdef_meta object.  If we have
+    // files in a +packagename folder, they will also be an
+    // octave_classdef_meta object, but we don't want to index them.
+
+    std::list<octave_value_list> idx_list;
+
+    octave_value_list retval;
+
+    if (m_arg)
+      {
+        {
+          // Note: need new scope so that the following unwind action will
+          // happen before we perform the final indexing for objects (for
+          // example).
+
+          unwind_reset_tw_indexed_obj obj {&tw, tw.indexed_object (),
+                                           tw.index_type (), tw.index_list ()};
+
+          tw.set_indexed_object ();
+          tw.clear_index_list ();
+
+          tw.set_indexed_object (base_expr_val);
+
+          octave_value_list tmp = tw.make_value_list (m_arg,
+                                                      *m_arg_nm);
+          tw.append_index_list ('(', tmp);
+          idx_list.push_back (tmp);
+        }
+
+        // This is for +package and other classdef_meta objects
+        if (OCTAVE_LIKELY (! base_expr_val.is_function ()
+                           || base_expr_val.is_classdef_meta ()))
+          {
+            try
+              {
+                retval = base_expr_val.subsref ("(",
+                                                idx_list, nargout);
+                idx_list.clear ();
+              }
+            catch (index_exception& ie)
+              {
+                tw.final_index_error (ie, m_expr);
+              }
+          }
+        else
+          {
+            retval = handle_superclass (tw, base_expr_val, idx_list, nargout);
+          }
+      }
+
+    // FIXME: when can the following happen?  In what case does indexing
+    // result in a value that is a function?  Classdef method calls?
+    // Something else?
+
+    bool is_fcn = (retval.length () ? retval(0).is_function() : false);
+
+    if (OCTAVE_LIKELY (!is_fcn))
+      {
+        idx_list.clear ();
+        base_expr_val = octave_value ();
+
+        return retval;
+      }
+    else
+      {
+        octave_value val = retval(0);
+        octave_function *fcn = val.function_value (true);
+
+        if (fcn)
+          {
+            octave_value_list final_args;
+
+            if (! idx_list.empty ())
+              final_args = idx_list.front ();
+
+            retval = fcn->call (tw, nargout, final_args);
+          }
+
+        idx_list.clear ();
+        base_expr_val = octave_value ();
+        val = octave_value ();
+
+        return retval;
+      }
+  }
+
+} // namespace octave
+
+
 
 /*
 %!test
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-idx.h
--- a/libinterp/parse-tree/pt-idx.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-idx.h	Sun Jan 09 17:54:16 2022 +0100
@@ -125,6 +125,8 @@
      std::list<std::shared_ptr<string_vector>>::const_iterator p_arg_nm,
      std::list<tree_expression *>::const_iterator p_dyn_field) const;
 
+    tree *fold ();
+
   private:
 
     // The LHS of this index expression.
@@ -150,6 +152,72 @@
 
     octave_map make_arg_struct (void) const;
   };
+
+  class tree_simple_index_expression : public tree_expression
+  {
+  public:
+    tree_simple_index_expression (tree_expression *e,
+                                  tree_argument_list *lst,
+                                  std::shared_ptr<string_vector> arg_nm,
+                                  int l, int c)
+      : tree_expression (l, c), m_expr (e), m_arg (lst), m_arg_nm (arg_nm)
+    {}
+
+    tree_simple_index_expression (int l, int c)
+      : tree_expression (l, c) {}
+
+    // No copying!
+
+    tree_simple_index_expression (const tree_simple_index_expression&) = delete;
+
+    tree_simple_index_expression&
+    operator = (const tree_simple_index_expression&) = delete;
+
+    bool lvalue_ok (void) const { return m_expr->lvalue_ok (); }
+
+    bool rvalue_ok (void) const { return true; }
+
+    void accept (tree_walker& tw)
+    {
+      tw.visit_simple_index_expression (*this);
+    }
+
+    tree_expression * expression (void) { return m_expr; }
+
+    octave_value evaluate (tree_evaluator& tw, int nargout = 1)
+    {
+      octave_value_list retval = evaluate_n (tw, nargout);
+
+      return retval.length () > 0 ? retval(0) : octave_value ();
+    }
+
+    tree_argument_list * arg () const { return m_arg; }
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1);
+
+    tree_simple_index_expression * dup (symbol_scope& scope) const;
+
+    const std::string & name (void) const;
+
+    octave_lvalue lvalue (tree_evaluator& tw);
+
+  private:
+    // We have these two helper functions to keep the code path short
+    // in evaluate_n ()
+    octave_function * __attribute__ ((noinline,noclone))
+    find_fcn (tree_evaluator& tw, octave_value_list &first_args);
+
+    octave_value_list __attribute__ ((noinline,noclone))
+    handle_superclass (tree_evaluator& tw, octave_value base_expr_val,
+                       std::list<octave_value_list> &idx_list, int nargout);
+
+    // The LHS of this index expression.
+    tree_expression *m_expr;
+
+    tree_argument_list * m_arg;
+
+    std::shared_ptr<string_vector> m_arg_nm;
+  };
 }
 
 #endif
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-walk.cc
--- a/libinterp/parse-tree/pt-walk.cc	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-walk.cc	Sun Jan 09 17:54:16 2022 +0100
@@ -369,6 +369,19 @@
       list->accept (*this);
   }
 
+  void tree_walker::
+  visit_simple_index_expression (tree_simple_index_expression& expr)
+  {
+    tree_expression *e = expr.expression ();
+
+    if (e)
+      e->accept (*this);
+
+    tree_argument_list *arg = expr.arg ();
+    if (arg)
+      arg->accept (*this);
+  }
+
   void tree_walker::visit_index_expression (tree_index_expression& expr)
   {
     tree_expression *e = expr.expression ();
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt-walk.h
--- a/libinterp/parse-tree/pt-walk.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt-walk.h	Sun Jan 09 17:54:16 2022 +0100
@@ -80,6 +80,7 @@
   class tree_prefix_expression;
   class tree_return_command;
   class tree_simple_assignment;
+  class tree_simple_index_expression;
   class tree_statement;
   class tree_statement_cmd;
   class tree_statement_expression;
@@ -188,6 +189,8 @@
 
     virtual void visit_index_expression (tree_index_expression&);
 
+    virtual void visit_simple_index_expression (tree_simple_index_expression&);
+
     virtual void visit_matrix (tree_matrix&);
 
     virtual void visit_cell (tree_cell&);
diff -r 3849d8664922 -r f70c5dd1bb09 libinterp/parse-tree/pt.h
--- a/libinterp/parse-tree/pt.h	Sat Jan 08 14:48:34 2022 +0100
+++ b/libinterp/parse-tree/pt.h	Sun Jan 09 17:54:16 2022 +0100
@@ -112,6 +112,8 @@
 
     virtual void accept (tree_walker& tw) = 0;
 
+    virtual tree *fold () { return this; }
+
   private:
 
     // The input line and column where we found the text that was
