changeset 27371:fcaecdbc8d8a

don't use visitor pattern for expression evaluation (bug #56752) Although it is desirable to have all parse tree evaluation functions grouped together in a single file, using the visitor pattern can be inefficient, especially when the visitor function is small and the extra levels of indirection and virtual function resolution can take more time than the evaluation function itself (evaluation of constants, for example). For all classes derived from tree_expression, introduce new evaluate and evaluate_n methods. Use those instead of visit_CLASS functions to perform expression evaluation. Results are now returned directly from the evaluation functions instead of storing them in the tree_evaluator object. Files affected: cdef-class.cc, oct-parse.yy, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-classdef.cc, pt-classdef.h, pt-colon.cc, pt-colon.h, pt-const.h, pt-eval.cc, pt-eval.h, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-loop.cc, pt-mat.cc, pt-mat.h, pt-select.cc, pt-tm-const.cc, pt-unop.cc, and pt-unop.h.
author John W. Eaton <jwe@octave.org>
date Fri, 30 Aug 2019 15:02:14 -0400
parents a2d3fa82b730
children 2f8428b61bd6
files libinterp/octave-value/cdef-class.cc libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/pt-assign.cc libinterp/parse-tree/pt-assign.h libinterp/parse-tree/pt-binop.cc libinterp/parse-tree/pt-binop.h libinterp/parse-tree/pt-cbinop.cc libinterp/parse-tree/pt-cbinop.h libinterp/parse-tree/pt-cell.cc libinterp/parse-tree/pt-cell.h libinterp/parse-tree/pt-classdef.cc libinterp/parse-tree/pt-classdef.h libinterp/parse-tree/pt-colon.cc libinterp/parse-tree/pt-colon.h libinterp/parse-tree/pt-const.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-eval.h libinterp/parse-tree/pt-exp.h libinterp/parse-tree/pt-fcn-handle.cc libinterp/parse-tree/pt-fcn-handle.h libinterp/parse-tree/pt-id.cc libinterp/parse-tree/pt-id.h libinterp/parse-tree/pt-idx.cc libinterp/parse-tree/pt-idx.h libinterp/parse-tree/pt-loop.cc libinterp/parse-tree/pt-mat.cc libinterp/parse-tree/pt-mat.h libinterp/parse-tree/pt-select.cc libinterp/parse-tree/pt-tm-const.cc libinterp/parse-tree/pt-unop.cc libinterp/parse-tree/pt-unop.h
diffstat 31 files changed, 1443 insertions(+), 1398 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/cdef-class.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/octave-value/cdef-class.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -827,7 +827,7 @@
               return std::string ("private");
           }
 
-        return tw.evaluate (expr);
+        return expr->evaluate (tw);
       }
     else
       return octave_value (true);
@@ -1102,7 +1102,7 @@
                     tree_expression *expr = prop_p->expression ();
                     if (expr)
                       {
-                        octave_value pvalue = tw.evaluate (expr);
+                        octave_value pvalue = expr->evaluate (tw);
 
 #if DEBUG_TRACE
                         std::cerr << "property default: "
--- a/libinterp/parse-tree/oct-parse.yy	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/oct-parse.yy	Fri Aug 30 15:02:14 2019 -0400
@@ -2535,7 +2535,7 @@
           {
             tree_evaluator& tw = interp.get_evaluator ();
 
-            octave_value tmp = tw.evaluate (e);
+            octave_value tmp = e->evaluate (tw);
 
             tree_constant *tc_retval
               = new tree_constant (tmp, e->line (), e->column ());
@@ -4112,7 +4112,7 @@
         tree_evaluator& tw
           = __get_evaluator__ ("validate_matrix_for_assignment");
 
-        octave_value ov = tw.evaluate (e);
+        octave_value ov = e->evaluate (tw);
 
         delete e;
 
@@ -4187,7 +4187,7 @@
           {
             tree_evaluator& tw = interp.get_evaluator ();
 
-            octave_value tmp = tw.evaluate (array_list);
+            octave_value tmp = array_list->evaluate (tw);
 
             tree_constant *tc_retval
               = new tree_constant (tmp, array_list->line (),
--- a/libinterp/parse-tree/pt-assign.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-assign.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -27,7 +27,9 @@
 #include <string>
 
 #include "error.h"
+#include "oct-lvalue.h"
 #include "ov.h"
+#include "parse.h"
 #include "pt-arg-list.h"
 #include "pt-assign.h"
 
@@ -70,6 +72,79 @@
     return new_sa;
   }
 
+  octave_value
+  tree_simple_assignment::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    if (m_rhs)
+      {
+        try
+          {
+            unwind_protect frame;
+
+            octave_lvalue ult = m_lhs->lvalue (tw);
+
+            std::list<octave_lvalue> lvalue_list;
+            lvalue_list.push_back (ult);
+
+            frame.add_method (tw, &tree_evaluator::set_lvalue_list,
+                              tw.lvalue_list ());
+            tw.set_lvalue_list (&lvalue_list);
+
+            if (ult.numel () != 1)
+              err_invalid_structure_assignment ();
+
+            octave_value rhs_val = m_rhs->evaluate (tw);
+
+            if (rhs_val.is_undefined ())
+              error ("value on right hand side of assignment is undefined");
+
+            if (rhs_val.is_cs_list ())
+              {
+                const octave_value_list lst = rhs_val.list_value ();
+
+                if (lst.empty ())
+                  error ("invalid number of elements on RHS of assignment");
+
+                rhs_val = lst(0);
+              }
+
+            ult.assign (m_etype, rhs_val);
+
+            if (m_etype == octave_value::op_asn_eq)
+              val = rhs_val;
+            else
+              val = ult.value ();
+
+            if (print_result () && tw.statement_printing_enabled ())
+              {
+                // We clear any index here so that we can
+                // get the new value of the referenced
+                // object below, instead of the indexed
+                // value (which should be the same as the
+                // right hand side value).
+
+                ult.clear_index ();
+
+                octave_value lhs_val = ult.value ();
+
+                octave_value_list args = ovl (lhs_val);
+                args.stash_name_tags (string_vector (m_lhs->name ()));
+                feval ("display", args);
+              }
+          }
+        catch (index_exception& e)
+          {
+            e.set_var (m_lhs->name ());
+            std::string msg = e.message ();
+            error_with_id (e.err_id (), "%s", msg.c_str ());
+          }
+      }
+
+    return val;
+  }
+
   // Multi-valued assignment expressions.
 
   tree_multi_assignment::tree_multi_assignment (tree_argument_list *lst,
@@ -98,6 +173,167 @@
     panic_impossible ();
     return nullptr;
   }
+
+  octave_value_list
+  tree_multi_assignment::evaluate_n (tree_evaluator& tw, int)
+  {
+    octave_value_list val;
+
+    if (m_rhs)
+      {
+        unwind_protect frame;
+
+        std::list<octave_lvalue> lvalue_list = tw.make_lvalue_list (m_lhs);
+
+        frame.add_method (tw, &tree_evaluator::set_lvalue_list,
+                          tw.lvalue_list ());
+        tw.set_lvalue_list (&lvalue_list);
+
+        octave_idx_type n_out = 0;
+
+        for (const auto& lval : lvalue_list)
+          n_out += lval.numel ();
+
+        // The following trick is used to keep rhs_val constant.
+        const octave_value_list rhs_val1 = m_rhs->evaluate_n (tw, n_out);
+        const octave_value_list rhs_val = (rhs_val1.length () == 1
+                                           && rhs_val1(0).is_cs_list ()
+                                           ? rhs_val1(0).list_value ()
+                                           : rhs_val1);
+
+        octave_idx_type k = 0;
+
+        octave_idx_type n = rhs_val.length ();
+
+        // To avoid copying per elements and possible optimizations, we
+        // postpone joining the final values.
+        std::list<octave_value_list> retval_list;
+
+        auto q = m_lhs->begin ();
+
+        for (octave_lvalue ult : lvalue_list)
+          {
+            tree_expression *lhs_elt = *q++;
+
+            octave_idx_type nel = ult.numel ();
+
+            if (nel != 1)
+              {
+                // Huge kluge so that wrapper scripts with lines like
+                //
+                //   [varargout{1:nargout}] = fcn (args);
+                //
+                // Will work the same as calling fcn directly when nargout
+                // is 0 and fcn produces more than one output even when
+                // nargout is 0.  This only works if varargout has not yet
+                // been defined.  See also bug #43813.
+
+                if (lvalue_list.size () == 1 && nel == 0 && n > 0
+                    && ! ult.is_black_hole () && ult.is_undefined ()
+                    && ult.index_type () == "{" && ult.index_is_empty ())
+                  {
+                    // Convert undefined lvalue with empty index to a cell
+                    // array with a single value and indexed by 1 to
+                    // handle a single output.
+
+                    nel = 1;
+
+                    ult.define (Cell (1, 1));
+
+                    ult.clear_index ();
+                    std::list<octave_value_list> idx;
+                    idx.push_back (octave_value_list (octave_value (1)));
+                    ult.set_index ("{", idx);
+                  }
+
+                if (k + nel > n)
+                  error ("some elements undefined in return list");
+
+                // This element of the return list expects a
+                // comma-separated list of values.  Slicing avoids
+                // copying.
+
+                octave_value_list ovl = rhs_val.slice (k, nel);
+
+                ult.assign (octave_value::op_asn_eq, octave_value (ovl));
+
+                retval_list.push_back (ovl);
+
+                k += nel;
+              }
+            else
+              {
+                if (k < n)
+                  {
+                    if (ult.is_black_hole ())
+                      {
+                        k++;
+                        continue;
+                      }
+                    else
+                      {
+                        octave_value tmp = rhs_val(k);
+
+                        if (tmp.is_undefined ())
+                          error ("element number %" OCTAVE_IDX_TYPE_FORMAT
+                                 " undefined in return list", k+1);
+
+                        ult.assign (octave_value::op_asn_eq, tmp);
+
+                        retval_list.push_back (tmp);
+
+                        k++;
+                      }
+                  }
+                else
+                  {
+                    // This can happen for a function like
+                    //
+                    //   function varargout = f ()
+                    //     varargout{1} = nargout;
+                    //   endfunction
+                    //
+                    // called with
+                    //
+                    //    [a, ~] = f ();
+                    //
+                    // Then the list of of RHS values will contain one
+                    // element but we are iterating over the list of all
+                    // RHS values.  We shouldn't complain that a value we
+                    // don't need is missing from the list.
+
+                    if (! ult.is_black_hole ())
+                      error ("element number %" OCTAVE_IDX_TYPE_FORMAT
+                             " undefined in return list", k+1);
+
+                    k++;
+                    continue;
+                  }
+              }
+
+            if (print_result () && tw.statement_printing_enabled ())
+              {
+                // We clear any index here so that we can get
+                // the new value of the referenced object below,
+                // instead of the indexed value (which should be
+                // the same as the right hand side value).
+
+                ult.clear_index ();
+
+                octave_value lhs_val = ult.value ();
+
+                octave_value_list args = ovl (lhs_val);
+                args.stash_name_tags (string_vector (lhs_elt->name ()));
+                feval ("display", args);
+              }
+          }
+
+        // Concatenate return values.
+        val = retval_list;
+      }
+
+    return val;
+  }
 }
 
 /*
--- a/libinterp/parse-tree/pt-assign.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-assign.h	Fri Aug 30 15:02:14 2019 -0400
@@ -79,6 +79,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator& tw, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_simple_assignment (*this);
@@ -148,6 +155,15 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    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 ();
+    }
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1);
+
     void accept (tree_walker& tw)
     {
       tw.visit_multi_assignment (*this);
--- a/libinterp/parse-tree/pt-binop.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-binop.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -25,8 +25,11 @@
 #endif
 
 #include "error.h"
+#include "interpreter.h"
 #include "ov.h"
+#include "profiler.h"
 #include "pt-binop.h"
+#include "pt-eval.h"
 #include "variables.h"
 
 namespace octave
@@ -62,6 +65,83 @@
     return new_be;
   }
 
+  octave_value
+  tree_binary_expression::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    if (is_eligible_for_braindead_shortcircuit ())
+      {
+        if (m_lhs)
+          {
+            octave_value a = std::move (m_lhs->evaluate (tw));
+
+            if (a.ndims () == 2 && a.rows () == 1 && a.columns () == 1)
+              {
+                bool result = false;
+
+                bool a_true = a.is_true ();
+
+                if (a_true)
+                  {
+                    if (m_etype == octave_value::op_el_or)
+                      {
+                        matlab_style_short_circuit_warning ("|");
+                        return octave_value (true);
+                      }
+                  }
+                else
+                  {
+                    if (m_etype == octave_value::op_el_and)
+                      {
+                        matlab_style_short_circuit_warning ("&");
+                        return octave_value (false);
+                      }
+                  }
+
+                if (m_rhs)
+                  {
+                    octave_value b = std::move (m_rhs->evaluate (tw));
+
+                    result = b.is_true ();
+                  }
+
+                return octave_value (result);
+              }
+          }
+      }
+
+    if (m_lhs)
+      {
+        octave_value a = std::move (m_lhs->evaluate (tw));
+
+        if (a.is_defined () && m_rhs)
+          {
+            octave_value b = std::move (m_rhs->evaluate (tw));
+
+            if (b.is_defined ())
+              {
+                profiler::enter<tree_binary_expression>
+                  block (tw.get_profiler (), *this);
+
+                // Note: The profiler does not catch the braindead
+                // short-circuit evaluation code above, but that should be
+                // ok.  The evaluation of operands and the operator itself
+                // is entangled and it's not clear where to start/stop
+                // timing the operator to make it reasonable.
+
+                interpreter& interp = tw.get_interpreter ();
+
+                type_info& ti = interp.get_type_info ();
+
+                val = ::do_binary_op (ti, m_etype, a, b);
+              }
+          }
+      }
+
+    return val;
+  }
+
   // Boolean expressions.
 
   std::string
@@ -98,4 +178,46 @@
 
     return new_be;
   }
+
+  octave_value
+  tree_boolean_expression::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    bool result = false;
+
+    // This evaluation is not caught by the profiler, since we can't find
+    // a reasonable place where to time.  Note that we don't want to
+    // include evaluation of LHS or RHS into the timing, but this is
+    // entangled together with short-circuit evaluation here.
+
+    if (m_lhs)
+      {
+        octave_value a = std::move (m_lhs->evaluate (tw));
+
+        bool a_true = a.is_true ();
+
+        if (a_true)
+          {
+            if (m_etype == tree_boolean_expression::bool_or)
+              return octave_value (true);
+          }
+        else
+          {
+            if (m_etype == tree_boolean_expression::bool_and)
+              return octave_value (false);
+          }
+
+        if (m_rhs)
+          {
+            octave_value b = std::move (m_rhs->evaluate (tw));
+
+            result = b.is_true ();
+          }
+
+        val = octave_value (result);
+      }
+
+    return val;
+  }
 }
--- a/libinterp/parse-tree/pt-binop.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-binop.h	Fri Aug 30 15:02:14 2019 -0400
@@ -106,6 +106,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_binary_expression (*this);
@@ -173,6 +180,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_boolean_expression (*this);
--- a/libinterp/parse-tree/pt-cbinop.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-cbinop.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -24,12 +24,41 @@
 #  include "config.h"
 #endif
 
+#include "interpreter.h"
 #include "ov.h"
 #include "pt-cbinop.h"
+#include "pt-eval.h"
 #include "pt-unop.h"
 
 namespace octave
 {
+  octave_value
+  tree_compound_binary_expression::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    if (m_lhs)
+      {
+        octave_value a = std::move (m_lhs->evaluate (tw));
+
+        if (a.is_defined () && m_rhs)
+          {
+            octave_value b = std::move (m_rhs->evaluate (tw));
+
+            if (b.is_defined ())
+              {
+                interpreter& interp = tw.get_interpreter ();
+
+                type_info& ti = interp.get_type_info ();
+
+                val = ::do_binary_op (ti, m_etype, a, b);
+              }
+          }
+      }
+
+    return val;
+  }
+
   typedef tree_expression* tree_expression_ptr_t;
 
   // If a tree expression is a transpose or hermitian transpose, return
--- a/libinterp/parse-tree/pt-cbinop.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-cbinop.h	Fri Aug 30 15:02:14 2019 -0400
@@ -58,6 +58,13 @@
     tree_expression * clhs (void) { return m_lhs; }
     tree_expression * crhs (void) { return m_rhs; }
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_compound_binary_expression (*this);
--- a/libinterp/parse-tree/pt-cell.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-cell.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -27,6 +27,7 @@
 #include "Cell.h"
 #include "ovl.h"
 #include "pt-arg-list.h"
+#include "pt-eval.h"
 #include "pt-exp.h"
 #include "pt-cell.h"
 #include "pt-walk.h"
@@ -43,4 +44,58 @@
 
     return new_cell;
   }
+
+  octave_value
+  tree_cell::evaluate (tree_evaluator& tw, int)
+  {
+    unwind_protect frame;
+
+    frame.add_method (tw, &tree_evaluator::set_lvalue_list,
+                      tw.lvalue_list ());
+    tw.set_lvalue_list (nullptr);
+
+    octave_idx_type nr = length ();
+    octave_idx_type nc = -1;
+
+    Cell val;
+
+    octave_idx_type i = 0;
+
+    for (tree_argument_list *elt : *this)
+      {
+        octave_value_list row = tw.convert_to_const_vector (elt);
+
+        if (nr == 1)
+          // Optimize the single row case.
+          val = row.cell_value ();
+        else if (nc < 0)
+          {
+            nc = row.length ();
+
+            val = Cell (nr, nc);
+          }
+        else
+          {
+            octave_idx_type this_nc = row.length ();
+
+            if (this_nc != nc)
+              {
+                if (this_nc == 0)
+                  continue;  // blank line
+                else
+                  error ("number of columns must match");
+              }
+          }
+
+        for (octave_idx_type j = 0; j < nc; j++)
+          val(i,j) = row(j);
+
+        i++;
+      }
+
+    if (i < nr)
+      val.resize (dim_vector (i, nc));  // there were blank rows
+
+    return octave_value (val);
+  }
 }
--- a/libinterp/parse-tree/pt-cell.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-cell.h	Fri Aug 30 15:02:14 2019 -0400
@@ -62,6 +62,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_cell (*this);
--- a/libinterp/parse-tree/pt-classdef.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-classdef.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -24,8 +24,10 @@
 #  include "config.h"
 #endif
 
+#include "ov.h"
 #include "ov-classdef.h"
 #include "pt-classdef.h"
+#include "pt-eval.h"
 
 namespace octave
 {
@@ -41,6 +43,32 @@
     return new_scr;
   }
 
+  octave_value_list
+  tree_superclass_ref::evaluate_n (tree_evaluator& tw, int nargout)
+  {
+    octave_value tmp
+      = octave_classdef::superclass_ref (m_method_name, m_class_name);
+
+    if (! is_postfix_indexed ())
+      {
+        // There was no index, so this superclass_ref object is not
+        // part of an index expression.  It is also not an identifier in
+        // the syntax tree but we need to handle it as if it were.  So
+        // call the function here.
+
+        octave_function *f = tmp.function_value (true);
+
+        assert (f);
+
+        return f->call (tw, nargout);
+      }
+
+    // The superclass_ref function object will be indexed as part of the
+    // enclosing index expression.
+
+    return ovl (tmp);
+  }
+
   tree_metaclass_query *
   tree_metaclass_query::dup (symbol_scope&) const
   {
@@ -52,6 +80,12 @@
     return new_mcq;
   }
 
+  octave_value
+  tree_metaclass_query::evaluate (tree_evaluator&, int)
+  {
+    return octave_classdef::metaclass_query (m_class_name);
+  }
+
   // Classdef attribute
 
   // Classdef attribute_list
--- a/libinterp/parse-tree/pt-classdef.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-classdef.h	Fri Aug 30 15:02:14 2019 -0400
@@ -69,6 +69,15 @@
 
     tree_superclass_ref * dup (symbol_scope& scope) const;
 
+    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 ();
+    }
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1);
+
     void accept (tree_walker& tw)
     {
       tw.visit_superclass_ref (*this);
@@ -107,6 +116,13 @@
 
     tree_metaclass_query * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_metaclass_query (*this);
--- a/libinterp/parse-tree/pt-colon.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-colon.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -24,7 +24,10 @@
 #  include "config.h"
 #endif
 
+#include "interpreter.h"
+#include "parse.h"
 #include "pt-colon.h"
+#include "pt-eval.h"
 
 namespace octave
 {
@@ -43,4 +46,60 @@
 
     return new_ce;
   }
+
+  octave_value tree_colon_expression::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    if (! m_base || ! m_limit)
+      return val;
+
+    octave_value ov_base = std::move (m_base->evaluate (tw));
+
+    octave_value ov_limit = std::move (m_limit->evaluate (tw));
+
+    if (ov_base.isobject () || ov_limit.isobject ())
+      {
+        octave_value_list tmp1;
+
+        if (m_increment)
+          {
+            octave_value ov_increment = std::move (m_increment->evaluate (tw));
+
+            tmp1(2) = ov_limit;
+            tmp1(1) = ov_increment;
+            tmp1(0) = ov_base;
+          }
+        else
+          {
+            tmp1(1) = ov_limit;
+            tmp1(0) = ov_base;
+          }
+
+        interpreter& interp = tw.get_interpreter ();
+
+        symbol_table& symtab = interp.get_symbol_table ();
+
+        octave_value fcn = symtab.find_function ("colon", tmp1);
+
+        if (! fcn.is_defined ())
+          error ("can not find overloaded colon function");
+
+        octave_value_list tmp2 = feval (fcn, tmp1, 1);
+
+        val = tmp2 (0);
+      }
+    else
+      {
+        octave_value ov_increment = 1.0;
+
+        if (m_increment)
+          ov_increment = std::move (m_increment->evaluate (tw));
+
+        val = do_colon_op (ov_base, ov_increment, ov_limit,
+                           is_for_cmd_expr ());
+      }
+
+    return val;
+  }
 }
--- a/libinterp/parse-tree/pt-colon.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-colon.h	Fri Aug 30 15:02:14 2019 -0400
@@ -93,6 +93,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_colon_expression (*this);
--- a/libinterp/parse-tree/pt-const.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-const.h	Fri Aug 30 15:02:14 2019 -0400
@@ -30,6 +30,7 @@
 
 class octave_value_list;
 
+#include "error.h"
 #include "ov.h"
 #include "pt-bp.h"
 #include "pt-exp.h"
@@ -38,6 +39,7 @@
 namespace octave
 {
   class symbol_scope;
+  class tree_evaluator;
 
   class tree_constant : public tree_expression
   {
@@ -96,6 +98,19 @@
 
     std::string original_text (void) const { return m_orig_text; }
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1)
+    {
+      if (nargout > 1)
+        error ("invalid number of output arguments for constant expression");
+
+      return value ();
+    }
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
   private:
 
     // The actual value that this constant refers to.
--- a/libinterp/parse-tree/pt-eval.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -551,7 +551,7 @@
                     if (silent)
                       expr->set_print_flag (false);
 
-                    retval = evaluate_n (expr, nargout);
+                    retval = std::move (expr->evaluate_n (*this, nargout));
 
                     bool do_bind_ans = false;
 
@@ -762,87 +762,9 @@
   }
 
   void
-  tree_evaluator::visit_anon_fcn_handle (tree_anon_fcn_handle& anon_fh)
+  tree_evaluator::visit_anon_fcn_handle (tree_anon_fcn_handle&)
   {
-    // FIXME: should CMD_LIST be limited to a single expression?
-    // I think that is what Matlab does.
-
-    tree_parameter_list *param_list = anon_fh.parameter_list ();
-    tree_expression *expr = anon_fh.expression ();
-
-    symbol_scope af_scope = anon_fh.scope ();
-
-    symbol_scope new_scope;
-    if (af_scope)
-      new_scope = af_scope.dup ();
-
-    tree_parameter_list *param_list_dup
-      = param_list ? param_list->dup (new_scope) : nullptr;
-
-    tree_parameter_list *ret_list = nullptr;
-
-    tree_statement_list *stmt_list = nullptr;
-
-    symbol_scope parent_scope = get_current_scope ();
-
-    new_scope.set_parent (parent_scope);
-    new_scope.set_primary_parent (parent_scope);
-
-    if (expr)
-      {
-        tree_expression *expr_dup = expr->dup (new_scope);
-        tree_statement *stmt = new tree_statement (expr_dup, nullptr);
-        stmt_list = new tree_statement_list (stmt);
-      }
-
-    tree_anon_scopes anon_fcn_ctx (anon_fh);
-
-    std::set<std::string> free_vars = anon_fcn_ctx.free_variables ();
-
-    octave_user_function::local_vars_map local_var_init_vals;
-
-    stack_frame& frame = m_call_stack.get_current_stack_frame ();
-
-    for (auto& name : free_vars)
-      {
-        octave_value val = frame.varval (name);
-
-        if (val.is_defined ())
-          local_var_init_vals[name] = val;
-      }
-
-    octave_user_function *af
-      = new octave_user_function (new_scope, param_list_dup, ret_list,
-                                  stmt_list, local_var_init_vals);
-
-    octave_function *curr_fcn = m_call_stack.current ();
-
-    if (curr_fcn)
-      {
-        // FIXME: maybe it would be better to just stash curr_fcn
-        // instead of individual bits of info about it?
-
-        af->stash_parent_fcn_name (curr_fcn->name ());
-        af->stash_dir_name (curr_fcn->dir_name ());
-
-        // The following is needed so that class method dispatch works
-        // properly for anonymous functions that wrap class methods.
-
-        if (curr_fcn->is_class_method () || curr_fcn->is_class_constructor ())
-          af->stash_dispatch_class (curr_fcn->dispatch_class ());
-
-        af->stash_fcn_file_name (curr_fcn->fcn_file_name ());
-      }
-
-    af->mark_as_anonymous_function ();
-
-    octave_value ov_fcn (af);
-
-    // octave_value fh (octave_fcn_binder::maybe_binder (ov_fcn, m_interpreter));
-
-    octave_value fh (new octave_fcn_handle (ov_fcn, octave_fcn_handle::anonymous));
-
-    push_result (fh);
+    panic_impossible ();
   }
 
   void
@@ -852,170 +774,21 @@
   }
 
   void
-  tree_evaluator::visit_binary_expression (tree_binary_expression& expr)
+  tree_evaluator::visit_binary_expression (tree_binary_expression&)
   {
-    octave_value val;
-
-    tree_expression *op_lhs = expr.lhs ();
-    tree_expression *op_rhs = expr.rhs ();
-    octave_value::binary_op etype = expr.op_type ();
-
-    if (expr.is_eligible_for_braindead_shortcircuit ())
-      {
-        if (op_lhs)
-          {
-            octave_value a = evaluate (op_lhs);
-
-            if (a.ndims () == 2 && a.rows () == 1 && a.columns () == 1)
-              {
-                bool result = false;
-
-                bool a_true = a.is_true ();
-
-                if (a_true)
-                  {
-                    if (etype == octave_value::op_el_or)
-                      {
-                        expr.matlab_style_short_circuit_warning ("|");
-                        push_result (octave_value (true));
-                        return;
-                      }
-                  }
-                else
-                  {
-                    if (etype == octave_value::op_el_and)
-                      {
-                        expr.matlab_style_short_circuit_warning ("&");
-                        push_result (octave_value (false));
-                        return;
-                      }
-                  }
-
-                if (op_rhs)
-                  {
-                    octave_value b = evaluate (op_rhs);
-
-                    result = b.is_true ();
-                  }
-
-                push_result (octave_value (result));
-                return;
-              }
-          }
-      }
-
-    if (op_lhs)
-      {
-        octave_value a = evaluate (op_lhs);
-
-        if (a.is_defined () && op_rhs)
-          {
-            octave_value b = evaluate (op_rhs);
-
-            if (b.is_defined ())
-              {
-                profiler::enter<tree_binary_expression>
-                  block (m_profiler, expr);
-
-                // Note: The profiler does not catch the braindead
-                // short-circuit evaluation code above, but that should be
-                // ok.  The evaluation of operands and the operator itself
-                // is entangled and it's not clear where to start/stop
-                // timing the operator to make it reasonable.
-
-                type_info& ti = m_interpreter.get_type_info ();
-
-                val = ::do_binary_op (ti, etype, a, b);
-              }
-          }
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_boolean_expression (tree_boolean_expression& expr)
+  tree_evaluator::visit_boolean_expression (tree_boolean_expression&)
   {
-    octave_value val;
-
-    bool result = false;
-
-    // This evaluation is not caught by the profiler, since we can't find
-    // a reasonable place where to time.  Note that we don't want to
-    // include evaluation of LHS or RHS into the timing, but this is
-    // entangled together with short-circuit evaluation here.
-
-    tree_expression *op_lhs = expr.lhs ();
-
-    if (op_lhs)
-      {
-        octave_value a = evaluate (op_lhs);
-
-        bool a_true = a.is_true ();
-
-        tree_boolean_expression::type etype = expr.op_type ();
-
-        if (a_true)
-          {
-            if (etype == tree_boolean_expression::bool_or)
-              {
-                push_result (octave_value (true));
-                return;
-              }
-          }
-        else
-          {
-            if (etype == tree_boolean_expression::bool_and)
-              {
-                push_result (octave_value (false));
-                return;
-              }
-          }
-
-        tree_expression *op_rhs = expr.rhs ();
-
-        if (op_rhs)
-          {
-            octave_value b = evaluate (op_rhs);
-
-            result = b.is_true ();
-          }
-
-        val = octave_value (result);
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_compound_binary_expression (tree_compound_binary_expression& expr)
+  tree_evaluator::visit_compound_binary_expression (tree_compound_binary_expression&)
   {
-    octave_value val;
-
-    tree_expression *op_lhs = expr.clhs ();
-
-    if (op_lhs)
-      {
-        octave_value a = evaluate (op_lhs);
-
-        tree_expression *op_rhs = expr.crhs ();
-
-        if (a.is_defined () && op_rhs)
-          {
-            octave_value b = evaluate (op_rhs);
-
-            if (b.is_defined ())
-              {
-                octave_value::compound_binary_op etype = expr.cop_type ();
-
-                type_info& ti = m_interpreter.get_type_info ();
-
-                val = ::do_binary_op (ti, etype, a, b);
-              }
-          }
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
@@ -1038,66 +811,9 @@
   }
 
   void
-  tree_evaluator::visit_colon_expression (tree_colon_expression& expr)
+  tree_evaluator::visit_colon_expression (tree_colon_expression&)
   {
-    octave_value val;
-
-    tree_expression *op_base = expr.base ();
-    tree_expression *op_limit = expr.limit ();
-
-    if (! op_base || ! op_limit)
-      {
-        push_result (octave_value (val));
-        return;
-      }
-
-    octave_value ov_base = evaluate (op_base);
-
-    octave_value ov_limit = evaluate (op_limit);
-
-    tree_expression *op_increment = expr.increment ();
-
-    if (ov_base.isobject () || ov_limit.isobject ())
-      {
-        octave_value_list tmp1;
-
-        if (op_increment)
-          {
-            octave_value ov_increment = evaluate (op_increment);
-
-            tmp1(2) = ov_limit;
-            tmp1(1) = ov_increment;
-            tmp1(0) = ov_base;
-          }
-        else
-          {
-            tmp1(1) = ov_limit;
-            tmp1(0) = ov_base;
-          }
-
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        octave_value fcn = symtab.find_function ("colon", tmp1);
-
-        if (! fcn.is_defined ())
-          error ("can not find overloaded colon function");
-
-        octave_value_list tmp2 = feval (fcn, tmp1, 1);
-
-        val = tmp2 (0);
-      }
-    else
-      {
-        octave_value ov_increment = 1.0;
-
-        if (op_increment)
-          ov_increment = evaluate (op_increment);
-
-        val = do_colon_op (ov_base, ov_increment, ov_limit,
-                           expr.is_for_cmd_expr ());
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
@@ -1235,60 +951,6 @@
     return retval;
   }
 
-  bool
-  tree_evaluator::isargout (int nargout, int iout) const
-  {
-    const std::list<octave_lvalue> *lvalues = m_lvalue_list;
-
-    if (iout >= std::max (nargout, 1))
-      return false;
-    else if (lvalues)
-      {
-        int k = 0;
-        for (const auto& lval : *lvalues)
-          {
-            if (k == iout)
-              return ! lval.is_black_hole ();
-            k += lval.numel ();
-            if (k > iout)
-              break;
-          }
-
-        return true;
-      }
-    else
-      return true;
-  }
-
-  void
-  tree_evaluator::isargout (int nargout, int nout, bool *isargout) const
-  {
-    const std::list<octave_lvalue> *lvalues = m_lvalue_list;
-
-    if (lvalues)
-      {
-        int k = 0;
-        for (const auto& lval : *lvalues)
-          {
-            if (lval.is_black_hole ())
-              isargout[k++] = false;
-            else
-              {
-                int l = std::min (k + lval.numel (),
-                                  static_cast<octave_idx_type> (nout));
-                while (k < l)
-                  isargout[k++] = true;
-              }
-          }
-      }
-    else
-      for (int i = 0; i < nout; i++)
-        isargout[i] = true;
-
-    for (int i = std::max (nargout, 1); i < nout; i++)
-      isargout[i] = false;
-  }
-
   octave_value
   tree_evaluator::evaluate (tree_decl_elt *elt)
   {
@@ -1296,7 +958,7 @@
 
     tree_identifier *id = elt->ident ();
 
-    return id ? evaluate (id).storable_value () : octave_value ();
+    return id ? id->evaluate (*this).storable_value () : octave_value ();
   }
 
   bool
@@ -1814,7 +1476,7 @@
 
         if (elt)
           {
-            octave_value tmp = evaluate (elt);
+            octave_value tmp = std::move (elt->evaluate (*this));
 
             if (tmp.is_cs_list ())
               {
@@ -1852,10 +1514,7 @@
         for (tree_decl_elt *elt : *ret_list)
           {
             if (is_defined (elt->ident ()))
-              {
-                octave_value tmp = evaluate (elt);
-                retval(i) = tmp;
-              }
+              retval(i) = std::move (evaluate (elt));
 
             i++;
           }
@@ -1869,7 +1528,7 @@
         int i = 0;
 
         for (tree_decl_elt *elt : *ret_list)
-          retval(i++) = evaluate (elt);
+          retval(i++) = std::move (evaluate (elt));
 
         for (octave_idx_type j = 0; j < vlen; j++)
           retval(i++) = varargout(j);
@@ -1890,7 +1549,7 @@
       {
         octave_lvalue ult = id->lvalue (*this);
 
-        octave_value init_val = evaluate (expr);
+        octave_value init_val = std::move (expr->evaluate (*this));
 
         ult.assign (octave_value::op_asn_eq, init_val);
 
@@ -1906,7 +1565,7 @@
   {
     tree_expression *label = expr->case_label ();
 
-    octave_value label_value = evaluate (label);
+    octave_value label_value = std::move (label->evaluate (*this));
 
     if (label_value.is_defined ())
       {
@@ -2383,6 +2042,9 @@
     if (m_debug_mode)
       do_breakpoint (cmd.is_active_breakpoint (*this));
 
+    // FIXME: tree_decl_init_list is not derived from tree, so should it
+    // really have an accept method?
+
     tree_decl_init_list *init_list = cmd.initializer_list ();
 
     if (init_list)
@@ -2392,6 +2054,9 @@
   void
   tree_evaluator::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)
       elt->accept (*this);
   }
@@ -2419,7 +2084,7 @@
             octave_value init_val;
 
             if (expr)
-              init_val = evaluate (expr);
+              init_val = std::move (expr->evaluate (*this));
             else
               init_val = Matrix ();
 
@@ -2453,7 +2118,7 @@
 
     tree_expression *expr = cmd.control_expr ();
 
-    octave_value rhs = evaluate (expr);
+    octave_value rhs = expr->evaluate (*this);
 
 #if defined (HAVE_LLVM)
     if (tree_jit::execute (cmd, rhs))
@@ -2588,7 +2253,7 @@
 
     tree_expression *expr = cmd.control_expr ();
 
-    octave_value rhs = evaluate (expr);
+    octave_value rhs = std::move (expr->evaluate (*this));
 
     if (rhs.is_undefined ())
       return;
@@ -2834,7 +2499,7 @@
             {
               m_call_stack.set_location (stmt->line (), stmt->column ());
 
-              retval = evaluate_n (expr, nargout);
+              retval = std::move (expr->evaluate_n (*this, nargout));
             }
         }
       else
@@ -2922,64 +2587,9 @@
   }
 
   void
-  tree_evaluator::visit_identifier (tree_identifier& expr)
+  tree_evaluator::visit_identifier (tree_identifier&)
   {
-    octave_value_list retval;
-
-    symbol_record sym = expr.symbol ();
-
-    octave_value val = varval (sym);
-
-    if (val.is_undefined ())
-      {
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        val = symtab.find_function (sym.name ());
-      }
-
-    if (val.is_defined ())
-      {
-        // GAGME -- this would be cleaner if we required
-        // parens to indicate function calls.
-        //
-        // If this identifier refers to a function, we need to know
-        // whether it is indexed so that we can do the same thing
-        // for 'f' and 'f()'.  If the index is present and the function
-        // object declares it can handle it, return the function object
-        // and let tree_index_expression::rvalue handle indexing.
-        // Otherwise, arrange to call the function here, so that we don't
-        // return the function definition as a value.
-
-        octave_function *fcn = nullptr;
-
-        if (val.is_function ())
-          fcn = val.function_value (true);
-
-        if (fcn && ! (expr.is_postfix_indexed ()
-                      && fcn->accepts_postfix_index (expr.postfix_index ())))
-          {
-            retval = fcn->call (*this, m_nargout);
-          }
-        else
-          {
-            if (expr.print_result () && m_nargout == 0
-                && statement_printing_enabled ())
-              {
-                octave_value_list args = ovl (val);
-                args.stash_name_tags (string_vector (expr.name ()));
-                feval ("display", args);
-              }
-
-            push_result (val);
-            return;
-          }
-      }
-    else if (sym.is_added_static ())
-      expr.static_workspace_error ();
-    else
-      expr.eval_undefined_error ();
-
-    push_result (retval);
+    panic_impossible ();
   }
 
   void
@@ -2998,6 +2608,9 @@
         m_echo_file_pos = line + 1;
       }
 
+    // FIXME: tree_if_command_list is not derived from tree, so should it
+    // really have an accept method?
+
     tree_if_command_list *lst = cmd.cmd_list ();
 
     if (lst)
@@ -3030,635 +2643,28 @@
       }
   }
 
-  // Unlike Matlab, which does not allow the result of a function call
-  // or array indexing expression to be further indexed, Octave attempts
-  // to handle arbitrary index expressions.  For example, Octave allows
-  // expressions like
-  //
-  //   svd (rand (10))(1:5)
-  //
-  // Although octave_value objects may contain function objects, no
-  // indexing operation or function call is supposed to return them
-  // directly.  Instead, the language is supposed to only allow function
-  // objects to be stored as function handles (named or anonymous) or as
-  // inline functions.  The only place a function object should appear
-  // directly is if the symbol stored in a tree_identifier object
-  // resolves to a function.  This means that the only place we need to
-  // look for functions is in the first element of the index
-  // expression.
-  //
-  // Steps:
-  //
-  //  * Obtain the initial value from the expression component of the
-  //    tree_index_expression object.  If it is a tree_identifier object
-  //    indexed by '(args)' and the identifier is not a variable, then
-  //    peform a function call.  Use the (optional) arguments to perform
-  //    the function lookup so we choose the correct function or class
-  //    method to call.  Otherwise, evaluate the first expression
-  //    without any additional arguments.
-  //
-  //  * Iterate over the remaining elements of the index expression and
-  //    call the octave_value::subsref method.  If indexing a class or
-  //    classdef object, build up a list of indices for a call to the
-  //    subsref method for the object.  Otherwise, use the result of
-  //    each temporary evaluation for the next index element.
-  //
-  //  * If not indexing a class or classdef object and any partial
-  //    expression evaluation produces a class or classdef object, then
-  //    build up a complete argument list from that point on for a final
-  //    subsref call for that object.
-  //
-  //    Multiple partial evaluations may be required.  For example,
-  //    given a class or classdef object X, then for the expression
-  //
-  //      x.a{end}(2:end).b
-  //
-  //    we must evaluate x.a to obtain the size for the first {end}
-  //    expression, then we must evaluate x.a{end} to obtain the size
-  //    for the second (2:end) expression.  Finally, the complete
-  //    expression may be evaluated.
-  //
-  //    If X is a cell array in the above expression, and none of the
-  //    intermediate evaluations produces a class or classdef object,
-  //    then the evaluation is performed as the following series of
-  //    steps
-  //
-  //      tmp = x.a
-  //      tmp = tmp{end}
-  //      tmp = tmp(2:end)
-  //      result = tmp.b
-  //
-  //    If any of the partial evaluations produces a class or classdef
-  //    object, then the subsref method for that object is called as
-  //    described above.  For example, suppose x.a produces a classdef
-  //    object.  Then the evaluation is performed as the following
-  //    series of steps
-  //
-  //      base_expr = tmp = x.a
-  //      tmp = base_expr{end}
-  //      base_expr{end}(2:end).b
-  //
-  //    In the last two steps, the partial value computed in the
-  //    previous step is used to determine the value of END.
-
   void
-  tree_evaluator::visit_index_expression (tree_index_expression& idx_expr)
+  tree_evaluator::visit_index_expression (tree_index_expression&)
   {
-    octave_value_list retval;
-
-    std::string type = idx_expr.type_tags ();
-    std::list<tree_argument_list *> args = idx_expr.arg_lists ();
-    std::list<string_vector> arg_nm = idx_expr.arg_names ();
-    std::list<tree_expression *> dyn_field = idx_expr.dyn_fields ();
-
-    assert (! args.empty ());
-
-    auto p_args = args.begin ();
-    auto p_arg_nm = arg_nm.begin ();
-    auto p_dyn_field = dyn_field.begin ();
-
-    int n = args.size ();
-    int beg = 0;
-
-    octave_value base_expr_val;
-
-    tree_expression *expr = idx_expr.expression ();
-
-    if (expr->is_identifier () && type[beg] == '(')
-      {
-        tree_identifier *id = dynamic_cast<tree_identifier *> (expr);
-
-        bool is_var = is_variable (expr);
-
-        std::string nm =  id->name ();
-
-        if (is_var && idx_expr.is_word_list_cmd ())
-          error ("%s used as variable and later as function", nm.c_str ());
-
-        if (! is_var)
-          {
-            octave_value_list first_args;
-
-            tree_argument_list *al = *p_args;
-
-            if (al && al->length () > 0)
-              {
-                // Function calls inside an argument list can't have
-                // ignored output arguments.
-
-                unwind_protect frame;
-
-                frame.protect_var (m_lvalue_list);
-                m_lvalue_list = nullptr;
-
-                string_vector anm = *p_arg_nm;
-                first_args = convert_to_const_vector (al);
-                first_args.stash_name_tags (anm);
-              }
-
-            symbol_record sym = id->symbol ();
-
-            octave_value val = varval (sym);
-
-            if (val.is_undefined ())
-              {
-                symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-                val = symtab.find_function (sym.name (), first_args);
-              }
-
-            octave_function *fcn = nullptr;
-
-            if (val.is_function ())
-              fcn = val.function_value (true);
-
-            if (fcn)
-              {
-                try
-                  {
-                    retval = fcn->call (*this, m_nargout, first_args);
-                  }
-                catch (index_exception& e)
-                  {
-                    final_index_error (e, expr);
-                  }
-
-                beg++;
-                p_args++;
-                p_arg_nm++;
-                p_dyn_field++;
-
-                if (n > beg)
-                  {
-                    // More indices to follow.  Silently ignore
-                    // extra output values.
-
-                    if (retval.length () == 0)
-                      error ("indexing undefined value");
-                    else
-                      base_expr_val = retval(0);
-                  }
-                else
-                  {
-                    // No more indices, so we are done.
-
-                    // See note at end of function about deleting
-                    // temporaries prior to pushing result.
-
-                    base_expr_val = octave_value ();
-                    first_args = octave_value_list ();
-
-                    push_result (retval);
-                    return;
-                  }
-              }
-          }
-      }
-
-    if (base_expr_val.is_undefined ())
-      base_expr_val = evaluate (expr);
-
-    // 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.
-
-    bool indexing_object = (base_expr_val.isobject ()
-                            || base_expr_val.isjava ()
-                            || (base_expr_val.is_classdef_meta ()
-                                && ! base_expr_val.is_package ()));
-
-    std::list<octave_value_list> idx_list;
-
-    octave_value partial_expr_val = base_expr_val;
-
-    for (int i = beg; i < n; i++)
-      {
-        if (i > beg)
-          {
-            tree_argument_list *al = *p_args;
-
-            if (! indexing_object || (al && al->has_magic_end ()))
-              {
-                // Evaluate what we have so far to find the value to
-                // pass to the END function.
-
-                try
-                  {
-                    // Silently ignore extra output values.
-
-                    octave_value_list tmp_list
-                      = base_expr_val.subsref (type.substr (beg, i-beg),
-                                               idx_list, m_nargout);
-
-                    partial_expr_val
-                      = tmp_list.length () ? tmp_list(0) : octave_value ();
-
-                    if (! indexing_object)
-                      {
-                        base_expr_val = partial_expr_val;
-
-                        if (partial_expr_val.is_cs_list ())
-                          err_indexed_cs_list ();
-
-                        retval = partial_expr_val;
-
-                        beg = i;
-                        idx_list.clear ();
-
-                        if (partial_expr_val.isobject ()
-                            || partial_expr_val.isjava ()
-                            || (partial_expr_val.is_classdef_meta ()
-                                && ! partial_expr_val.is_package ()))
-                          {
-                            // Found an object, so now we'll build up
-                            // complete index list for one big subsref
-                            // call from this point on.
-
-                            // FIXME: is is also possible to have a
-                            // static method call buried somewhere in
-                            // the depths of a complex indexing
-                            // expression so that we would also need to
-                            // check for an octave_classdef_meta object
-                            // here?
-
-                            indexing_object = true;
-                          }
-                      }
-                  }
-                catch (index_exception& e)
-                  {
-                    final_index_error (e, expr);
-                  }
-              }
-          }
-
-        switch (type[i])
-          {
-          case '(':
-            idx_list.push_back (make_value_list (*p_args, *p_arg_nm, &partial_expr_val));
-            break;
-
-          case '{':
-            idx_list.push_back (make_value_list (*p_args, *p_arg_nm, &partial_expr_val));
-            break;
-
-          case '.':
-            idx_list.push_back (octave_value
-                                (idx_expr.get_struct_index (*this, p_arg_nm, p_dyn_field)));
-            break;
-
-          default:
-            panic_impossible ();
-          }
-
-        p_args++;
-        p_arg_nm++;
-        p_dyn_field++;
-      }
-
-
-    // If ! idx_list.empty () that means we still have stuff to index
-    // otherwise they would have been dealt with and idx_list would have
-    // been emptied.
-    if (! idx_list.empty ())
-      {
-        // This is for +package and other classdef_meta objects
-        if (! base_expr_val.is_function ()
-            || base_expr_val.is_classdef_meta ())
-          {
-            try
-              {
-                retval = base_expr_val.subsref (type.substr (beg, n-beg),
-                                                idx_list, m_nargout);
-                beg = n;
-                idx_list.clear ();
-              }
-            catch (index_exception& e)
-              {
-                final_index_error (e, expr);
-              }
-          }
-        else
-          {
-            // 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;
-
-                    if (idx_list.size () != 1)
-                      error ("unexpected extra index at end of expression");
-
-                    if (type[beg] != '(')
-                      error ("invalid index type '%c' for function call",
-                             type[beg]);
-
-                    final_args = idx_list.front ();
-
-                    // FIXME: Do we ever need the names of the arguments
-                    // passed to FCN here?
-
-                    retval = fcn->call (*this, m_nargout, final_args);
-                  }
-                catch (index_exception& e)
-                  {
-                    final_index_error (e, expr);
-                  }
-              }
-          }
-      }
-
-    // FIXME: when can the following happen?  In what case does indexing
-    // result in a value that is a function?  Classdef method calls?
-    // Something else?
-
-    octave_value val = (retval.length () ? retval(0) : octave_value ());
-
-    if (val.is_function ())
-      {
-        octave_function *fcn = val.function_value (true);
-
-        if (fcn)
-          {
-            octave_value_list final_args;
-
-            if (! idx_list.empty ())
-              {
-                if (n - beg != 1)
-                  error ("unexpected extra index at end of expression");
-
-                if (type[beg] != '(')
-                  error ("invalid index type '%c' for function call",
-                         type[beg]);
-
-                final_args = idx_list.front ();
-              }
-
-            retval = fcn->call (*this, m_nargout, final_args);
-          }
-      }
-
-    // Delete any temporary values prior to pushing the result and
-    // returning so that destructors for any temporary classdef handle
-    // objects will be called before we return.  Otherwise, the
-    // destructor may push result values that will wipe out the result
-    // that we push below.  Although the method name is "push_result"
-    // there is only a single register (either an octave_value or an
-    // octave_value_list) not a stack.
-
-    idx_list.clear ();
-    partial_expr_val = octave_value ();
-    base_expr_val = octave_value ();
-    val = octave_value ();
-
-    push_result (retval);
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_matrix (tree_matrix& expr)
-  {
-    tm_const tmp (expr, *this);
-
-    push_result (tmp.concat (m_string_fill_char));
-  }
-
-  void
-  tree_evaluator::visit_cell (tree_cell& expr)
+  tree_evaluator::visit_matrix (tree_matrix&)
   {
-    octave_value retval;
-
-    // Function calls inside an argument list can't have ignored
-    // output arguments.
-
-    unwind_protect frame;
-
-    frame.protect_var (m_lvalue_list);
-    m_lvalue_list = nullptr;
-
-    octave_idx_type nr = expr.length ();
-    octave_idx_type nc = -1;
-
-    Cell val;
-
-    octave_idx_type i = 0;
-
-    for (tree_argument_list *elt : expr)
-      {
-        octave_value_list row = convert_to_const_vector (elt);
-
-        if (nr == 1)
-          // Optimize the single row case.
-          val = row.cell_value ();
-        else if (nc < 0)
-          {
-            nc = row.length ();
-
-            val = Cell (nr, nc);
-          }
-        else
-          {
-            octave_idx_type this_nc = row.length ();
-
-            if (this_nc != nc)
-              {
-                if (this_nc == 0)
-                  continue;  // blank line
-                else
-                  error ("number of columns must match");
-              }
-          }
-
-        for (octave_idx_type j = 0; j < nc; j++)
-          val(i,j) = row(j);
-
-        i++;
-      }
-
-    if (i < nr)
-      val.resize (dim_vector (i, nc));  // there were blank rows
-
-    retval = val;
-
-    push_result (retval);
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_multi_assignment (tree_multi_assignment& expr)
+  tree_evaluator::visit_cell (tree_cell&)
   {
-    octave_value_list val;
-
-    tree_expression *rhs = expr.right_hand_side ();
-
-    if (rhs)
-      {
-        unwind_protect frame;
-
-        tree_argument_list *lhs = expr.left_hand_side ();
-
-        std::list<octave_lvalue> lvalue_list = make_lvalue_list (lhs);
-
-        frame.protect_var (m_lvalue_list);
-        m_lvalue_list = &lvalue_list;
-
-        octave_idx_type n_out = 0;
-
-        for (const auto& lval : lvalue_list)
-          n_out += lval.numel ();
-
-        // The following trick is used to keep rhs_val constant.
-        const octave_value_list rhs_val1 = evaluate_n (rhs, n_out);
-        const octave_value_list rhs_val = (rhs_val1.length () == 1
-                                           && rhs_val1(0).is_cs_list ()
-                                           ? rhs_val1(0).list_value ()
-                                           : rhs_val1);
-
-        octave_idx_type k = 0;
-
-        octave_idx_type n = rhs_val.length ();
-
-        // To avoid copying per elements and possible optimizations, we
-        // postpone joining the final values.
-        std::list<octave_value_list> retval_list;
-
-        auto q = lhs->begin ();
-
-        for (octave_lvalue ult : lvalue_list)
-          {
-            tree_expression *lhs_elt = *q++;
-
-            octave_idx_type nel = ult.numel ();
-
-            if (nel != 1)
-              {
-                // Huge kluge so that wrapper scripts with lines like
-                //
-                //   [varargout{1:nargout}] = fcn (args);
-                //
-                // Will work the same as calling fcn directly when nargout
-                // is 0 and fcn produces more than one output even when
-                // nargout is 0.  This only works if varargout has not yet
-                // been defined.  See also bug #43813.
-
-                if (lvalue_list.size () == 1 && nel == 0 && n > 0
-                    && ! ult.is_black_hole () && ult.is_undefined ()
-                    && ult.index_type () == "{" && ult.index_is_empty ())
-                  {
-                    // Convert undefined lvalue with empty index to a cell
-                    // array with a single value and indexed by 1 to
-                    // handle a single output.
-
-                    nel = 1;
-
-                    ult.define (Cell (1, 1));
-
-                    ult.clear_index ();
-                    std::list<octave_value_list> idx;
-                    idx.push_back (octave_value_list (octave_value (1)));
-                    ult.set_index ("{", idx);
-                  }
-
-                if (k + nel > n)
-                  error ("some elements undefined in return list");
-
-                // This element of the return list expects a
-                // comma-separated list of values.  Slicing avoids
-                // copying.
-
-                octave_value_list ovl = rhs_val.slice (k, nel);
-
-                ult.assign (octave_value::op_asn_eq, octave_value (ovl));
-
-                retval_list.push_back (ovl);
-
-                k += nel;
-              }
-            else
-              {
-                if (k < n)
-                  {
-                    if (ult.is_black_hole ())
-                      {
-                        k++;
-                        continue;
-                      }
-                    else
-                      {
-                        octave_value tmp = rhs_val(k);
-
-                        if (tmp.is_undefined ())
-                          error ("element number %" OCTAVE_IDX_TYPE_FORMAT
-                                 " undefined in return list", k+1);
-
-                        ult.assign (octave_value::op_asn_eq, tmp);
-
-                        retval_list.push_back (tmp);
-
-                        k++;
-                      }
-                  }
-                else
-                  {
-                    // This can happen for a function like
-                    //
-                    //   function varargout = f ()
-                    //     varargout{1} = nargout;
-                    //   endfunction
-                    //
-                    // called with
-                    //
-                    //    [a, ~] = f ();
-                    //
-                    // Then the list of of RHS values will contain one
-                    // element but we are iterating over the list of all
-                    // RHS values.  We shouldn't complain that a value we
-                    // don't need is missing from the list.
-
-                    if (! ult.is_black_hole ())
-                      error ("element number %" OCTAVE_IDX_TYPE_FORMAT
-                             " undefined in return list", k+1);
-
-                    k++;
-                    continue;
-                  }
-              }
-
-            if (expr.print_result () && statement_printing_enabled ())
-              {
-                // We clear any index here so that we can get
-                // the new value of the referenced object below,
-                // instead of the indexed value (which should be
-                // the same as the right hand side value).
-
-                ult.clear_index ();
-
-                octave_value lhs_val = ult.value ();
-
-                octave_value_list args = ovl (lhs_val);
-                args.stash_name_tags (string_vector (lhs_elt->name ()));
-                feval ("display", args);
-              }
-          }
-
-        // Concatenate return values.
-        val = retval_list;
-      }
-
-    push_result (val);
+    panic_impossible ();
+  }
+
+  void
+  tree_evaluator::visit_multi_assignment (tree_multi_assignment&)
+  {
+    panic_impossible ();
   }
 
   void
@@ -3676,22 +2682,15 @@
   }
 
   void
-  tree_evaluator::visit_constant (tree_constant& expr)
+  tree_evaluator::visit_constant (tree_constant&)
   {
-    if (m_nargout > 1)
-      error ("invalid number of output arguments for constant expression");
-
-    push_result (expr.value ());
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_fcn_handle (tree_fcn_handle& expr)
+  tree_evaluator::visit_fcn_handle (tree_fcn_handle&)
   {
-    std::string nm = expr.name ();
-
-    octave_value fh = make_fcn_handle (m_interpreter, nm);
-
-    push_result (fh);
+    panic_impossible ();
   }
 
   void
@@ -3701,90 +2700,15 @@
   }
 
   void
-  tree_evaluator::visit_postfix_expression (tree_postfix_expression& expr)
+  tree_evaluator::visit_postfix_expression (tree_postfix_expression&)
   {
-    octave_value val;
-
-    tree_expression *op = expr.operand ();
-
-    if (op)
-      {
-        octave_value::unary_op etype = expr.op_type ();
-
-        if (etype == octave_value::op_incr || etype == octave_value::op_decr)
-          {
-            octave_lvalue ref = op->lvalue (*this);
-
-            val = ref.value ();
-
-            profiler::enter<tree_postfix_expression> block (m_profiler, expr);
-
-            ref.do_unary_op (etype);
-          }
-        else
-          {
-            octave_value op_val = evaluate (op);
-
-            if (op_val.is_defined ())
-              {
-                profiler::enter<tree_postfix_expression>
-                  block (m_profiler, expr);
-
-                type_info& ti = m_interpreter.get_type_info ();
-
-                val = ::do_unary_op (ti, etype, op_val);
-              }
-          }
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_prefix_expression (tree_prefix_expression& expr)
+  tree_evaluator::visit_prefix_expression (tree_prefix_expression&)
   {
-    octave_value val;
-
-    tree_expression *op = expr.operand ();
-
-    if (op)
-      {
-        octave_value::unary_op etype = expr.op_type ();
-
-        if (etype == octave_value::op_incr || etype == octave_value::op_decr)
-          {
-            octave_lvalue op_ref = op->lvalue (*this);
-
-            profiler::enter<tree_prefix_expression> block (m_profiler, expr);
-
-            op_ref.do_unary_op (etype);
-
-            val = op_ref.value ();
-          }
-        else
-          {
-            octave_value op_val = evaluate (op);
-
-            if (op_val.is_defined ())
-              {
-                profiler::enter<tree_prefix_expression>
-                  block (m_profiler, expr);
-
-                // Attempt to do the operation in-place if it is unshared
-                // (a temporary expression).
-                if (op_val.get_count () == 1)
-                  val = op_val.do_non_const_unary_op (etype);
-                else
-                  {
-                    type_info& ti = m_interpreter.get_type_info ();
-
-                    val = ::do_unary_op (ti, etype, op_val);
-                  }
-              }
-          }
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
@@ -3817,81 +2741,9 @@
   }
 
   void
-  tree_evaluator::visit_simple_assignment (tree_simple_assignment& expr)
+  tree_evaluator::visit_simple_assignment (tree_simple_assignment&)
   {
-    octave_value val;
-
-    tree_expression *rhs = expr.right_hand_side ();
-
-    if (rhs)
-      {
-        tree_expression *lhs = expr.left_hand_side ();
-
-        try
-          {
-            unwind_protect frame;
-
-            octave_lvalue ult = lhs->lvalue (*this);
-
-            std::list<octave_lvalue> lvalue_list;
-            lvalue_list.push_back (ult);
-
-            frame.protect_var (m_lvalue_list);
-            m_lvalue_list = &lvalue_list;
-
-            if (ult.numel () != 1)
-              err_invalid_structure_assignment ();
-
-            octave_value rhs_val = evaluate (rhs);
-
-            if (rhs_val.is_undefined ())
-              error ("value on right hand side of assignment is undefined");
-
-            if (rhs_val.is_cs_list ())
-              {
-                const octave_value_list lst = rhs_val.list_value ();
-
-                if (lst.empty ())
-                  error ("invalid number of elements on RHS of assignment");
-
-                rhs_val = lst(0);
-              }
-
-            octave_value::assign_op etype = expr.op_type ();
-
-            ult.assign (etype, rhs_val);
-
-            if (etype == octave_value::op_asn_eq)
-              val = rhs_val;
-            else
-              val = ult.value ();
-
-            if (expr.print_result () && statement_printing_enabled ())
-              {
-                // We clear any index here so that we can
-                // get the new value of the referenced
-                // object below, instead of the indexed
-                // value (which should be the same as the
-                // right hand side value).
-
-                ult.clear_index ();
-
-                octave_value lhs_val = ult.value ();
-
-                octave_value_list args = ovl (lhs_val);
-                args.stash_name_tags (string_vector (lhs->name ()));
-                feval ("display", args);
-              }
-          }
-        catch (index_exception& e)
-          {
-            e.set_var (lhs->name ());
-            std::string msg = e.message ();
-            error_with_id (e.err_id (), "%s", msg.c_str ());
-          }
-      }
-
-    push_result (val);
+    panic_impossible ();
   }
 
   void
@@ -3937,7 +2789,7 @@
                 // evaluate the expression and that should take care of
                 // everything, binding ans as necessary?
 
-                octave_value tmp_result = evaluate (expr, 0);
+                octave_value tmp_result = expr->evaluate (*this, 0);
 
                 if (tmp_result.is_defined ())
                   {
@@ -4058,7 +2910,7 @@
       error ("missing value in switch command near line %d, column %d",
              cmd.line (), cmd.column ());
 
-    octave_value val = evaluate (expr);
+    octave_value val = std::move (expr->evaluate (*this));
 
     tree_switch_case_list *lst = cmd.case_list ();
 
@@ -4388,41 +3240,15 @@
   }
 
   void
-  tree_evaluator::visit_superclass_ref (tree_superclass_ref& expr)
+  tree_evaluator::visit_superclass_ref (tree_superclass_ref&)
   {
-    std::string meth = expr.method_name ();
-    std::string cls = expr.class_name ();
-
-    octave_value tmp = octave_classdef::superclass_ref (meth, cls);
-
-    if (! expr.is_postfix_indexed ())
-      {
-        // There was no index, so this superclass_ref object is not
-        // part of an index expression.  It is also not an identifier in
-        // the syntax tree but we need to handle it as if it were.  So
-        // call the function here.
-
-        octave_function *f = tmp.function_value (true);
-
-        assert (f);
-
-        push_result (f->call (*this, m_nargout));
-
-        return;
-      }
-
-    // The superclass_ref function object will be indexed as part of the
-    // enclosing index expression.
-
-    push_result (tmp);
+    panic_impossible ();
   }
 
   void
-  tree_evaluator::visit_metaclass_query (tree_metaclass_query& expr)
+  tree_evaluator::visit_metaclass_query (tree_metaclass_query&)
   {
-    std::string cls = expr.class_name ();
-
-    push_result (octave_classdef::metaclass_query (cls));
+    panic_impossible ();
   }
 
   void tree_evaluator::bind_ans (const octave_value& val, bool print)
@@ -4453,18 +3279,6 @@
   }
 
   void
-  tree_evaluator::evaluate_internal (tree_expression *expr, int nargout)
-  {
-    unwind_protect frame;
-
-    frame.protect_var (m_nargout);
-
-    m_nargout = nargout;
-
-    expr->accept (*this);
-  }
-
-  void
   tree_evaluator::do_breakpoint (tree_statement& stmt)
   {
     do_breakpoint (stmt.is_active_breakpoint (*this),
@@ -4546,7 +3360,7 @@
   {
     bool expr_value = false;
 
-    octave_value t1 = evaluate (expr);
+    octave_value t1 = std::move (expr->evaluate (*this));
 
     if (t1.is_defined ())
       return t1.is_true ();
@@ -4556,49 +3370,6 @@
     return expr_value;
   }
 
-  octave_value_list
-  tree_evaluator::make_value_list (tree_argument_list *args,
-                                   const string_vector& arg_nm,
-                                   const octave_value *object, bool rvalue)
-  {
-    octave_value_list retval;
-
-    if (args)
-      {
-        // Function calls inside an argument list can't have ignored
-        // output arguments.
-
-        unwind_protect frame;
-
-        frame.protect_var (m_lvalue_list);
-        m_lvalue_list = nullptr;
-
-        if (rvalue && object && args->has_magic_end ()
-            && object->is_undefined ())
-          err_invalid_inquiry_subscript ();
-
-        retval = convert_to_const_vector (args, object);
-      }
-
-    octave_idx_type n = retval.length ();
-
-    if (n > 0)
-      retval.stash_name_tags (arg_nm);
-
-    return retval;
-  }
-
-  std::list<octave_lvalue>
-  tree_evaluator::make_lvalue_list (tree_argument_list *lhs)
-  {
-    std::list<octave_lvalue> retval;
-
-    for (tree_expression *elt : *lhs)
-      retval.push_back (elt->lvalue (*this));
-
-    return retval;
-  }
-
   octave_value
   tree_evaluator::max_recursion_depth (const octave_value_list& args,
                                        int nargout)
@@ -4776,6 +3547,46 @@
     return m_call_stack.do_who (argc, argv, return_list, verbose);
   }
 
+  octave_value_list
+  tree_evaluator::make_value_list (tree_argument_list *args,
+                                   const string_vector& arg_nm,
+                                   const octave_value *object, bool rvalue)
+  {
+    octave_value_list retval;
+
+    if (args)
+      {
+        unwind_protect frame;
+
+        frame.protect_var (m_lvalue_list);
+        m_lvalue_list = nullptr;
+
+        if (rvalue && object && args->has_magic_end ()
+            && object->is_undefined ())
+          err_invalid_inquiry_subscript ();
+
+        retval = convert_to_const_vector (args, object);
+      }
+
+    octave_idx_type n = retval.length ();
+
+    if (n > 0)
+      retval.stash_name_tags (arg_nm);
+
+    return retval;
+  }
+
+  std::list<octave_lvalue>
+  tree_evaluator::make_lvalue_list (tree_argument_list *lhs)
+  {
+    std::list<octave_lvalue> retval;
+
+    for (tree_expression *elt : *lhs)
+      retval.push_back (elt->lvalue (*this));
+
+    return retval;
+  }
+
   void
   tree_evaluator::push_echo_state (unwind_protect& frame, int type,
                                    const std::string& file_name,
--- a/libinterp/parse-tree/pt-eval.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-eval.h	Fri Aug 30 15:02:14 2019 -0400
@@ -33,10 +33,10 @@
 
 #include "bp-table.h"
 #include "call-stack.h"
+#include "oct-lvalue.h"
 #include "ov.h"
 #include "ovl.h"
 #include "profiler.h"
-#include "pt-exp.h"
 #include "pt-walk.h"
 
 class octave_user_code;
@@ -52,13 +52,6 @@
   class interpreter;
   class unwind_protect;
 
-  enum result_type
-  {
-    RT_UNDEFINED = 0,
-    RT_VALUE = 1,
-    RT_VALUE_LIST = 2
-  };
-
   // How to evaluate the code that the parse trees represent.
 
   class OCTINTERP_API tree_evaluator : public tree_walker
@@ -130,9 +123,7 @@
 
     tree_evaluator (interpreter& interp)
       : m_interpreter (interp), m_statement_context (SC_OTHER),
-        m_result_type (RT_UNDEFINED), m_expr_result_value (),
-        m_expr_result_value_list (), m_lvalue_list (nullptr),
-        m_nargout (0), m_autoload_map (), m_bp_table (*this),
+        m_lvalue_list (nullptr), m_autoload_map (), m_bp_table (*this),
         m_call_stack (*this), m_profiler (), m_debug_frame (0),
         m_debug_mode (false), m_quiet_breakpoint_flag (false),
         m_debugger_stack (), m_max_recursion_depth (256),
@@ -306,79 +297,6 @@
 
     Matrix ignored_fcn_outputs (void) const;
 
-    bool isargout (int nargout, int iout) const;
-
-    void isargout (int nargout, int nout, bool *isargout) const;
-
-    void push_result (const octave_value& val)
-    {
-      m_result_type = RT_VALUE;
-      m_expr_result_value = val;
-    }
-
-    void push_result (const octave_value_list& vals)
-    {
-      m_result_type = RT_VALUE_LIST;
-      m_expr_result_value_list = vals;
-    }
-
-    octave_value evaluate (tree_expression *expr, int nargout = 1)
-    {
-      octave_value retval;
-
-      evaluate_internal (expr, nargout);
-
-      switch (m_result_type)
-        {
-        case RT_UNDEFINED:
-          panic_impossible ();
-          break;
-
-        case RT_VALUE:
-          retval = m_expr_result_value;
-          m_expr_result_value = octave_value ();
-          break;
-
-        case RT_VALUE_LIST:
-          retval = (m_expr_result_value_list.empty ()
-                    ? octave_value () : m_expr_result_value_list(0));
-          m_expr_result_value_list = octave_value_list ();
-          break;
-        }
-
-      m_result_type = RT_UNDEFINED;
-
-      return retval;
-    }
-
-    octave_value_list evaluate_n (tree_expression *expr, int nargout = 1)
-    {
-      octave_value_list retval;
-
-      evaluate_internal (expr, nargout);
-
-      switch (m_result_type)
-        {
-        case RT_UNDEFINED:
-          panic_impossible ();
-          break;
-
-        case RT_VALUE:
-          retval = ovl (m_expr_result_value);
-          m_expr_result_value = octave_value ();
-          break;
-
-        case RT_VALUE_LIST:
-          retval = m_expr_result_value_list;
-          m_expr_result_value_list = octave_value_list ();
-          break;
-        }
-
-      m_result_type = RT_UNDEFINED;
-
-      return retval;
-    }
-
     octave_value evaluate (tree_decl_elt *);
 
     void install_variable (const std::string& name,
@@ -708,6 +626,16 @@
 
     bool in_top_level_repl (void) const { return m_in_top_level_repl; }
 
+    const std::list<octave_lvalue> * lvalue_list (void) const
+    {
+      return m_lvalue_list;
+    }
+
+    void set_lvalue_list (const std::list<octave_lvalue> *lst)
+    {
+      m_lvalue_list = lst;
+    }
+
     int breaking (void) const { return m_breaking; }
 
     int breaking (int n)
@@ -765,6 +693,13 @@
     octave_value do_who (int argc, const string_vector& argv,
                          bool return_list, bool verbose = false);
 
+    octave_value_list
+    make_value_list (tree_argument_list *args,
+                     const string_vector& arg_nm,
+                     const octave_value *object, bool rvalue = true);
+
+    std::list<octave_lvalue> make_lvalue_list (tree_argument_list *);
+
     void push_echo_state (unwind_protect& frame, int type,
                           const std::string& file_name, size_t pos = 1);
 
@@ -778,8 +713,6 @@
 
     bool maybe_push_echo_state_cleanup (void);
 
-    void evaluate_internal (tree_expression *expr, int nargout);
-
     void do_breakpoint (tree_statement& stmt);
 
     void do_breakpoint (bool is_breakpoint,
@@ -787,13 +720,6 @@
 
     bool is_logically_true (tree_expression *expr, const char *warn_for);
 
-    octave_value_list
-    make_value_list (tree_argument_list *args,
-                     const string_vector& arg_nm,
-                     const octave_value *object, bool rvalue = true);
-
-    std::list<octave_lvalue> make_lvalue_list (tree_argument_list *);
-
     // For unwind-protect.
     void uwp_set_echo_state (bool state, const std::string& file_name,
                              size_t pos);
@@ -817,14 +743,8 @@
     // The context for the current evaluation.
     stmt_list_type m_statement_context;
 
-    result_type m_result_type;
-    octave_value m_expr_result_value;
-    octave_value_list m_expr_result_value_list;
-
     const std::list<octave_lvalue> *m_lvalue_list;
 
-    int m_nargout;
-
     // List of autoloads (function -> file mapping).
     std::map<std::string, std::string> m_autoload_map;
 
--- a/libinterp/parse-tree/pt-exp.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-exp.h	Fri Aug 30 15:02:14 2019 -0400
@@ -31,12 +31,12 @@
 class octave_value;
 
 #include "pt.h"
+#include "pt-eval.h"
 
 namespace octave
 {
   class symbol_scope;
   class octave_lvalue;
-  class tree_evaluator;
 
   // A base class for expressions.
 
@@ -134,6 +134,11 @@
       print_flag = e.print_flag;
     }
 
+    virtual octave_value evaluate (tree_evaluator& tw, int nargout = 1) = 0;
+
+    virtual octave_value_list
+    evaluate_n (tree_evaluator& tw, int nargout = 1) = 0;
+
   protected:
 
     // A count of the number of times this expression appears directly
--- a/libinterp/parse-tree/pt-fcn-handle.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-fcn-handle.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -27,6 +27,7 @@
 #include <ostream>
 
 #include "interpreter-private.h"
+#include "pt-anon-scopes.h"
 #include "pt-fcn-handle.h"
 
 namespace octave
@@ -55,6 +56,12 @@
     return new_fh;
   }
 
+  octave_value
+  tree_fcn_handle::evaluate (tree_evaluator& tw, int)
+  {
+    return make_fcn_handle (tw.get_interpreter (), m_name);
+  }
+
   tree_anon_fcn_handle::~tree_anon_fcn_handle (void)
   {
     delete m_parameter_list;
@@ -86,6 +93,86 @@
 
     return new_afh;
   }
+
+  octave_value
+  tree_anon_fcn_handle::evaluate (tree_evaluator& tw, int)
+  {
+    // FIXME: should CMD_LIST be limited to a single expression?
+    // I think that is what Matlab does.
+
+    symbol_scope new_scope;
+    if (m_scope)
+      new_scope = m_scope.dup ();
+
+    tree_parameter_list *param_list_dup
+      = m_parameter_list ? m_parameter_list->dup (new_scope) : nullptr;
+
+    tree_parameter_list *ret_list = nullptr;
+
+    tree_statement_list *stmt_list = nullptr;
+
+    symbol_scope parent_scope = tw.get_current_scope ();
+
+    new_scope.set_parent (parent_scope);
+    new_scope.set_primary_parent (parent_scope);
+
+    if (m_expression)
+      {
+        tree_expression *expr_dup = m_expression->dup (new_scope);
+        tree_statement *stmt = new tree_statement (expr_dup, nullptr);
+        stmt_list = new tree_statement_list (stmt);
+      }
+
+    tree_anon_scopes anon_fcn_ctx (*this);
+
+    std::set<std::string> free_vars = anon_fcn_ctx.free_variables ();
+
+    octave_user_function::local_vars_map local_var_init_vals;
+
+    call_stack& cs = tw.get_call_stack ();
+
+    stack_frame& frame = cs.get_current_stack_frame ();
+
+    for (auto& name : free_vars)
+      {
+        octave_value val = frame.varval (name);
+
+        if (val.is_defined ())
+          local_var_init_vals[name] = val;
+      }
+
+    octave_user_function *af
+      = new octave_user_function (new_scope, param_list_dup, ret_list,
+                                  stmt_list, local_var_init_vals);
+
+    octave_function *curr_fcn = cs.current ();
+
+    if (curr_fcn)
+      {
+        // FIXME: maybe it would be better to just stash curr_fcn
+        // instead of individual bits of info about it?
+
+        af->stash_parent_fcn_name (curr_fcn->name ());
+        af->stash_dir_name (curr_fcn->dir_name ());
+
+        // The following is needed so that class method dispatch works
+        // properly for anonymous functions that wrap class methods.
+
+        if (curr_fcn->is_class_method () || curr_fcn->is_class_constructor ())
+          af->stash_dispatch_class (curr_fcn->dispatch_class ());
+
+        af->stash_fcn_file_name (curr_fcn->fcn_file_name ());
+      }
+
+    af->mark_as_anonymous_function ();
+
+    octave_value ov_fcn (af);
+
+    // octave_value fh (octave_fcn_binder::maybe_binder (ov_fcn, m_interpreter));
+
+    return octave_value (new octave_fcn_handle
+                         (ov_fcn, octave_fcn_handle::anonymous));
+  }
 }
 
 /*
--- a/libinterp/parse-tree/pt-fcn-handle.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-fcn-handle.h	Fri Aug 30 15:02:14 2019 -0400
@@ -74,6 +74,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator& tw, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_fcn_handle (*this);
@@ -130,6 +137,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator& tw, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw) { tw.visit_anon_fcn_handle (*this); }
 
     void stash_file_name (const std::string& file) { m_file_name = file; }
--- a/libinterp/parse-tree/pt-id.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-id.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -80,6 +80,66 @@
     return new_id;
   }
 
+  octave_value_list
+  tree_identifier::evaluate_n (tree_evaluator& tw, int nargout)
+  {
+    octave_value_list retval;
+
+    octave_value val = tw.varval (m_sym);
+
+    if (val.is_undefined ())
+      {
+        interpreter& interp = tw.get_interpreter ();
+
+        symbol_table& symtab = interp.get_symbol_table ();
+
+        val = symtab.find_function (m_sym.name ());
+      }
+
+    if (val.is_defined ())
+      {
+        // GAGME -- this would be cleaner if we required
+        // parens to indicate function calls.
+        //
+        // If this identifier refers to a function, we need to know
+        // whether it is indexed so that we can do the same thing
+        // for 'f' and 'f()'.  If the index is present and the function
+        // object declares it can handle it, return the function object
+        // and let tree_index_expression::rvalue handle indexing.
+        // Otherwise, arrange to call the function here, so that we don't
+        // return the function definition as a value.
+
+        octave_function *fcn = nullptr;
+
+        if (val.is_function ())
+          fcn = val.function_value (true);
+
+        if (fcn && ! (is_postfix_indexed ()
+                      && fcn->accepts_postfix_index (postfix_index ())))
+          {
+            retval = fcn->call (tw, nargout);
+          }
+        else
+          {
+            if (print_result () && nargout == 0
+                && tw.statement_printing_enabled ())
+              {
+                octave_value_list args = ovl (val);
+                args.stash_name_tags (string_vector (name ()));
+                feval ("display", args);
+              }
+
+            retval = ovl (val);
+          }
+      }
+    else if (m_sym.is_added_static ())
+      static_workspace_error ();
+    else
+      eval_undefined_error ();
+
+    return retval;
+  }
+
   octave_lvalue
   tree_black_hole::lvalue (tree_evaluator& tw)
   {
--- a/libinterp/parse-tree/pt-id.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-id.h	Fri Aug 30 15:02:14 2019 -0400
@@ -92,6 +92,15 @@
 
     tree_identifier * dup (symbol_scope& scope) const;
 
+    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 ();
+    }
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1);
+
     void accept (tree_walker& tw)
     {
       tw.visit_identifier (*this);
--- a/libinterp/parse-tree/pt-idx.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-idx.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -183,7 +183,7 @@
 
         if (df)
           {
-            octave_value t = tw.evaluate (df);
+            octave_value t = df->evaluate (tw);
 
             fn = t.xstring_value ("dynamic structure field names must be strings");
           }
@@ -374,6 +374,397 @@
 
     return new_idx_expr;
   }
+
+  // Unlike Matlab, which does not allow the result of a function call
+  // or array indexing expression to be further indexed, Octave attempts
+  // to handle arbitrary index expressions.  For example, Octave allows
+  // expressions like
+  //
+  //   svd (rand (10))(1:5)
+  //
+  // Although octave_value objects may contain function objects, no
+  // indexing operation or function call is supposed to return them
+  // directly.  Instead, the language is supposed to only allow function
+  // objects to be stored as function handles (named or anonymous) or as
+  // inline functions.  The only place a function object should appear
+  // directly is if the symbol stored in a tree_identifier object
+  // resolves to a function.  This means that the only place we need to
+  // look for functions is in the first element of the index
+  // expression.
+  //
+  // Steps:
+  //
+  //  * Obtain the initial value from the expression component of the
+  //    tree_index_expression object.  If it is a tree_identifier object
+  //    indexed by '(args)' and the identifier is not a variable, then
+  //    peform a function call.  Use the (optional) arguments to perform
+  //    the function lookup so we choose the correct function or class
+  //    method to call.  Otherwise, evaluate the first expression
+  //    without any additional arguments.
+  //
+  //  * Iterate over the remaining elements of the index expression and
+  //    call the octave_value::subsref method.  If indexing a class or
+  //    classdef object, build up a list of indices for a call to the
+  //    subsref method for the object.  Otherwise, use the result of
+  //    each temporary evaluation for the next index element.
+  //
+  //  * If not indexing a class or classdef object and any partial
+  //    expression evaluation produces a class or classdef object, then
+  //    build up a complete argument list from that point on for a final
+  //    subsref call for that object.
+  //
+  //    Multiple partial evaluations may be required.  For example,
+  //    given a class or classdef object X, then for the expression
+  //
+  //      x.a{end}(2:end).b
+  //
+  //    we must evaluate x.a to obtain the size for the first {end}
+  //    expression, then we must evaluate x.a{end} to obtain the size
+  //    for the second (2:end) expression.  Finally, the complete
+  //    expression may be evaluated.
+  //
+  //    If X is a cell array in the above expression, and none of the
+  //    intermediate evaluations produces a class or classdef object,
+  //    then the evaluation is performed as the following series of
+  //    steps
+  //
+  //      tmp = x.a
+  //      tmp = tmp{end}
+  //      tmp = tmp(2:end)
+  //      result = tmp.b
+  //
+  //    If any of the partial evaluations produces a class or classdef
+  //    object, then the subsref method for that object is called as
+  //    described above.  For example, suppose x.a produces a classdef
+  //    object.  Then the evaluation is performed as the following
+  //    series of steps
+  //
+  //      base_expr = tmp = x.a
+  //      tmp = base_expr{end}
+  //      base_expr{end}(2:end).b
+  //
+  //    In the last two steps, the partial value computed in the
+  //    previous step is used to determine the value of END.
+
+  octave_value_list
+  tree_index_expression::evaluate_n (tree_evaluator& tw, int nargout)
+  {
+    octave_value_list retval;
+
+    assert (! m_args.empty ());
+
+    auto p_args = m_args.begin ();
+    auto p_arg_nm = m_arg_nm.begin ();
+    auto p_dyn_field = m_dyn_field.begin ();
+
+    int n = m_args.size ();
+    int beg = 0;
+
+    octave_value base_expr_val;
+
+    if (m_expr->is_identifier () && m_type[beg] == '(')
+      {
+        tree_identifier *id = dynamic_cast<tree_identifier *> (m_expr);
+
+        bool is_var = tw.is_variable (m_expr);
+
+        std::string nm =  id->name ();
+
+        if (is_var && is_word_list_cmd ())
+          error ("%s used as variable and later as function", nm.c_str ());
+
+        if (! is_var)
+          {
+            octave_value_list first_args;
+
+            tree_argument_list *al = *p_args;
+
+            if (al && al->length () > 0)
+              {
+                unwind_protect frame;
+
+                frame.add_method (tw, &tree_evaluator::set_lvalue_list,
+                                  tw.lvalue_list ());
+                tw.set_lvalue_list (nullptr);
+
+                string_vector anm = *p_arg_nm;
+                first_args = tw.convert_to_const_vector (al);
+                first_args.stash_name_tags (anm);
+              }
+
+            symbol_record sym = id->symbol ();
+
+            octave_value val = tw.varval (sym);
+
+            if (val.is_undefined ())
+              {
+                interpreter& interp = tw.get_interpreter ();
+
+                symbol_table& symtab = interp.get_symbol_table ();
+
+                val = symtab.find_function (sym.name (), first_args);
+              }
+
+            octave_function *fcn = nullptr;
+
+            if (val.is_function ())
+              fcn = val.function_value (true);
+
+            if (fcn)
+              {
+                try
+                  {
+                    retval = fcn->call (tw, nargout, first_args);
+                  }
+                catch (index_exception& e)
+                  {
+                    tw.final_index_error (e, m_expr);
+                  }
+
+                beg++;
+                p_args++;
+                p_arg_nm++;
+                p_dyn_field++;
+
+                if (n > beg)
+                  {
+                    // More indices to follow.  Silently ignore
+                    // extra output values.
+
+                    if (retval.length () == 0)
+                      error ("indexing undefined value");
+                    else
+                      base_expr_val = retval(0);
+                  }
+                else
+                  {
+                    // No more indices, so we are done.
+
+                    // See note at end of function about deleting
+                    // temporaries prior to pushing result.
+
+                    base_expr_val = octave_value ();
+                    first_args = octave_value_list ();
+
+                    return retval;
+                  }
+              }
+          }
+      }
+
+    if (base_expr_val.is_undefined ())
+      base_expr_val = std::move (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.
+
+    bool indexing_object = (base_expr_val.isobject ()
+                            || base_expr_val.isjava ()
+                            || (base_expr_val.is_classdef_meta ()
+                                && ! base_expr_val.is_package ()));
+
+    std::list<octave_value_list> idx_list;
+
+    octave_value partial_expr_val = base_expr_val;
+
+    for (int i = beg; i < n; i++)
+      {
+        if (i > beg)
+          {
+            tree_argument_list *al = *p_args;
+
+            if (! indexing_object || (al && al->has_magic_end ()))
+              {
+                // Evaluate what we have so far to find the value to
+                // pass to the END function.
+
+                try
+                  {
+                    // Silently ignore extra output values.
+
+                    octave_value_list tmp_list
+                      = base_expr_val.subsref (m_type.substr (beg, i-beg),
+                                               idx_list, nargout);
+
+                    partial_expr_val
+                      = tmp_list.length () ? tmp_list(0) : octave_value ();
+
+                    if (! indexing_object)
+                      {
+                        base_expr_val = partial_expr_val;
+
+                        if (partial_expr_val.is_cs_list ())
+                          err_indexed_cs_list ();
+
+                        retval = partial_expr_val;
+
+                        beg = i;
+                        idx_list.clear ();
+
+                        if (partial_expr_val.isobject ()
+                            || partial_expr_val.isjava ()
+                            || (partial_expr_val.is_classdef_meta ()
+                                && ! partial_expr_val.is_package ()))
+                          {
+                            // Found an object, so now we'll build up
+                            // complete index list for one big subsref
+                            // call from this point on.
+
+                            // FIXME: is is also possible to have a
+                            // static method call buried somewhere in
+                            // the depths of a complex indexing
+                            // expression so that we would also need to
+                            // check for an octave_classdef_meta object
+                            // here?
+
+                            indexing_object = true;
+                          }
+                      }
+                  }
+                catch (index_exception& e)
+                  {
+                    tw.final_index_error (e, m_expr);
+                  }
+              }
+          }
+
+        switch (m_type[i])
+          {
+          case '(':
+            idx_list.push_back (make_value_list (tw, *p_args, *p_arg_nm, &partial_expr_val));
+            break;
+
+          case '{':
+            idx_list.push_back (make_value_list (tw, *p_args, *p_arg_nm, &partial_expr_val));
+            break;
+
+          case '.':
+            idx_list.push_back (octave_value
+                                (get_struct_index (tw, p_arg_nm, p_dyn_field)));
+            break;
+
+          default:
+            panic_impossible ();
+          }
+
+        p_args++;
+        p_arg_nm++;
+        p_dyn_field++;
+      }
+
+
+    // If ! idx_list.empty () that means we still have stuff to index
+    // otherwise they would have been dealt with and idx_list would have
+    // been emptied.
+    if (! idx_list.empty ())
+      {
+        // This is for +package and other classdef_meta objects
+        if (! base_expr_val.is_function ()
+            || base_expr_val.is_classdef_meta ())
+          {
+            try
+              {
+                retval = base_expr_val.subsref (m_type.substr (beg, n-beg),
+                                                idx_list, nargout);
+                beg = n;
+                idx_list.clear ();
+              }
+            catch (index_exception& e)
+              {
+                tw.final_index_error (e, m_expr);
+              }
+          }
+        else
+          {
+            // 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;
+
+                    if (idx_list.size () != 1)
+                      error ("unexpected extra index at end of expression");
+
+                    if (m_type[beg] != '(')
+                      error ("invalid index type '%c' for function call",
+                             m_type[beg]);
+
+                    final_args = idx_list.front ();
+
+                    // FIXME: Do we ever need the names of the arguments
+                    // passed to FCN here?
+
+                    retval = fcn->call (tw, nargout, final_args);
+                  }
+                catch (index_exception& e)
+                  {
+                    tw.final_index_error (e, m_expr);
+                  }
+              }
+          }
+      }
+
+    // FIXME: when can the following happen?  In what case does indexing
+    // result in a value that is a function?  Classdef method calls?
+    // Something else?
+
+    octave_value val = (retval.length () ? retval(0) : octave_value ());
+
+    if (val.is_function ())
+      {
+        octave_function *fcn = val.function_value (true);
+
+        if (fcn)
+          {
+            octave_value_list final_args;
+
+            if (! idx_list.empty ())
+              {
+                if (n - beg != 1)
+                  error ("unexpected extra index at end of expression");
+
+                if (m_type[beg] != '(')
+                  error ("invalid index type '%c' for function call",
+                         m_type[beg]);
+
+                final_args = idx_list.front ();
+              }
+
+            retval = fcn->call (tw, nargout, final_args);
+          }
+      }
+
+    // Delete any temporary values prior to pushing the result and
+    // returning so that destructors for any temporary classdef handle
+    // objects will be called before we return.  Otherwise, the
+    // destructor may push result values that will wipe out the result
+    // that we push below.  Although the method name is "push_result"
+    // there is only a single register (either an octave_value or an
+    // octave_value_list) not a stack.
+
+    idx_list.clear ();
+    partial_expr_val = octave_value ();
+    base_expr_val = octave_value ();
+    val = octave_value ();
+
+    return retval;
+  }
 }
 
 /*
--- a/libinterp/parse-tree/pt-idx.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-idx.h	Fri Aug 30 15:02:14 2019 -0400
@@ -101,6 +101,15 @@
 
     tree_index_expression * dup (symbol_scope& scope) const;
 
+    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 ();
+    }
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1);
+
     void accept (tree_walker& tw)
     {
       tw.visit_index_expression (*this);
--- a/libinterp/parse-tree/pt-loop.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-loop.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -25,6 +25,7 @@
 #endif
 
 #include "pt-arg-list.h"
+#include "pt-exp.h"
 #include "pt-loop.h"
 #include "pt-stmt.h"
 
--- a/libinterp/parse-tree/pt-mat.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-mat.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -25,11 +25,13 @@
 #endif
 
 #include "defun.h"
+#include "ov.h"
 #include "ovl.h"
 #include "pt-arg-list.h"
+#include "pt-eval.h"
 #include "pt-exp.h"
 #include "pt-mat.h"
-#include "ov.h"
+#include "pt-tm-const.h"
 #include "variables.h"
 
 #include "ov-cx-mat.h"
@@ -39,6 +41,14 @@
 
 namespace octave
 {
+  octave_value
+  tree_matrix::evaluate (tree_evaluator& tw, int)
+  {
+    tm_const tmp (*this, tw);
+
+    return tmp.concat (tw.string_fill_char ());
+  }
+
   std::string
   get_concat_class (const std::string& c1, const std::string& c2)
   {
--- a/libinterp/parse-tree/pt-mat.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-mat.h	Fri Aug 30 15:02:14 2019 -0400
@@ -65,6 +65,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_matrix (*this);
--- a/libinterp/parse-tree/pt-select.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-select.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -24,6 +24,7 @@
 #  include "config.h"
 #endif
 
+#include "pt-exp.h"
 #include "pt-select.h"
 #include "pt-stmt.h"
 
--- a/libinterp/parse-tree/pt-tm-const.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-tm-const.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -165,7 +165,7 @@
       {
         octave_quit ();
 
-        octave_value tmp = tw.evaluate (elt);
+        octave_value tmp = elt->evaluate (tw);
 
         if (tmp.is_undefined ())
           return;
--- a/libinterp/parse-tree/pt-unop.cc	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-unop.cc	Fri Aug 30 15:02:14 2019 -0400
@@ -24,7 +24,9 @@
 #  include "config.h"
 #endif
 
+#include "interpreter.h"
 #include "ov.h"
+#include "profiler.h"
 #include "pt-unop.h"
 
 namespace octave
@@ -51,6 +53,53 @@
     return new_pe;
   }
 
+  octave_value
+  tree_prefix_expression::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    if (m_op)
+      {
+        if (m_etype == octave_value::op_incr
+            || m_etype == octave_value::op_decr)
+          {
+            octave_lvalue op_ref = m_op->lvalue (tw);
+
+            profiler::enter<tree_prefix_expression>
+              block (tw.get_profiler (), *this);
+
+            op_ref.do_unary_op (m_etype);
+
+            val = op_ref.value ();
+          }
+        else
+          {
+            octave_value op_val = std::move (m_op->evaluate (tw));
+
+            if (op_val.is_defined ())
+              {
+                profiler::enter<tree_prefix_expression>
+                  block (tw.get_profiler (), *this);
+
+                // Attempt to do the operation in-place if it is unshared
+                // (a temporary expression).
+                if (op_val.get_count () == 1)
+                  val = op_val.do_non_const_unary_op (m_etype);
+                else
+                  {
+                    interpreter& interp = tw.get_interpreter ();
+
+                    type_info& ti = interp.get_type_info ();
+
+                    val = ::do_unary_op (ti, m_etype, op_val);
+                  }
+              }
+          }
+      }
+
+    return val;
+  }
+
   // Postfix expressions.
 
   tree_expression *
@@ -64,4 +113,44 @@
 
     return new_pe;
   }
+
+  octave_value
+  tree_postfix_expression::evaluate (tree_evaluator& tw, int)
+  {
+    octave_value val;
+
+    if (m_op)
+      {
+        if (m_etype == octave_value::op_incr
+            || m_etype == octave_value::op_decr)
+          {
+            octave_lvalue ref = m_op->lvalue (tw);
+
+            val = ref.value ();
+
+            profiler::enter<tree_postfix_expression>
+              block (tw.get_profiler (), *this);
+
+            ref.do_unary_op (m_etype);
+          }
+        else
+          {
+            octave_value op_val = std::move (m_op->evaluate (tw));
+
+            if (op_val.is_defined ())
+              {
+                profiler::enter<tree_postfix_expression>
+                  block (tw.get_profiler (), *this);
+
+                interpreter& interp = tw.get_interpreter ();
+
+                type_info& ti = interp.get_type_info ();
+
+                val = ::do_unary_op (ti, m_etype, op_val);
+              }
+          }
+      }
+
+    return val;
+  }
 }
--- a/libinterp/parse-tree/pt-unop.h	Wed Aug 28 21:38:34 2019 -0400
+++ b/libinterp/parse-tree/pt-unop.h	Fri Aug 30 15:02:14 2019 -0400
@@ -109,6 +109,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_prefix_expression (*this);
@@ -143,6 +150,13 @@
 
     tree_expression * dup (symbol_scope& scope) const;
 
+    octave_value evaluate (tree_evaluator&, int nargout = 1);
+
+    octave_value_list evaluate_n (tree_evaluator& tw, int nargout = 1)
+    {
+      return ovl (evaluate (tw, nargout));
+    }
+
     void accept (tree_walker& tw)
     {
       tw.visit_postfix_expression (*this);