changeset 31809:32acdc376a36

maint: Remove unnecessary indent after OCTAVE_BEGIN_NAMESPACE in libinterp/ * ov.cc, bp-table.h, pt-eval.cc: Remove unnecessary indent after OCTAVE_BEGIN_NAMESPACE in libinterp/.
author Rik <rik@octave.org>
date Thu, 02 Feb 2023 12:32:15 -0800
parents 90621682cc03
children ce32aca67acf
files libinterp/octave-value/ov.cc libinterp/parse-tree/bp-table.h libinterp/parse-tree/pt-eval.cc
diffstat 3 files changed, 6140 insertions(+), 6140 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/ov.cc	Thu Feb 02 12:25:51 2023 -0800
+++ b/libinterp/octave-value/ov.cc	Thu Feb 02 12:32:15 2023 -0800
@@ -2658,950 +2658,950 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-  OCTAVE_NORETURN static void
-  err_binary_op (const std::string& on, const std::string& tn1,
-                 const std::string& tn2)
-  {
-    error ("binary operator '%s' not implemented for '%s' by '%s' operations",
-           on.c_str (), tn1.c_str (), tn2.c_str ());
-  }
-
-  OCTAVE_NORETURN static void
-  err_binary_op_conv (const std::string& on)
-  {
-    error ("type conversion failed for binary operator '%s'", on.c_str ());
-  }
-
-  octave_value
-  binary_op (type_info& ti, octave_value::binary_op op,
-             const octave_value& v1, const octave_value& v2)
-  {
-    octave_value retval;
-
-    int t1 = v1.type_id ();
-    int t2 = v2.type_id ();
-
-    if (t1 == octave_class::static_type_id ()
-        || t2 == octave_class::static_type_id ()
-        || t1 == octave_classdef::static_type_id ()
-        || t2 == octave_classdef::static_type_id ())
-      {
-        type_info::binary_class_op_fcn f = ti.lookup_binary_class_op (op);
-
-        if (! f)
-          err_binary_op (octave_value::binary_op_as_string (op),
-                         v1.class_name (), v2.class_name ());
-
+OCTAVE_NORETURN static void
+err_binary_op (const std::string& on, const std::string& tn1,
+               const std::string& tn2)
+{
+  error ("binary operator '%s' not implemented for '%s' by '%s' operations",
+         on.c_str (), tn1.c_str (), tn2.c_str ());
+}
+
+OCTAVE_NORETURN static void
+err_binary_op_conv (const std::string& on)
+{
+  error ("type conversion failed for binary operator '%s'", on.c_str ());
+}
+
+octave_value
+binary_op (type_info& ti, octave_value::binary_op op,
+           const octave_value& v1, const octave_value& v2)
+{
+  octave_value retval;
+
+  int t1 = v1.type_id ();
+  int t2 = v2.type_id ();
+
+  if (t1 == octave_class::static_type_id ()
+      || t2 == octave_class::static_type_id ()
+      || t1 == octave_classdef::static_type_id ()
+      || t2 == octave_classdef::static_type_id ())
+    {
+      type_info::binary_class_op_fcn f = ti.lookup_binary_class_op (op);
+
+      if (! f)
+        err_binary_op (octave_value::binary_op_as_string (op),
+                       v1.class_name (), v2.class_name ());
+
+      retval = f (v1, v2);
+    }
+  else
+    {
+      // FIXME: we need to handle overloading operators for built-in
+      // classes (double, char, int8, etc.)
+
+      type_info::binary_op_fcn f
+        = ti.lookup_binary_op (op, t1, t2);
+
+      if (f)
+        retval = f (v1.get_rep (), v2.get_rep ());
+      else
+        {
+          octave_value tv1;
+          octave_base_value::type_conv_info cf1
+            = v1.numeric_conversion_function ();
+
+          octave_value tv2;
+          octave_base_value::type_conv_info cf2
+            = v2.numeric_conversion_function ();
+
+          // Try biased (one-sided) conversions first.
+          if (cf2.type_id () >= 0
+              && ti.lookup_binary_op (op, t1, cf2.type_id ()))
+            cf1 = nullptr;
+          else if (cf1.type_id () >= 0
+                   && ti.lookup_binary_op (op, cf1.type_id (), t2))
+            cf2 = nullptr;
+
+          if (cf1)
+            {
+              octave_base_value *tmp = cf1 (v1.get_rep ());
+
+              if (! tmp)
+                err_binary_op_conv (octave_value::binary_op_as_string (op));
+
+              tv1 = octave_value (tmp);
+              t1 = tv1.type_id ();
+            }
+          else
+            tv1 = v1;
+
+          if (cf2)
+            {
+              octave_base_value *tmp = cf2 (v2.get_rep ());
+
+              if (! tmp)
+                err_binary_op_conv (octave_value::binary_op_as_string (op));
+
+              tv2 = octave_value (tmp);
+              t2 = tv2.type_id ();
+            }
+          else
+            tv2 = v2;
+
+          if (cf1 || cf2)
+            {
+              retval = binary_op (op, tv1, tv2);
+            }
+          else
+            {
+              //demote double -> single and try again
+              cf1 = tv1.numeric_demotion_function ();
+
+              cf2 = tv2.numeric_demotion_function ();
+
+              // Try biased (one-sided) conversions first.
+              if (cf2.type_id () >= 0
+                  && ti.lookup_binary_op (op, t1, cf2.type_id ()))
+                cf1 = nullptr;
+              else if (cf1.type_id () >= 0
+                       && ti.lookup_binary_op (op, cf1.type_id (), t2))
+                cf2 = nullptr;
+
+              if (cf1)
+                {
+                  octave_base_value *tmp = cf1 (tv1.get_rep ());
+
+                  if (! tmp)
+                    err_binary_op_conv (octave_value::binary_op_as_string (op));
+
+                  tv1 = octave_value (tmp);
+                  t1 = tv1.type_id ();
+                }
+
+              if (cf2)
+                {
+                  octave_base_value *tmp = cf2 (tv2.get_rep ());
+
+                  if (! tmp)
+                    err_binary_op_conv (octave_value::binary_op_as_string (op));
+
+                  tv2 = octave_value (tmp);
+                  t2 = tv2.type_id ();
+                }
+
+              if (! cf1 && ! cf2)
+                err_binary_op (octave_value::binary_op_as_string (op),
+                               v1.type_name (), v2.type_name ());
+
+              f = ti.lookup_binary_op (op, t1, t2);
+
+              if (! f)
+                err_binary_op (octave_value::binary_op_as_string (op),
+                               v1.type_name (), v2.type_name ());
+
+              retval = f (tv1.get_rep (), tv2.get_rep ());
+            }
+        }
+    }
+
+  return retval;
+}
+
+octave_value
+binary_op (octave_value::binary_op op, const octave_value& v1,
+           const octave_value& v2)
+{
+  type_info& ti = __get_type_info__ ();
+
+  return binary_op (ti, op, v1, v2);
+}
+
+static octave_value
+decompose_binary_op (type_info& ti, octave_value::compound_binary_op op,
+                     const octave_value& v1, const octave_value& v2)
+{
+  switch (op)
+    {
+    case octave_value::op_trans_mul:
+      return binary_op (octave_value::op_mul,
+                        unary_op (octave_value::op_transpose, v1), v2);
+
+    case octave_value::op_mul_trans:
+      return binary_op (ti, octave_value::op_mul,
+                        v1, unary_op (octave_value::op_transpose, v2));
+
+    case octave_value::op_herm_mul:
+      return binary_op (ti, octave_value::op_mul,
+                        unary_op (octave_value::op_hermitian, v1), v2);
+
+    case octave_value::op_mul_herm:
+      return binary_op (ti, octave_value::op_mul,
+                        v1, unary_op (octave_value::op_hermitian, v2));
+
+    case octave_value::op_trans_ldiv:
+      return binary_op (ti, octave_value::op_ldiv,
+                        unary_op (octave_value::op_transpose, v1), v2);
+
+    case octave_value::op_herm_ldiv:
+      return binary_op (ti, octave_value::op_ldiv,
+                        unary_op (octave_value::op_hermitian, v1), v2);
+
+    case octave_value::op_el_not_and:
+      return binary_op (ti, octave_value::op_el_and,
+                        unary_op (octave_value::op_not, v1), v2);
+
+    case octave_value::op_el_not_or:
+      return binary_op (ti, octave_value::op_el_or,
+                        unary_op (octave_value::op_not, v1), v2);
+
+    case octave_value::op_el_and_not:
+      return binary_op (ti, octave_value::op_el_and,
+                        v1, unary_op (octave_value::op_not, v2));
+
+    case octave_value::op_el_or_not:
+      return binary_op (ti, octave_value::op_el_or,
+                        v1, unary_op (octave_value::op_not, v2));
+
+    default:
+      error ("invalid compound operator");
+    }
+}
+
+octave_value
+binary_op (type_info& ti, octave_value::compound_binary_op op,
+           const octave_value& v1, const octave_value& v2)
+{
+  octave_value retval;
+
+  int t1 = v1.type_id ();
+  int t2 = v2.type_id ();
+
+  if (t1 == octave_class::static_type_id ()
+      || t2 == octave_class::static_type_id ()
+      || t1 == octave_classdef::static_type_id ()
+      || t2 == octave_classdef::static_type_id ())
+    {
+      type_info::binary_class_op_fcn f = ti.lookup_binary_class_op (op);
+
+      if (f)
         retval = f (v1, v2);
-      }
-    else
-      {
-        // FIXME: we need to handle overloading operators for built-in
-        // classes (double, char, int8, etc.)
-
-        type_info::binary_op_fcn f
-          = ti.lookup_binary_op (op, t1, t2);
-
-        if (f)
-          retval = f (v1.get_rep (), v2.get_rep ());
-        else
-          {
-            octave_value tv1;
-            octave_base_value::type_conv_info cf1
-              = v1.numeric_conversion_function ();
-
-            octave_value tv2;
-            octave_base_value::type_conv_info cf2
-              = v2.numeric_conversion_function ();
-
-            // Try biased (one-sided) conversions first.
-            if (cf2.type_id () >= 0
-                && ti.lookup_binary_op (op, t1, cf2.type_id ()))
-              cf1 = nullptr;
-            else if (cf1.type_id () >= 0
-                     && ti.lookup_binary_op (op, cf1.type_id (), t2))
-              cf2 = nullptr;
-
-            if (cf1)
-              {
-                octave_base_value *tmp = cf1 (v1.get_rep ());
-
-                if (! tmp)
-                  err_binary_op_conv (octave_value::binary_op_as_string (op));
-
-                tv1 = octave_value (tmp);
-                t1 = tv1.type_id ();
-              }
-            else
-              tv1 = v1;
-
-            if (cf2)
-              {
-                octave_base_value *tmp = cf2 (v2.get_rep ());
-
-                if (! tmp)
-                  err_binary_op_conv (octave_value::binary_op_as_string (op));
-
-                tv2 = octave_value (tmp);
-                t2 = tv2.type_id ();
-              }
-            else
-              tv2 = v2;
-
-            if (cf1 || cf2)
-              {
-                retval = binary_op (op, tv1, tv2);
-              }
-            else
-              {
-                //demote double -> single and try again
-                cf1 = tv1.numeric_demotion_function ();
-
-                cf2 = tv2.numeric_demotion_function ();
-
-                // Try biased (one-sided) conversions first.
-                if (cf2.type_id () >= 0
-                    && ti.lookup_binary_op (op, t1, cf2.type_id ()))
-                  cf1 = nullptr;
-                else if (cf1.type_id () >= 0
-                         && ti.lookup_binary_op (op, cf1.type_id (), t2))
-                  cf2 = nullptr;
-
-                if (cf1)
-                  {
-                    octave_base_value *tmp = cf1 (tv1.get_rep ());
-
-                    if (! tmp)
-                      err_binary_op_conv (octave_value::binary_op_as_string (op));
-
-                    tv1 = octave_value (tmp);
-                    t1 = tv1.type_id ();
-                  }
-
-                if (cf2)
-                  {
-                    octave_base_value *tmp = cf2 (tv2.get_rep ());
-
-                    if (! tmp)
-                      err_binary_op_conv (octave_value::binary_op_as_string (op));
-
-                    tv2 = octave_value (tmp);
-                    t2 = tv2.type_id ();
-                  }
-
-                if (! cf1 && ! cf2)
-                  err_binary_op (octave_value::binary_op_as_string (op),
-                                 v1.type_name (), v2.type_name ());
-
-                f = ti.lookup_binary_op (op, t1, t2);
-
-                if (! f)
-                  err_binary_op (octave_value::binary_op_as_string (op),
-                                 v1.type_name (), v2.type_name ());
-
-                retval = f (tv1.get_rep (), tv2.get_rep ());
-              }
-          }
-      }
-
-    return retval;
-  }
-
-  octave_value
-  binary_op (octave_value::binary_op op, const octave_value& v1,
-             const octave_value& v2)
-  {
-    type_info& ti = __get_type_info__ ();
-
-    return binary_op (ti, op, v1, v2);
-  }
-
-  static octave_value
-  decompose_binary_op (type_info& ti, octave_value::compound_binary_op op,
-                       const octave_value& v1, const octave_value& v2)
-  {
-    switch (op)
-      {
-      case octave_value::op_trans_mul:
-        return binary_op (octave_value::op_mul,
-                          unary_op (octave_value::op_transpose, v1), v2);
-
-      case octave_value::op_mul_trans:
-        return binary_op (ti, octave_value::op_mul,
-                          v1, unary_op (octave_value::op_transpose, v2));
-
-      case octave_value::op_herm_mul:
-        return binary_op (ti, octave_value::op_mul,
-                          unary_op (octave_value::op_hermitian, v1), v2);
-
-      case octave_value::op_mul_herm:
-        return binary_op (ti, octave_value::op_mul,
-                          v1, unary_op (octave_value::op_hermitian, v2));
-
-      case octave_value::op_trans_ldiv:
-        return binary_op (ti, octave_value::op_ldiv,
-                          unary_op (octave_value::op_transpose, v1), v2);
-
-      case octave_value::op_herm_ldiv:
-        return binary_op (ti, octave_value::op_ldiv,
-                          unary_op (octave_value::op_hermitian, v1), v2);
-
-      case octave_value::op_el_not_and:
-        return binary_op (ti, octave_value::op_el_and,
-                          unary_op (octave_value::op_not, v1), v2);
-
-      case octave_value::op_el_not_or:
-        return binary_op (ti, octave_value::op_el_or,
-                          unary_op (octave_value::op_not, v1), v2);
-
-      case octave_value::op_el_and_not:
-        return binary_op (ti, octave_value::op_el_and,
-                          v1, unary_op (octave_value::op_not, v2));
-
-      case octave_value::op_el_or_not:
-        return binary_op (ti, octave_value::op_el_or,
-                          v1, unary_op (octave_value::op_not, v2));
-
-      default:
-        error ("invalid compound operator");
-      }
-  }
-
-  octave_value
-  binary_op (type_info& ti, octave_value::compound_binary_op op,
-             const octave_value& v1, const octave_value& v2)
-  {
-    octave_value retval;
-
-    int t1 = v1.type_id ();
-    int t2 = v2.type_id ();
-
-    if (t1 == octave_class::static_type_id ()
-        || t2 == octave_class::static_type_id ()
-        || t1 == octave_classdef::static_type_id ()
-        || t2 == octave_classdef::static_type_id ())
-      {
-        type_info::binary_class_op_fcn f = ti.lookup_binary_class_op (op);
-
-        if (f)
-          retval = f (v1, v2);
-        else
-          retval = decompose_binary_op (ti, op, v1, v2);
-      }
-    else
-      {
-        type_info::binary_op_fcn f = ti.lookup_binary_op (op, t1, t2);
-
-        if (f)
-          retval = f (v1.get_rep (), v2.get_rep ());
-        else
-          retval = decompose_binary_op (ti, op, v1, v2);
-      }
-
-    return retval;
-  }
-
-  octave_value
-  binary_op (octave_value::compound_binary_op op,
-             const octave_value& v1, const octave_value& v2)
-  {
-    type_info& ti = __get_type_info__ ();
-
-    return binary_op (ti, op, v1, v2);
-  }
-
-  OCTAVE_NORETURN static void
-  err_cat_op (const std::string& tn1, const std::string& tn2)
-  {
-    error ("concatenation operator not implemented for '%s' by '%s' operations",
-           tn1.c_str (), tn2.c_str ());
-  }
-
-  OCTAVE_NORETURN static void
-  err_cat_op_conv ()
-  {
-    error ("type conversion failed for concatenation operator");
-  }
-
-  octave_value
-  cat_op (type_info& ti, const octave_value& v1,
-          const octave_value& v2, const Array<octave_idx_type>& ra_idx)
-  {
-    octave_value retval;
-
-    // Can't rapid return for concatenation with an empty object here as
-    // something like cat(1,[],single([]) must return the correct type.
-
-    int t1 = v1.type_id ();
-    int t2 = v2.type_id ();
-
-    type_info::cat_op_fcn f = ti.lookup_cat_op (t1, t2);
-
-    if (f)
-      retval = f (v1.get_rep (), v2.get_rep (), ra_idx);
-    else
-      {
-        octave_value tv1;
-        octave_base_value::type_conv_info cf1 = v1.numeric_conversion_function ();
-
-        octave_value tv2;
-        octave_base_value::type_conv_info cf2 = v2.numeric_conversion_function ();
-
-        // Try biased (one-sided) conversions first.
-        if (cf2.type_id () >= 0 && ti.lookup_cat_op (t1, cf2.type_id ()))
-          cf1 = nullptr;
-        else if (cf1.type_id () >= 0 && ti.lookup_cat_op (cf1.type_id (), t2))
-          cf2 = nullptr;
-
-        if (cf1)
-          {
-            octave_base_value *tmp = cf1 (v1.get_rep ());
-
-            if (! tmp)
-              err_cat_op_conv ();
-
-            tv1 = octave_value (tmp);
-            t1 = tv1.type_id ();
-          }
-        else
-          tv1 = v1;
-
-        if (cf2)
-          {
-            octave_base_value *tmp = cf2 (v2.get_rep ());
-
-            if (! tmp)
-              err_cat_op_conv ();
-
-            tv2 = octave_value (tmp);
-            t2 = tv2.type_id ();
-          }
-        else
-          tv2 = v2;
-
-        if (! cf1 && ! cf2)
-          err_cat_op (v1.type_name (), v2.type_name ());
-
-        retval = cat_op (ti, tv1, tv2, ra_idx);
-      }
-
-    return retval;
-  }
-
-  octave_value
-  cat_op (const octave_value& v1, const octave_value& v2,
-          const Array<octave_idx_type>& ra_idx)
-  {
-    type_info& ti = __get_type_info__ ();
-
-    return cat_op (ti, v1, v2, ra_idx);
-  }
-
-  // Unless the colon operator is used with a class or classdef object,
-  // then all arguments must be the same type or mixed with double
-  // values.
-
-  static builtin_type_t
-  get_colon_op_type (builtin_type_t op1_type, builtin_type_t op2_type)
-  {
-    if (op1_type == op2_type)
-      return op1_type;
-
-    if (op1_type == btyp_double)
-      return op2_type;
-
-    if (op2_type == btyp_double)
-      return op1_type;
-
-    return btyp_unknown;
-  }
-
-  static builtin_type_t
-  get_colon_op_type (const octave_value& base, const octave_value& increment,
-                     const octave_value& limit)
-  {
-    builtin_type_t typ
-      = get_colon_op_type (base.builtin_type (), increment.builtin_type ());
-
-    if (typ == btyp_unknown)
-      return typ;
-
-    return get_colon_op_type (typ, limit.builtin_type ());
-  }
-
-  // This check depends on the type of VAL either being the expected
-  // integer type or a double value.
-
-  template <typename T>
-  static void
-  check_colon_operand (const octave_value& val, const char *op_str)
-  {
-    if (! val.is_double_type ())
-      return;
-
-    double dval = val.double_value ();
-    double intpart;
-    static const double out_of_range_top
-      = static_cast<double> (std::numeric_limits<typename T::val_type>::max ())
-        + 1.;
-
-    if (dval >= out_of_range_top
-        || dval < std::numeric_limits<typename T::val_type>::min ()
-        || std::modf (dval, &intpart) != 0.0)
-      error ("colon operator %s invalid (not an integer or out of range for given integer type)", op_str);
-  }
-
-  // Return the difference between two unsigned integers as an unsigned
-  // integer of the same type.
-
-  template <typename UT,
-            typename std::enable_if<(std::is_integral<UT>::value
-                                     && std::is_unsigned<UT>::value),
-                                    bool>::type = true>
-  UT
-  integer_difference (UT a, UT b)
-  {
-    return a > b ? a - b : b - a;
-  }
-
-  // Return the difference between two signed integers as an unsigned
-  // integer corresponding to the signed type.
-
-  template <typename ST,
-            typename UT = typename std::make_unsigned<ST>::type,
-            typename std::enable_if<(std::is_integral<ST>::value
-                                     && std::is_signed<ST>::value),
-                                    bool>::type = true>
-  UT
-  integer_difference (ST a, ST b)
-  {
-    // Map to unsigned.
-    // Idea from https://stackoverflow.com/questions/10589559
-
-    static const UT offset
-      = UT (0) - static_cast<UT> (std::numeric_limits<ST>::min ());
-
-    UT au = static_cast<UT> (a) + offset;
-    UT bu = static_cast<UT> (b) + offset;
-
-    return integer_difference (au, bu);
-  }
-
-  // Number of elements in an integer range taking care to avoid
-  // overflow.  Base and limit are of the same type.  If they are
-  // unsigned, then increment is also of the same type.  If they are
-  // signed, then the type of increment is the unsigned type
-  // corresponding to T.  Assumes that the base and limit values are
-  // consistent with the sign of the original increment (not an empty
-  // range) so we can calculate numel with the absolute value of the
-  // increment and the absolute difference between the base and limit
-  // values.
-
-  template <typename T,
-            typename UT = typename std::make_unsigned<T>::type,
-            typename std::enable_if<std::is_integral<T>::value,
-                                    bool>::type = true>
-  octave_idx_type
-  range_numel_aux (T base, UT unsigned_increment, T limit)
-  {
-    // Adding one to DIFF/INCREMENT may overflow, so check whether it is
-    // out of range before adding.
-
-    UT nel_m1 = integer_difference (limit, base) / unsigned_increment;
-
-    // FIXME: fix error message.
-    if (nel_m1 > std::numeric_limits<octave_idx_type>::max () - 1)
-      error ("too many elements for range!");
-
-    return static_cast<octave_idx_type> (nel_m1) + 1;
-  }
-
-  // Convert signed range increment to unsigned.
-
-  template <typename ST,
-            typename UT = typename std::make_unsigned<ST>::type,
-            typename std::enable_if<(std::is_integral<ST>::value
-                                     && std::is_signed<ST>::value),
-                                    bool>::type = true>
-  UT
-  range_increment (ST increment)
-  {
-    return (increment < 0
-            ? UT (0) - static_cast<UT> (increment)
-            : static_cast<UT> (increment));
-  }
-
-  // "Convert" unsigned range increment to unsigned.  A no-op, but
-  // needed to provide a consistent interface for other template
-  // functions.
-
-  template <typename T,
-            typename UT = typename std::make_unsigned<T>::type,
-            typename std::enable_if<(std::is_integral<UT>::value
-                                     && std::is_unsigned<UT>::value),
-                                    bool>::type = true>
-  UT
-  range_increment (UT increment)
-  {
-    return increment;
-  }
-
-  // Convert double range increment to unsigned.  Enable by return type.
-
-  template <typename T,
-            typename UT = typename std::make_unsigned<T>::type>
-  typename std::enable_if<(std::is_integral<UT>::value
-                           && std::is_unsigned<UT>::value), UT>::type
-  range_increment (double increment)
-  {
-    double abs_increment = std::abs (increment);
-
-    return static_cast<UT> (abs_increment);
-  }
-
-  // Number of elements in an integer range base:increment:limit.  Base,
-  // increment, and limit are of the same signed type.
-
-  template <typename ST,
-            typename std::enable_if<(std::is_integral<ST>::value
-                                     && std::is_signed<ST>::value),
-                                    bool>::type = true>
-  octave_idx_type
-  range_numel (ST base, ST increment, ST limit)
-  {
-    typedef typename std::make_unsigned<ST>::type UT;
-
-    if (increment == 0
-        || (increment > 0 && base > limit)
-        || (increment < 0 && base < limit))
-      return 0;
-
-    UT unsigned_increment = range_increment<ST> (increment);
-
-    return range_numel_aux (base, unsigned_increment, limit);
-  }
-
-  // Number of elements in an integer range base:increment:limit.  Base,
-  // increment, and limit are unsigned and of the same type.
-
-  template <typename UT,
-            typename std::enable_if<(std::is_integral<UT>::value
-                                     && std::is_unsigned<UT>::value),
-                                    bool>::type = true>
-  octave_idx_type
-  range_numel (UT base, UT increment, UT limit)
-  {
-    // Unsigned, INCREMENT is always >= 0.
-    if (increment == 0 || base > limit)
-      return 0;
-
-    return range_numel_aux (base, increment, limit);
-  }
-
-  // Number of elements in an integer range base:increment:limit.  Base
-  // and limit are of the same type and increment is a double value.
-
-  template <typename T,
-            typename UT = typename std::make_unsigned<T>::type,
-            typename std::enable_if<std::is_integral<T>::value,
-                                    bool>::type = true>
-  octave_idx_type
-  range_numel (T base, double increment, T limit)
-  {
-    double intpart;
-    if (math::isnan (increment) || std::modf (increment, &intpart) != 0.0)
-      error ("colon operator increment invalid (not an integer)");
-
-    if (increment == 0
-        || (increment > 0 && base > limit)
-        || (increment < 0 && base < limit))
-      return 0;
-
-    static const double out_of_range_top
-      = static_cast<double> (std::numeric_limits<UT>::max ()) + 1.;
-
-    double abs_increment = std::abs (increment);
-
-    // Technically, this condition should be
-    // `abs_increment > std::numeric_limits<UT>::max ()`.
-    // But intmax('uint64') is not representable exactly as floating point
-    // number.  Instead, it "rounds" up by 1 to 2^64.  To account for
-    // this, use the following expression which works for all unsigned
-    // integer types.
-    if (abs_increment >= out_of_range_top)
-      return 1;
-
-    UT unsigned_increment = range_increment<T> (increment);
-
-    return range_numel_aux (base, unsigned_increment, limit);
-  }
-
-  // Make a range from integer values.  Increment may be integer or double.
-
-  template <typename T,
-            typename IT,
-            typename std::enable_if<(std::is_integral<T>::value
-                                     && std::is_arithmetic<IT>::value),
-                                    bool>::type = true>
-  octave_value
-  make_int_range (T base, IT increment, T limit)
-  {
-    octave_idx_type nel = range_numel (base, increment, limit);
-
-    // For now, we create arrays instead of range<T> for all types
-    // except double.
-
-    Array<octave_int<T>> result (dim_vector (1, nel));
-
-    if (nel > 0)
-      {
-        typedef typename std::make_unsigned<T>::type UT;
-
-        UT unsigned_increment = range_increment<T> (increment);
-
-        T val = base;
-        result.xelem (0) = val;
-
-        if (limit > base)
-          {
-            for (octave_idx_type i = 1; i < nel; i++)
-              {
-                val += unsigned_increment;
-                result.xelem (i) = val;
-              }
-          }
-        else
-          {
-            for (octave_idx_type i = 1; i < nel; i++)
-              {
-                val -= unsigned_increment;
-                result.xelem (i) = val;
-              }
-          }
-      }
-
-    return octave_value (result);
-  }
-
-  // Make a range from floating point values.
-
-  // FIXME: Try again to define memory efficient range classes for
-  // integer and floating point values?  Maybe with the templates
-  // defined in this file we could do that in a reasonable way?
-  // Regardless of that, it might be good to provide special treatment
-  // of colon expressions in FOR loops so that we can eliminate the
-  // "is_for_cmd_expr / force_range" flag from the parser and the
-  // octave_value constructors for range objects.
-
-  // NOTE: We define this function separately for float and double so
-  // that we can avoid having to instantiate ov_range<float>.  We DO
-  // instantiate range<float> but only so we can take advantage of the
-  // range<T> class to generate the corresponding array of float values
-  // and not have to duplicate that code here.
-
-  template <typename T,
-            typename std::enable_if<std::is_same<T, double>::value,
-                                    bool>::type = true>
-  octave_value
-  make_float_range (T base, T increment, T limit, bool is_for_cmd_expr)
-  {
-    if (math::isnan (base)
-        || math::isnan (increment)
-        || math::isnan (limit))
-      return octave_value (numeric_limits<T>::NaN ());
-
-    if (increment == 0
-        || (increment > 0 && base > limit)
-        || (increment < 0 && base < limit))
-      return octave_value (Array<T> (dim_vector (1, 0)));
-
-    // At this point, we know that the base and limit values are
-    // consistent with the sign of the increment (not an empty range).
-
-    range<T> r (base, increment, limit);
-
-    if (! is_for_cmd_expr && ! r.is_storable ())
-      error ("range with infinite number of elements cannot be stored");
-
-    return octave_value (r, is_for_cmd_expr);
-  }
-
-  template <typename T,
-            typename std::enable_if<std::is_same<T, float>::value,
-                                    bool>::type = true>
-  octave_value
-  make_float_range (T base, T increment, T limit, bool is_for_cmd_expr)
-  {
-    if (math::isnan (base)
-        || math::isnan (increment)
-        || math::isnan (limit))
-      return octave_value (numeric_limits<T>::NaN ());
-
-    if (increment == 0
-        || (increment > 0 && base > limit)
-        || (increment < 0 && base < limit))
-      return octave_value (Array<T> (dim_vector (1, 0)));
-
-    // At this point, we know that the base and limit values are
-    // consistent with the sign of the increment (not an empty range).
-
-    range<T> r (base, increment, limit);
-
-    if (! is_for_cmd_expr && ! r.is_storable ())
-      error ("range with infinite number of elements cannot be stored");
-
-    return octave_value (r.array_value ());
-  }
-
-  template <typename T,
-            typename std::enable_if<(std::is_same<T, octave_int8>::value
-                                     || std::is_same<T, octave_uint8>::value
-                                     || std::is_same<T, octave_int16>::value
-                                     || std::is_same<T, octave_uint16>::value
-                                     || std::is_same<T, octave_int32>::value
-                                     || std::is_same<T, octave_uint32>::value
-                                     || std::is_same<T, octave_int64>::value
-                                     || std::is_same<T, octave_uint64>::value),
-                                    bool>::type = true>
-  octave_value
-  make_int_range (const octave_value& base, const octave_value& increment,
-                  const octave_value& limit)
-  {
-    if (base.isempty () || increment.isempty () || limit.isempty ())
-      return octave_value (Array<T> (dim_vector (1, 0)));
-
-    check_colon_operand<T> (base, "lower bound");
-    check_colon_operand<T> (limit, "upper bound");
-
-    typename T::val_type base_val = octave_value_extract<T> (base).value ();
-    typename T::val_type limit_val = octave_value_extract<T> (limit).value ();
-
-    if (increment.is_double_type ())
-      {
-        double increment_val = increment.double_value ();
-
-        return make_int_range (base_val, increment_val, limit_val);
-      }
-
-    check_colon_operand<T> (increment, "increment");
-
-    typename T::val_type increment_val
-      = octave_value_extract<T> (increment).value ();
-
-    return make_int_range (base_val, increment_val, limit_val);
-  }
-
-  template <typename T,
-            typename std::enable_if<std::is_floating_point<T>::value,
-                                    bool>::type = true>
-  octave_value
-  make_float_range (const octave_value& base, const octave_value& increment,
-                    const octave_value& limit, bool is_for_cmd_expr)
-  {
-    if (base.isempty () || increment.isempty () || limit.isempty ())
-      return octave_value (Array<T> (dim_vector (1, 0)));
-
-    T base_val = octave_value_extract<T> (base);
-    T increment_val = octave_value_extract<T> (increment);
-    T limit_val = octave_value_extract<T> (limit);
-
-    return make_float_range (base_val, increment_val, limit_val,
-                             is_for_cmd_expr);
-  }
-
-
-  octave_value
-  make_char_range (const octave_value& base, const octave_value& increment,
+      else
+        retval = decompose_binary_op (ti, op, v1, v2);
+    }
+  else
+    {
+      type_info::binary_op_fcn f = ti.lookup_binary_op (op, t1, t2);
+
+      if (f)
+        retval = f (v1.get_rep (), v2.get_rep ());
+      else
+        retval = decompose_binary_op (ti, op, v1, v2);
+    }
+
+  return retval;
+}
+
+octave_value
+binary_op (octave_value::compound_binary_op op,
+           const octave_value& v1, const octave_value& v2)
+{
+  type_info& ti = __get_type_info__ ();
+
+  return binary_op (ti, op, v1, v2);
+}
+
+OCTAVE_NORETURN static void
+err_cat_op (const std::string& tn1, const std::string& tn2)
+{
+  error ("concatenation operator not implemented for '%s' by '%s' operations",
+         tn1.c_str (), tn2.c_str ());
+}
+
+OCTAVE_NORETURN static void
+err_cat_op_conv ()
+{
+  error ("type conversion failed for concatenation operator");
+}
+
+octave_value
+cat_op (type_info& ti, const octave_value& v1,
+        const octave_value& v2, const Array<octave_idx_type>& ra_idx)
+{
+  octave_value retval;
+
+  // Can't rapid return for concatenation with an empty object here as
+  // something like cat(1,[],single([]) must return the correct type.
+
+  int t1 = v1.type_id ();
+  int t2 = v2.type_id ();
+
+  type_info::cat_op_fcn f = ti.lookup_cat_op (t1, t2);
+
+  if (f)
+    retval = f (v1.get_rep (), v2.get_rep (), ra_idx);
+  else
+    {
+      octave_value tv1;
+      octave_base_value::type_conv_info cf1 = v1.numeric_conversion_function ();
+
+      octave_value tv2;
+      octave_base_value::type_conv_info cf2 = v2.numeric_conversion_function ();
+
+      // Try biased (one-sided) conversions first.
+      if (cf2.type_id () >= 0 && ti.lookup_cat_op (t1, cf2.type_id ()))
+        cf1 = nullptr;
+      else if (cf1.type_id () >= 0 && ti.lookup_cat_op (cf1.type_id (), t2))
+        cf2 = nullptr;
+
+      if (cf1)
+        {
+          octave_base_value *tmp = cf1 (v1.get_rep ());
+
+          if (! tmp)
+            err_cat_op_conv ();
+
+          tv1 = octave_value (tmp);
+          t1 = tv1.type_id ();
+        }
+      else
+        tv1 = v1;
+
+      if (cf2)
+        {
+          octave_base_value *tmp = cf2 (v2.get_rep ());
+
+          if (! tmp)
+            err_cat_op_conv ();
+
+          tv2 = octave_value (tmp);
+          t2 = tv2.type_id ();
+        }
+      else
+        tv2 = v2;
+
+      if (! cf1 && ! cf2)
+        err_cat_op (v1.type_name (), v2.type_name ());
+
+      retval = cat_op (ti, tv1, tv2, ra_idx);
+    }
+
+  return retval;
+}
+
+octave_value
+cat_op (const octave_value& v1, const octave_value& v2,
+        const Array<octave_idx_type>& ra_idx)
+{
+  type_info& ti = __get_type_info__ ();
+
+  return cat_op (ti, v1, v2, ra_idx);
+}
+
+// Unless the colon operator is used with a class or classdef object,
+// then all arguments must be the same type or mixed with double
+// values.
+
+static builtin_type_t
+get_colon_op_type (builtin_type_t op1_type, builtin_type_t op2_type)
+{
+  if (op1_type == op2_type)
+    return op1_type;
+
+  if (op1_type == btyp_double)
+    return op2_type;
+
+  if (op2_type == btyp_double)
+    return op1_type;
+
+  return btyp_unknown;
+}
+
+static builtin_type_t
+get_colon_op_type (const octave_value& base, const octave_value& increment,
                    const octave_value& limit)
-  {
-    octave_value retval;
-
-    bool dq_str = (base.is_dq_string () || increment.is_dq_string ()
-                   || limit.is_dq_string ());
-
-    char type = dq_str ? '"' : '\'';
-
-    if (base.isempty () || increment.isempty () || limit.isempty ())
-      retval = octave_value ("", type);
-    else
-      {
-        Matrix mtx_base = base.matrix_value (true);
-        Matrix mtx_increment = increment.matrix_value (true);
-        Matrix mtx_limit = limit.matrix_value (true);
-
-        range<double> tmp (mtx_base(0), mtx_increment(0), mtx_limit(0));
-
-        retval = octave_value (tmp);
-      }
-
-    return retval.convert_to_str (false, true, type);
-  }
-
-  octave_value
-  colon_op (const octave_value& base, const octave_value& increment_arg,
-            const octave_value& limit, bool is_for_cmd_expr)
-  {
-    if (base.isobject () || increment_arg.isobject () || limit.isobject ())
-      {
-        octave_value_list tmp1;
-
-        if (increment_arg.is_defined ())
-          {
-            tmp1(2) = limit;
-            tmp1(1) = increment_arg;
-            tmp1(0) = base;
-          }
-        else
-          {
-            tmp1(1) = limit;
-            tmp1(0) = base;
-          }
-
-        interpreter& interp = __get_interpreter__ ();
-
-        symbol_table& symtab = interp.get_symbol_table ();
-
-        octave_value fcn = symtab.find_function ("colon", tmp1);
-
-        if (fcn.is_defined ())
-          {
-            octave_value_list tmp2 = interp.feval (fcn, tmp1, 1);
-
-            return tmp2(0);
-          }
-      }
-
-    octave_value increment
-      = increment_arg.is_defined () ? increment_arg : octave_value (1.0);
-
-    if (base.numel () > 1 || limit.numel () > 1 || increment.numel () > 1)
-      warning_with_id ("Octave:colon-nonscalar-argument",
-                       "colon arguments should be scalars");
-
-    if (base.iscomplex () || limit.iscomplex () || increment.iscomplex ())
-      warning_with_id ("Octave:colon-complex-argument",
-                       "imaginary part of complex colon arguments is ignored");
-
-    // FIXME: is there a better way to do this job, maybe using type traits?
-
-    builtin_type_t type_id = get_colon_op_type (base, increment, limit);
-
-    // For compatibility with Matlab, don't allow the range used in
-    // a FOR loop expression to be converted to a Matrix.
-
-    // For now, these functions create arrays instead of range<T> for
-    // all types except double.
-
-    switch (type_id)
-      {
-      case btyp_double:
-      case btyp_complex:
-        return make_float_range<double> (base, increment, limit, is_for_cmd_expr);
-
-      case btyp_float:
-      case btyp_float_complex:
-        return make_float_range<float> (base, increment, limit, is_for_cmd_expr);
-
-      case btyp_int8:
-        return make_int_range<octave_int8> (base, increment, limit);
-
-      case btyp_int16:
-        return make_int_range<octave_int16> (base, increment, limit);
-
-      case btyp_int32:
-        return make_int_range<octave_int32> (base, increment, limit);
-
-      case btyp_int64:
-        return make_int_range<octave_int64> (base, increment, limit);
-
-      case btyp_uint8:
-        return make_int_range<octave_uint8> (base, increment, limit);
-
-      case btyp_uint16:
-        return make_int_range<octave_uint16> (base, increment, limit);
-
-      case btyp_uint32:
-        return make_int_range<octave_uint32> (base, increment, limit);
-
-      case btyp_uint64:
-        return make_int_range<octave_uint64> (base, increment, limit);
-
-      case btyp_char:
-        return make_char_range (base, increment, limit);
-
-      case btyp_unknown:
-        error ("incompatible types found in range expression");
-
-      default:
-        error ("invalid types found in range expression");
-      }
-
-    return octave_value ();
-  }
-
-  OCTAVE_NORETURN static void
-  err_unary_op_conv (const std::string& on)
-  {
-    error ("type conversion failed for unary operator '%s'", on.c_str ());
-  }
-
-  octave_value
-  unary_op (type_info& ti, octave_value::unary_op op,
-            const octave_value& v)
-  {
-    octave_value retval;
-
-    int t = v.type_id ();
-
-    if (t == octave_class::static_type_id ()
-        || t == octave_classdef::static_type_id ())
-      {
-        type_info::unary_class_op_fcn f = ti.lookup_unary_class_op (op);
-
-        if (! f)
-          err_unary_op (octave_value::unary_op_as_string (op), v.class_name ());
-
-        retval = f (v);
-      }
-    else
-      {
-        // FIXME: we need to handle overloading operators for built-in
-        // classes (double, char, int8, etc.)
-
-        type_info::unary_op_fcn f = ti.lookup_unary_op (op, t);
-
-        if (f)
-          retval = f (v.get_rep ());
-        else
-          {
-            octave_value tv;
-            octave_base_value::type_conv_fcn cf
-              = v.numeric_conversion_function ();
-
-            if (! cf)
-              err_unary_op (octave_value::unary_op_as_string (op),
-                            v.type_name ());
-
-            octave_base_value *tmp = cf (v.get_rep ());
-
-            if (! tmp)
-              err_unary_op_conv (octave_value::unary_op_as_string (op));
-
-            tv = octave_value (tmp);
-            retval = unary_op (op, tv);
-          }
-      }
-
-    return retval;
-  }
-
-  octave_value
-  unary_op (octave_value::unary_op op, const octave_value& v)
-  {
-    type_info& ti = __get_type_info__ ();
-
-    return unary_op (ti, op, v);
-  }
+{
+  builtin_type_t typ
+    = get_colon_op_type (base.builtin_type (), increment.builtin_type ());
+
+  if (typ == btyp_unknown)
+    return typ;
+
+  return get_colon_op_type (typ, limit.builtin_type ());
+}
+
+// This check depends on the type of VAL either being the expected
+// integer type or a double value.
+
+template <typename T>
+static void
+check_colon_operand (const octave_value& val, const char *op_str)
+{
+  if (! val.is_double_type ())
+    return;
+
+  double dval = val.double_value ();
+  double intpart;
+  static const double out_of_range_top
+    = static_cast<double> (std::numeric_limits<typename T::val_type>::max ())
+      + 1.;
+
+  if (dval >= out_of_range_top
+      || dval < std::numeric_limits<typename T::val_type>::min ()
+      || std::modf (dval, &intpart) != 0.0)
+    error ("colon operator %s invalid (not an integer or out of range for given integer type)", op_str);
+}
+
+// Return the difference between two unsigned integers as an unsigned
+// integer of the same type.
+
+template <typename UT,
+          typename std::enable_if<(std::is_integral<UT>::value
+                                   && std::is_unsigned<UT>::value),
+                                  bool>::type = true>
+UT
+integer_difference (UT a, UT b)
+{
+  return a > b ? a - b : b - a;
+}
+
+// Return the difference between two signed integers as an unsigned
+// integer corresponding to the signed type.
+
+template <typename ST,
+          typename UT = typename std::make_unsigned<ST>::type,
+          typename std::enable_if<(std::is_integral<ST>::value
+                                   && std::is_signed<ST>::value),
+                                  bool>::type = true>
+UT
+integer_difference (ST a, ST b)
+{
+  // Map to unsigned.
+  // Idea from https://stackoverflow.com/questions/10589559
+
+  static const UT offset
+    = UT (0) - static_cast<UT> (std::numeric_limits<ST>::min ());
+
+  UT au = static_cast<UT> (a) + offset;
+  UT bu = static_cast<UT> (b) + offset;
+
+  return integer_difference (au, bu);
+}
+
+// Number of elements in an integer range taking care to avoid
+// overflow.  Base and limit are of the same type.  If they are
+// unsigned, then increment is also of the same type.  If they are
+// signed, then the type of increment is the unsigned type
+// corresponding to T.  Assumes that the base and limit values are
+// consistent with the sign of the original increment (not an empty
+// range) so we can calculate numel with the absolute value of the
+// increment and the absolute difference between the base and limit
+// values.
+
+template <typename T,
+          typename UT = typename std::make_unsigned<T>::type,
+          typename std::enable_if<std::is_integral<T>::value,
+                                  bool>::type = true>
+octave_idx_type
+range_numel_aux (T base, UT unsigned_increment, T limit)
+{
+  // Adding one to DIFF/INCREMENT may overflow, so check whether it is
+  // out of range before adding.
+
+  UT nel_m1 = integer_difference (limit, base) / unsigned_increment;
+
+  // FIXME: fix error message.
+  if (nel_m1 > std::numeric_limits<octave_idx_type>::max () - 1)
+    error ("too many elements for range!");
+
+  return static_cast<octave_idx_type> (nel_m1) + 1;
+}
+
+// Convert signed range increment to unsigned.
+
+template <typename ST,
+          typename UT = typename std::make_unsigned<ST>::type,
+          typename std::enable_if<(std::is_integral<ST>::value
+                                   && std::is_signed<ST>::value),
+                                  bool>::type = true>
+UT
+range_increment (ST increment)
+{
+  return (increment < 0
+          ? UT (0) - static_cast<UT> (increment)
+          : static_cast<UT> (increment));
+}
+
+// "Convert" unsigned range increment to unsigned.  A no-op, but
+// needed to provide a consistent interface for other template
+// functions.
+
+template <typename T,
+          typename UT = typename std::make_unsigned<T>::type,
+          typename std::enable_if<(std::is_integral<UT>::value
+                                   && std::is_unsigned<UT>::value),
+                                  bool>::type = true>
+UT
+range_increment (UT increment)
+{
+  return increment;
+}
+
+// Convert double range increment to unsigned.  Enable by return type.
+
+template <typename T,
+          typename UT = typename std::make_unsigned<T>::type>
+typename std::enable_if<(std::is_integral<UT>::value
+                         && std::is_unsigned<UT>::value), UT>::type
+range_increment (double increment)
+{
+  double abs_increment = std::abs (increment);
+
+  return static_cast<UT> (abs_increment);
+}
+
+// Number of elements in an integer range base:increment:limit.  Base,
+// increment, and limit are of the same signed type.
+
+template <typename ST,
+          typename std::enable_if<(std::is_integral<ST>::value
+                                   && std::is_signed<ST>::value),
+                                  bool>::type = true>
+octave_idx_type
+range_numel (ST base, ST increment, ST limit)
+{
+  typedef typename std::make_unsigned<ST>::type UT;
+
+  if (increment == 0
+      || (increment > 0 && base > limit)
+      || (increment < 0 && base < limit))
+    return 0;
+
+  UT unsigned_increment = range_increment<ST> (increment);
+
+  return range_numel_aux (base, unsigned_increment, limit);
+}
+
+// Number of elements in an integer range base:increment:limit.  Base,
+// increment, and limit are unsigned and of the same type.
+
+template <typename UT,
+          typename std::enable_if<(std::is_integral<UT>::value
+                                   && std::is_unsigned<UT>::value),
+                                  bool>::type = true>
+octave_idx_type
+range_numel (UT base, UT increment, UT limit)
+{
+  // Unsigned, INCREMENT is always >= 0.
+  if (increment == 0 || base > limit)
+    return 0;
+
+  return range_numel_aux (base, increment, limit);
+}
+
+// Number of elements in an integer range base:increment:limit.  Base
+// and limit are of the same type and increment is a double value.
+
+template <typename T,
+          typename UT = typename std::make_unsigned<T>::type,
+          typename std::enable_if<std::is_integral<T>::value,
+                                  bool>::type = true>
+octave_idx_type
+range_numel (T base, double increment, T limit)
+{
+  double intpart;
+  if (math::isnan (increment) || std::modf (increment, &intpart) != 0.0)
+    error ("colon operator increment invalid (not an integer)");
+
+  if (increment == 0
+      || (increment > 0 && base > limit)
+      || (increment < 0 && base < limit))
+    return 0;
+
+  static const double out_of_range_top
+    = static_cast<double> (std::numeric_limits<UT>::max ()) + 1.;
+
+  double abs_increment = std::abs (increment);
+
+  // Technically, this condition should be
+  // `abs_increment > std::numeric_limits<UT>::max ()`.
+  // But intmax('uint64') is not representable exactly as floating point
+  // number.  Instead, it "rounds" up by 1 to 2^64.  To account for
+  // this, use the following expression which works for all unsigned
+  // integer types.
+  if (abs_increment >= out_of_range_top)
+    return 1;
+
+  UT unsigned_increment = range_increment<T> (increment);
+
+  return range_numel_aux (base, unsigned_increment, limit);
+}
+
+// Make a range from integer values.  Increment may be integer or double.
+
+template <typename T,
+          typename IT,
+          typename std::enable_if<(std::is_integral<T>::value
+                                   && std::is_arithmetic<IT>::value),
+                                  bool>::type = true>
+octave_value
+make_int_range (T base, IT increment, T limit)
+{
+  octave_idx_type nel = range_numel (base, increment, limit);
+
+  // For now, we create arrays instead of range<T> for all types
+  // except double.
+
+  Array<octave_int<T>> result (dim_vector (1, nel));
+
+  if (nel > 0)
+    {
+      typedef typename std::make_unsigned<T>::type UT;
+
+      UT unsigned_increment = range_increment<T> (increment);
+
+      T val = base;
+      result.xelem (0) = val;
+
+      if (limit > base)
+        {
+          for (octave_idx_type i = 1; i < nel; i++)
+            {
+              val += unsigned_increment;
+              result.xelem (i) = val;
+            }
+        }
+      else
+        {
+          for (octave_idx_type i = 1; i < nel; i++)
+            {
+              val -= unsigned_increment;
+              result.xelem (i) = val;
+            }
+        }
+    }
+
+  return octave_value (result);
+}
+
+// Make a range from floating point values.
+
+// FIXME: Try again to define memory efficient range classes for
+// integer and floating point values?  Maybe with the templates
+// defined in this file we could do that in a reasonable way?
+// Regardless of that, it might be good to provide special treatment
+// of colon expressions in FOR loops so that we can eliminate the
+// "is_for_cmd_expr / force_range" flag from the parser and the
+// octave_value constructors for range objects.
+
+// NOTE: We define this function separately for float and double so
+// that we can avoid having to instantiate ov_range<float>.  We DO
+// instantiate range<float> but only so we can take advantage of the
+// range<T> class to generate the corresponding array of float values
+// and not have to duplicate that code here.
+
+template <typename T,
+          typename std::enable_if<std::is_same<T, double>::value,
+                                  bool>::type = true>
+octave_value
+make_float_range (T base, T increment, T limit, bool is_for_cmd_expr)
+{
+  if (math::isnan (base)
+      || math::isnan (increment)
+      || math::isnan (limit))
+    return octave_value (numeric_limits<T>::NaN ());
+
+  if (increment == 0
+      || (increment > 0 && base > limit)
+      || (increment < 0 && base < limit))
+    return octave_value (Array<T> (dim_vector (1, 0)));
+
+  // At this point, we know that the base and limit values are
+  // consistent with the sign of the increment (not an empty range).
+
+  range<T> r (base, increment, limit);
+
+  if (! is_for_cmd_expr && ! r.is_storable ())
+    error ("range with infinite number of elements cannot be stored");
+
+  return octave_value (r, is_for_cmd_expr);
+}
+
+template <typename T,
+          typename std::enable_if<std::is_same<T, float>::value,
+                                  bool>::type = true>
+octave_value
+make_float_range (T base, T increment, T limit, bool is_for_cmd_expr)
+{
+  if (math::isnan (base)
+      || math::isnan (increment)
+      || math::isnan (limit))
+    return octave_value (numeric_limits<T>::NaN ());
+
+  if (increment == 0
+      || (increment > 0 && base > limit)
+      || (increment < 0 && base < limit))
+    return octave_value (Array<T> (dim_vector (1, 0)));
+
+  // At this point, we know that the base and limit values are
+  // consistent with the sign of the increment (not an empty range).
+
+  range<T> r (base, increment, limit);
+
+  if (! is_for_cmd_expr && ! r.is_storable ())
+    error ("range with infinite number of elements cannot be stored");
+
+  return octave_value (r.array_value ());
+}
+
+template <typename T,
+          typename std::enable_if<(std::is_same<T, octave_int8>::value
+                                   || std::is_same<T, octave_uint8>::value
+                                   || std::is_same<T, octave_int16>::value
+                                   || std::is_same<T, octave_uint16>::value
+                                   || std::is_same<T, octave_int32>::value
+                                   || std::is_same<T, octave_uint32>::value
+                                   || std::is_same<T, octave_int64>::value
+                                   || std::is_same<T, octave_uint64>::value),
+                                  bool>::type = true>
+octave_value
+make_int_range (const octave_value& base, const octave_value& increment,
+                const octave_value& limit)
+{
+  if (base.isempty () || increment.isempty () || limit.isempty ())
+    return octave_value (Array<T> (dim_vector (1, 0)));
+
+  check_colon_operand<T> (base, "lower bound");
+  check_colon_operand<T> (limit, "upper bound");
+
+  typename T::val_type base_val = octave_value_extract<T> (base).value ();
+  typename T::val_type limit_val = octave_value_extract<T> (limit).value ();
+
+  if (increment.is_double_type ())
+    {
+      double increment_val = increment.double_value ();
+
+      return make_int_range (base_val, increment_val, limit_val);
+    }
+
+  check_colon_operand<T> (increment, "increment");
+
+  typename T::val_type increment_val
+    = octave_value_extract<T> (increment).value ();
+
+  return make_int_range (base_val, increment_val, limit_val);
+}
+
+template <typename T,
+          typename std::enable_if<std::is_floating_point<T>::value,
+                                  bool>::type = true>
+octave_value
+make_float_range (const octave_value& base, const octave_value& increment,
+                  const octave_value& limit, bool is_for_cmd_expr)
+{
+  if (base.isempty () || increment.isempty () || limit.isempty ())
+    return octave_value (Array<T> (dim_vector (1, 0)));
+
+  T base_val = octave_value_extract<T> (base);
+  T increment_val = octave_value_extract<T> (increment);
+  T limit_val = octave_value_extract<T> (limit);
+
+  return make_float_range (base_val, increment_val, limit_val,
+                           is_for_cmd_expr);
+}
+
+
+octave_value
+make_char_range (const octave_value& base, const octave_value& increment,
+                 const octave_value& limit)
+{
+  octave_value retval;
+
+  bool dq_str = (base.is_dq_string () || increment.is_dq_string ()
+                 || limit.is_dq_string ());
+
+  char type = dq_str ? '"' : '\'';
+
+  if (base.isempty () || increment.isempty () || limit.isempty ())
+    retval = octave_value ("", type);
+  else
+    {
+      Matrix mtx_base = base.matrix_value (true);
+      Matrix mtx_increment = increment.matrix_value (true);
+      Matrix mtx_limit = limit.matrix_value (true);
+
+      range<double> tmp (mtx_base(0), mtx_increment(0), mtx_limit(0));
+
+      retval = octave_value (tmp);
+    }
+
+  return retval.convert_to_str (false, true, type);
+}
+
+octave_value
+colon_op (const octave_value& base, const octave_value& increment_arg,
+          const octave_value& limit, bool is_for_cmd_expr)
+{
+  if (base.isobject () || increment_arg.isobject () || limit.isobject ())
+    {
+      octave_value_list tmp1;
+
+      if (increment_arg.is_defined ())
+        {
+          tmp1(2) = limit;
+          tmp1(1) = increment_arg;
+          tmp1(0) = base;
+        }
+      else
+        {
+          tmp1(1) = limit;
+          tmp1(0) = base;
+        }
+
+      interpreter& interp = __get_interpreter__ ();
+
+      symbol_table& symtab = interp.get_symbol_table ();
+
+      octave_value fcn = symtab.find_function ("colon", tmp1);
+
+      if (fcn.is_defined ())
+        {
+          octave_value_list tmp2 = interp.feval (fcn, tmp1, 1);
+
+          return tmp2(0);
+        }
+    }
+
+  octave_value increment
+    = increment_arg.is_defined () ? increment_arg : octave_value (1.0);
+
+  if (base.numel () > 1 || limit.numel () > 1 || increment.numel () > 1)
+    warning_with_id ("Octave:colon-nonscalar-argument",
+                     "colon arguments should be scalars");
+
+  if (base.iscomplex () || limit.iscomplex () || increment.iscomplex ())
+    warning_with_id ("Octave:colon-complex-argument",
+                     "imaginary part of complex colon arguments is ignored");
+
+  // FIXME: is there a better way to do this job, maybe using type traits?
+
+  builtin_type_t type_id = get_colon_op_type (base, increment, limit);
+
+  // For compatibility with Matlab, don't allow the range used in
+  // a FOR loop expression to be converted to a Matrix.
+
+  // For now, these functions create arrays instead of range<T> for
+  // all types except double.
+
+  switch (type_id)
+    {
+    case btyp_double:
+    case btyp_complex:
+      return make_float_range<double> (base, increment, limit, is_for_cmd_expr);
+
+    case btyp_float:
+    case btyp_float_complex:
+      return make_float_range<float> (base, increment, limit, is_for_cmd_expr);
+
+    case btyp_int8:
+      return make_int_range<octave_int8> (base, increment, limit);
+
+    case btyp_int16:
+      return make_int_range<octave_int16> (base, increment, limit);
+
+    case btyp_int32:
+      return make_int_range<octave_int32> (base, increment, limit);
+
+    case btyp_int64:
+      return make_int_range<octave_int64> (base, increment, limit);
+
+    case btyp_uint8:
+      return make_int_range<octave_uint8> (base, increment, limit);
+
+    case btyp_uint16:
+      return make_int_range<octave_uint16> (base, increment, limit);
+
+    case btyp_uint32:
+      return make_int_range<octave_uint32> (base, increment, limit);
+
+    case btyp_uint64:
+      return make_int_range<octave_uint64> (base, increment, limit);
+
+    case btyp_char:
+      return make_char_range (base, increment, limit);
+
+    case btyp_unknown:
+      error ("incompatible types found in range expression");
+
+    default:
+      error ("invalid types found in range expression");
+    }
+
+  return octave_value ();
+}
+
+OCTAVE_NORETURN static void
+err_unary_op_conv (const std::string& on)
+{
+  error ("type conversion failed for unary operator '%s'", on.c_str ());
+}
+
+octave_value
+unary_op (type_info& ti, octave_value::unary_op op,
+          const octave_value& v)
+{
+  octave_value retval;
+
+  int t = v.type_id ();
+
+  if (t == octave_class::static_type_id ()
+      || t == octave_classdef::static_type_id ())
+    {
+      type_info::unary_class_op_fcn f = ti.lookup_unary_class_op (op);
+
+      if (! f)
+        err_unary_op (octave_value::unary_op_as_string (op), v.class_name ());
+
+      retval = f (v);
+    }
+  else
+    {
+      // FIXME: we need to handle overloading operators for built-in
+      // classes (double, char, int8, etc.)
+
+      type_info::unary_op_fcn f = ti.lookup_unary_op (op, t);
+
+      if (f)
+        retval = f (v.get_rep ());
+      else
+        {
+          octave_value tv;
+          octave_base_value::type_conv_fcn cf
+            = v.numeric_conversion_function ();
+
+          if (! cf)
+            err_unary_op (octave_value::unary_op_as_string (op),
+                          v.type_name ());
+
+          octave_base_value *tmp = cf (v.get_rep ());
+
+          if (! tmp)
+            err_unary_op_conv (octave_value::unary_op_as_string (op));
+
+          tv = octave_value (tmp);
+          retval = unary_op (op, tv);
+        }
+    }
+
+  return retval;
+}
+
+octave_value
+unary_op (octave_value::unary_op op, const octave_value& v)
+{
+  type_info& ti = __get_type_info__ ();
+
+  return unary_op (ti, op, v);
+}
 
 OCTAVE_END_NAMESPACE(octave)
 
--- a/libinterp/parse-tree/bp-table.h	Thu Feb 02 12:25:51 2023 -0800
+++ b/libinterp/parse-tree/bp-table.h	Thu Feb 02 12:32:15 2023 -0800
@@ -39,172 +39,172 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-  class tree_evaluator;
+class tree_evaluator;
 
-  struct bp_type
-  {
-  public:
-    bp_type (int l, const std::string& c) : line (l), cond (c) { }
+struct bp_type
+{
+public:
+  bp_type (int l, const std::string& c) : line (l), cond (c) { }
 
-    //--------
+  //--------
+
+  int line;
+  std::string cond;
+};
 
-    int line;
-    std::string cond;
-  };
+// Interface to breakpoints.
+class OCTINTERP_API bp_table
+{
+public:
 
-  // Interface to breakpoints.
-  class OCTINTERP_API bp_table
-  {
-  public:
+  bp_table (tree_evaluator& tw)
+    : m_evaluator (tw), m_bp_set (), m_errors_that_stop (),
+      m_caught_that_stop (), m_warnings_that_stop ()
+  { }
+
+  ~bp_table () = default;
+
+  // Set of breakpoint lines.
+  typedef std::set<int> bp_lines;
+
+  typedef bp_lines::const_iterator const_bp_lines_iterator;
+  typedef bp_lines::iterator bp_lines_iterator;
 
-    bp_table (tree_evaluator& tw)
-      : m_evaluator (tw), m_bp_set (), m_errors_that_stop (),
-        m_caught_that_stop (), m_warnings_that_stop ()
-    { }
+  typedef std::map <std::string, bp_lines> fname_line_map;
+
+  typedef fname_line_map::const_iterator const_fname_line_map_iterator;
+  typedef fname_line_map::iterator fname_line_map_iterator;
 
-    ~bp_table () = default;
+  typedef std::map <std::string, std::list<bp_type>> fname_bp_map;
+  typedef fname_bp_map::const_iterator const_fname_bp_map_iterator;
+  typedef fname_bp_map::iterator fname_bp_map_iterator;
 
-    // Set of breakpoint lines.
-    typedef std::set<int> bp_lines;
-
-    typedef bp_lines::const_iterator const_bp_lines_iterator;
-    typedef bp_lines::iterator bp_lines_iterator;
+  // Add a breakpoint at the nearest executable line in a function.
+  int add_breakpoint_in_function (const std::string& fname = "",
+                                  const std::string& class_name = "",
+                                  int line = 1,
+                                  const std::string& condition = "");
 
-    typedef std::map <std::string, bp_lines> fname_line_map;
-
-    typedef fname_line_map::const_iterator const_fname_line_map_iterator;
-    typedef fname_line_map::iterator fname_line_map_iterator;
+  // Add a set of breakpoints at the nearest executable lines in a
+  // function.
+  bp_lines add_breakpoints_in_function (const std::string& fname = "",
+                                        const std::string& class_name = "",
+                                        const bp_lines& lines = bp_lines (),
+                                        const std::string& condition = "");
 
-    typedef std::map <std::string, std::list<bp_type>> fname_bp_map;
-    typedef fname_bp_map::const_iterator const_fname_bp_map_iterator;
-    typedef fname_bp_map::iterator fname_bp_map_iterator;
+  // Add a breakpoint at the nearest executable line in a file.
+  int add_breakpoint_in_file (const std::string& file = "",
+                              int line = 1,
+                              const std::string& condition = "");
 
-    // Add a breakpoint at the nearest executable line in a function.
-    int add_breakpoint_in_function (const std::string& fname = "",
-                                    const std::string& class_name = "",
-                                    int line = 1,
+  // Add a set of breakpoints at the nearest executable lines in a
+  // file.
+  bp_lines add_breakpoints_in_file (const std::string& file = "",
+                                    const bp_lines& lines = bp_lines (),
                                     const std::string& condition = "");
 
-    // Add a set of breakpoints at the nearest executable lines in a
-    // function.
-    bp_lines add_breakpoints_in_function (const std::string& fname = "",
-                                          const std::string& class_name = "",
-                                          const bp_lines& lines = bp_lines (),
-                                          const std::string& condition = "");
+  // Remove a breakpoint from the given line in file.
+  int remove_breakpoint_from_function (const std::string& fname = "",
+                                       int line = 1);
 
-    // Add a breakpoint at the nearest executable line in a file.
-    int add_breakpoint_in_file (const std::string& file = "",
-                                int line = 1,
-                                const std::string& condition = "");
-
-    // Add a set of breakpoints at the nearest executable lines in a
-    // file.
-    bp_lines add_breakpoints_in_file (const std::string& file = "",
-                                      const bp_lines& lines = bp_lines (),
-                                      const std::string& condition = "");
+  // Remove a set of breakpoints from the given lines in file.
+  int remove_breakpoints_from_function (const std::string& fname = "",
+                                        const bp_lines& lines = bp_lines ());
 
-    // Remove a breakpoint from the given line in file.
-    int remove_breakpoint_from_function (const std::string& fname = "",
-                                         int line = 1);
-
-    // Remove a set of breakpoints from the given lines in file.
-    int remove_breakpoints_from_function (const std::string& fname = "",
-                                          const bp_lines& lines = bp_lines ());
+  // Remove all the breakpoints in a specified function.
+  bp_lines remove_all_breakpoints_from_function (const std::string& fname,
+                                                 bool silent = false);
 
-    // Remove all the breakpoints in a specified function.
-    bp_lines remove_all_breakpoints_from_function (const std::string& fname,
-                                                   bool silent = false);
+  // Remove a breakpoint from the given line in file.
+  int remove_breakpoint_from_file (const std::string& file = "",
+                                   int line = 1);
 
-    // Remove a breakpoint from the given line in file.
-    int remove_breakpoint_from_file (const std::string& file = "",
-                                     int line = 1);
-
-    // Remove a set of breakpoints from the given lines in file.
-    int remove_breakpoints_from_file (const std::string& file = "",
-                                      const bp_lines& lines = bp_lines ());
+  // Remove a set of breakpoints from the given lines in file.
+  int remove_breakpoints_from_file (const std::string& file = "",
+                                    const bp_lines& lines = bp_lines ());
 
 
-    // Remove all the breakpoints from a file.
-    bp_lines remove_all_breakpoints_from_file (const std::string& file,
-                                               bool silent = false);
+  // Remove all the breakpoints from a file.
+  bp_lines remove_all_breakpoints_from_file (const std::string& file,
+                                             bool silent = false);
 
-    // Remove all the breakpoints registered with octave.
-    void remove_all_breakpoints ();
+  // Remove all the breakpoints registered with octave.
+  void remove_all_breakpoints ();
 
-    // Return all breakpoints.  Each element of the map is a vector
-    // containing the breakpoints corresponding to a given function name.
-    fname_bp_map get_breakpoint_list (const octave_value_list& fname_list);
+  // Return all breakpoints.  Each element of the map is a vector
+  // containing the breakpoints corresponding to a given function name.
+  fname_bp_map get_breakpoint_list (const octave_value_list& fname_list);
 
-    bool have_breakpoints () { return (! m_bp_set.empty ()); }
+  bool have_breakpoints () { return (! m_bp_set.empty ()); }
 
-    // Should we enter debugging for this particular error identifier?
-    bool debug_on_err (const std::string& id)
-    {
-      return (m_errors_that_stop.empty () || m_errors_that_stop.count (id));
-    }
+  // Should we enter debugging for this particular error identifier?
+  bool debug_on_err (const std::string& id)
+  {
+    return (m_errors_that_stop.empty () || m_errors_that_stop.count (id));
+  }
 
-    // Should we enter debugging for this particular identifier in a try/catch?
-    bool debug_on_caught (const std::string& id)
-    {
-      return (m_caught_that_stop.empty () || m_caught_that_stop.count (id));
-    }
+  // Should we enter debugging for this particular identifier in a try/catch?
+  bool debug_on_caught (const std::string& id)
+  {
+    return (m_caught_that_stop.empty () || m_caught_that_stop.count (id));
+  }
 
-    // Should we enter debugging for this particular warning identifier?
-    bool debug_on_warn (const std::string& id)
-    {
-      return (m_warnings_that_stop.empty () || m_warnings_that_stop.count (id));
-    }
+  // Should we enter debugging for this particular warning identifier?
+  bool debug_on_warn (const std::string& id)
+  {
+    return (m_warnings_that_stop.empty () || m_warnings_that_stop.count (id));
+  }
 
-    octave_map stop_on_err_warn_status (bool to_screen);
+  octave_map stop_on_err_warn_status (bool to_screen);
 
-    void dbstop_process_map_args (const octave_map& mv);
+  void dbstop_process_map_args (const octave_map& mv);
 
-    void dbclear_all_signals ();
+  void dbclear_all_signals ();
 
-    bool condition_valid (const std::string& cond);
+  bool condition_valid (const std::string& cond);
 
-    void parse_dbfunction_params (const char *who,
-                                  const octave_value_list& args,
-                                  std::string& fcn_name,
-                                  std::string& class_name,
-                                  bp_table::bp_lines& lines,
-                                  std::string& cond);
+  void parse_dbfunction_params (const char *who,
+                                const octave_value_list& args,
+                                std::string& fcn_name,
+                                std::string& class_name,
+                                bp_table::bp_lines& lines,
+                                std::string& cond);
 
-  private:
+private:
 
-    typedef std::set<std::string>::const_iterator const_bp_set_iterator;
-    typedef std::set<std::string>::iterator bp_set_iterator;
+  typedef std::set<std::string>::const_iterator const_bp_set_iterator;
+  typedef std::set<std::string>::iterator bp_set_iterator;
 
-    tree_evaluator& m_evaluator;
+  tree_evaluator& m_evaluator;
 
-    // Set of function (.m file) names containing at least one breakpoint.
-    std::set<std::string> m_bp_set;
+  // Set of function (.m file) names containing at least one breakpoint.
+  std::set<std::string> m_bp_set;
 
-    // Set of error and warning message IDs that cause us to stop
-    // *if* Vdebug_on_error / Vdebug_on_caught / Vdebug_on_warning is set.
-    // Empty means stop on any error / caught error / warning.
-    std::set<std::string> m_errors_that_stop;
-    std::set<std::string> m_caught_that_stop;
-    std::set<std::string> m_warnings_that_stop;
+  // Set of error and warning message IDs that cause us to stop
+  // *if* Vdebug_on_error / Vdebug_on_caught / Vdebug_on_warning is set.
+  // Empty means stop on any error / caught error / warning.
+  std::set<std::string> m_errors_that_stop;
+  std::set<std::string> m_caught_that_stop;
+  std::set<std::string> m_warnings_that_stop;
 
-    void set_stop_flag (const char *who, const std::string& condition,
-                        bool on_off);
+  void set_stop_flag (const char *who, const std::string& condition,
+                      bool on_off);
 
-    void process_id_list (const char *who, const std::string& condition,
-                          const octave_value_list& args,
-                          int nargin, int& pos, bool on_off,
-                          std::set<std::string>& id_list);
+  void process_id_list (const char *who, const std::string& condition,
+                        const octave_value_list& args,
+                        int nargin, int& pos, bool on_off,
+                        std::set<std::string>& id_list);
 
-    bool add_breakpoint_1 (octave_user_code *fcn, const std::string& fname,
-                           const bp_lines& line, const std::string& condition,
-                           bp_lines& retval);
+  bool add_breakpoint_1 (octave_user_code *fcn, const std::string& fname,
+                         const bp_lines& line, const std::string& condition,
+                         bp_lines& retval);
 
-    int remove_breakpoint_1 (octave_user_code *fcn, const std::string&,
-                             const bp_lines& lines);
+  int remove_breakpoint_1 (octave_user_code *fcn, const std::string&,
+                           const bp_lines& lines);
 
-    bp_lines remove_all_breakpoints_in_file_1 (octave_user_code *fcn,
-                                               const std::string& fname);
-  };
+  bp_lines remove_all_breakpoints_in_file_1 (octave_user_code *fcn,
+                                             const std::string& fname);
+};
 
 OCTAVE_END_NAMESPACE(octave)
 
--- a/libinterp/parse-tree/pt-eval.cc	Thu Feb 02 12:25:51 2023 -0800
+++ b/libinterp/parse-tree/pt-eval.cc	Thu Feb 02 12:32:15 2023 -0800
@@ -75,1731 +75,1731 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-  // Normal evaluator.
-
-  class quit_debug_exception
-  {
-  public:
-
-    quit_debug_exception (bool all = false) : m_all (all) { }
-
-    quit_debug_exception (const quit_debug_exception&) = default;
-
-    quit_debug_exception& operator = (const quit_debug_exception&) = default;
-
-    ~quit_debug_exception () = default;
-
-    bool all () const { return m_all; }
-
-  private:
-
-    bool m_all;
-  };
-
-  class debugger
-  {
-  public:
-
-    enum execution_mode
-      {
-        EX_NORMAL = 0,
-        EX_CONTINUE = 1,
-        EX_QUIT = 2,
-        EX_QUIT_ALL = 3
-      };
-
-    debugger (interpreter& interp, std::size_t level)
-      : m_interpreter (interp), m_level (level),
-        m_execution_mode (EX_NORMAL), m_in_debug_repl (false)
-    { }
-
-    int server_loop ();
-
-    void repl (const std::string& prompt = "debug> ");
-
-    bool in_debug_repl () const { return m_in_debug_repl; }
-
-    void dbcont () { m_execution_mode = EX_CONTINUE; }
-
-    void dbquit (bool all = false)
+// Normal evaluator.
+
+class quit_debug_exception
+{
+public:
+
+  quit_debug_exception (bool all = false) : m_all (all) { }
+
+  quit_debug_exception (const quit_debug_exception&) = default;
+
+  quit_debug_exception& operator = (const quit_debug_exception&) = default;
+
+  ~quit_debug_exception () = default;
+
+  bool all () const { return m_all; }
+
+private:
+
+  bool m_all;
+};
+
+class debugger
+{
+public:
+
+  enum execution_mode
     {
-      if (all)
-        m_execution_mode = EX_QUIT_ALL;
-      else
-        m_execution_mode = EX_QUIT;
-    }
-
-    bool quitting_debugger () const;
-
-  private:
-
-    interpreter& m_interpreter;
-
-    std::size_t m_level;
-    execution_mode m_execution_mode;
-    bool m_in_debug_repl;
-  };
-
-  // FIXME: Could the debugger server_loop and repl functions be merged
-  // with the corresponding tree_evaluator functions or do they need to
-  // remain separate?  They perform nearly the same functions.
-
-  int debugger::server_loop ()
+      EX_NORMAL = 0,
+      EX_CONTINUE = 1,
+      EX_QUIT = 2,
+      EX_QUIT_ALL = 3
+    };
+
+  debugger (interpreter& interp, std::size_t level)
+    : m_interpreter (interp), m_level (level),
+      m_execution_mode (EX_NORMAL), m_in_debug_repl (false)
+  { }
+
+  int server_loop ();
+
+  void repl (const std::string& prompt = "debug> ");
+
+  bool in_debug_repl () const { return m_in_debug_repl; }
+
+  void dbcont () { m_execution_mode = EX_CONTINUE; }
+
+  void dbquit (bool all = false)
   {
-    // Process events from the event queue.
-
-    tree_evaluator& tw = m_interpreter.get_evaluator ();
-
-    void (tree_evaluator::*server_mode_fptr) (bool)
-      = &tree_evaluator::server_mode;
-    unwind_action act (server_mode_fptr, &tw, true);
-
-    int exit_status = 0;
-
-    do
-      {
-        if (m_execution_mode == EX_CONTINUE || tw.dbstep_flag ())
-          break;
-
-        if (quitting_debugger ())
-          break;
-
-        try
-          {
-            // FIXME: Should we call octave_quit in the octave::sleep
-            // and/or command_editor::run_event_hooks functions?
-
-            octave_quit ();
-
-            // FIXME: Running the event queue should be decoupled from
-            // the command_editor.
-
-            // FIXME: Is it OK to use command_editor::run_event_hooks
-            // here?  It may run more than one queued function per call,
-            // and it seems that the checks at the top of the loop
-            // probably need to be done after each individual event
-            // function is executed.  For now, maybe the simplest thing
-            // would be to pass a predicate function (lambda expression)
-            // to the command_editor::run_event_hooks and have it check
-            // that and break out of the eval loop(s) if the condition
-            // is met?
-
-            // FIXME: We should also use a condition variable to manage
-            // the execution of entries in the queue and eliminate the
-            // need for the busy-wait loop.
-
-            command_editor::run_event_hooks ();
-
-            release_unreferenced_dynamic_libraries ();
-
-            sleep (0.1);
-          }
-        catch (const interrupt_exception&)
-          {
-            octave_interrupt_state = 1;
-            m_interpreter.recover_from_exception ();
-
-            // Required newline when the user does Ctrl+C at the prompt.
-            if (m_interpreter.interactive ())
-              octave_stdout << "\n";
-          }
-        catch (const index_exception& e)
-          {
-            m_interpreter.recover_from_exception ();
-
-            std::cerr << "error: unhandled index exception: "
-                      << e.message () << " -- trying to return to prompt"
-                      << std::endl;
-          }
-        catch (const execution_exception& ee)
-          {
-            error_system& es = m_interpreter.get_error_system ();
-
-            es.save_exception (ee);
-            es.display_exception (ee);
-
-            if (m_interpreter.interactive ())
-              {
-                m_interpreter.recover_from_exception ();
-              }
-            else
-              {
-                // We should exit with a nonzero status.
-                exit_status = 1;
-                break;
-              }
-          }
-        catch (const quit_debug_exception& qde)
-          {
-            if (qde.all ())
-              throw;
-
-            // Continue in this debug level.
-          }
-        catch (const std::bad_alloc&)
-          {
-            m_interpreter.recover_from_exception ();
-
-            std::cerr << "error: out of memory -- trying to return to prompt"
-                      << std::endl;
-          }
-      }
-    while (exit_status == 0);
-
-    if (exit_status == EOF)
-      {
-        if (m_interpreter.interactive ())
-          octave_stdout << "\n";
-
-        exit_status = 0;
-      }
-
-    return exit_status;
+    if (all)
+      m_execution_mode = EX_QUIT_ALL;
+    else
+      m_execution_mode = EX_QUIT;
   }
 
-  void debugger::repl (const std::string& prompt_arg)
-  {
-    unwind_protect frame;
-
-    frame.protect_var (m_in_debug_repl);
-    frame.protect_var (m_execution_mode);
-
-    m_in_debug_repl = true;
-
-    tree_evaluator& tw = m_interpreter.get_evaluator ();
-
-    bool silent = tw.quiet_breakpoint_flag (false);
-
-    frame.add (&tree_evaluator::restore_frame, &tw,
-               tw.current_call_stack_frame_number ());
-
-    tw.goto_frame (tw.debug_frame ());
-
-    octave_user_code *caller = tw.current_user_code ();
-    std::string fcn_file_nm, fcn_nm;
-
-    if (caller)
-      {
-        fcn_file_nm = caller->fcn_file_name ();
-        fcn_nm = fcn_file_nm.empty () ? caller->name () : fcn_file_nm;
-      }
-
-    int curr_debug_line = tw.current_line ();
-
-    std::ostringstream buf;
-
-    input_system& input_sys = m_interpreter.get_input_system ();
-
-    event_manager& evmgr = m_interpreter.get_event_manager ();
-
-    if (! fcn_nm.empty ())
-      {
-        if (input_sys.gud_mode ())
-          {
-            static char ctrl_z = 'Z' & 0x1f;
-
-            buf << ctrl_z << ctrl_z << fcn_nm << ':' << curr_debug_line;
-          }
-        else
-          {
-            // FIXME: we should come up with a clean way to detect
-            // that we are stopped on the no-op command that marks the
-            // end of a function or script.
-
-            if (! silent)
-              {
-                std::shared_ptr<stack_frame> frm = tw.current_user_frame ();
-
-                frm->display_stopped_in_message (buf);
-              }
-
-            evmgr.enter_debugger_event (fcn_nm, fcn_file_nm, curr_debug_line);
-
-            evmgr.set_workspace ();
-
-            frame.add (&event_manager::execute_in_debugger_event, &evmgr,
-                       fcn_nm, curr_debug_line);
-
-            if (! silent)
-              {
-                std::string line_buf;
-
-                if (caller)
-                  line_buf = caller->get_code_line (curr_debug_line);
-
-                if (! line_buf.empty ())
-                  buf << curr_debug_line << ": " << line_buf;
-              }
-          }
-      }
-
-    if (silent)
-      command_editor::erase_empty_line (true);
-
-    std::string stopped_in_msg = buf.str ();
-
-    if (m_interpreter.server_mode ())
-      {
-        if (! stopped_in_msg.empty ())
-          octave_stdout << stopped_in_msg << std::endl;
-
-        evmgr.push_event_queue ();
-
-        frame.add (&event_manager::pop_event_queue, &evmgr);
-
-        frame.add (&tree_evaluator::set_parser, &tw, tw.get_parser ());
-
-        std::shared_ptr<push_parser>
-          debug_parser (new push_parser (m_interpreter));
-
-        tw.set_parser (debug_parser);
-
-        server_loop ();
-      }
-    else
-      {
-        if (! stopped_in_msg.empty ())
-          std::cerr << stopped_in_msg << std::endl;
-
-        std::string tmp_prompt = prompt_arg;
-        if (m_level > 0)
-          tmp_prompt = "[" + std::to_string (m_level) + "]" + prompt_arg;
-
-        frame.add (&interpreter::set_PS1, &m_interpreter, m_interpreter.PS1 ());
-        m_interpreter.PS1 (tmp_prompt);
-
-        if (! m_interpreter.interactive ())
-          {
-            void (interpreter::*interactive_fptr) (bool)
-              = &interpreter::interactive;
-            frame.add (interactive_fptr, &m_interpreter,
-                       m_interpreter.interactive ());
-
-            m_interpreter.interactive (true);
-
-            // FIXME: should debugging be possible in an embedded
-            // interpreter?
-
-            application *app = application::app ();
-
-            if (app)
-              {
-                void (application::*forced_interactive_fptr) (bool)
-                  = &application::forced_interactive;
-                frame.add (forced_interactive_fptr, app,
-                           app->forced_interactive ());
-
-                app->forced_interactive (true);
-              }
-          }
+  bool quitting_debugger () const;
+
+private:
+
+  interpreter& m_interpreter;
+
+  std::size_t m_level;
+  execution_mode m_execution_mode;
+  bool m_in_debug_repl;
+};
+
+// FIXME: Could the debugger server_loop and repl functions be merged
+// with the corresponding tree_evaluator functions or do they need to
+// remain separate?  They perform nearly the same functions.
+
+int debugger::server_loop ()
+{
+  // Process events from the event queue.
+
+  tree_evaluator& tw = m_interpreter.get_evaluator ();
+
+  void (tree_evaluator::*server_mode_fptr) (bool)
+    = &tree_evaluator::server_mode;
+  unwind_action act (server_mode_fptr, &tw, true);
+
+  int exit_status = 0;
+
+  do
+    {
+      if (m_execution_mode == EX_CONTINUE || tw.dbstep_flag ())
+        break;
+
+      if (quitting_debugger ())
+        break;
+
+      try
+        {
+          // FIXME: Should we call octave_quit in the octave::sleep
+          // and/or command_editor::run_event_hooks functions?
+
+          octave_quit ();
+
+          // FIXME: Running the event queue should be decoupled from
+          // the command_editor.
+
+          // FIXME: Is it OK to use command_editor::run_event_hooks
+          // here?  It may run more than one queued function per call,
+          // and it seems that the checks at the top of the loop
+          // probably need to be done after each individual event
+          // function is executed.  For now, maybe the simplest thing
+          // would be to pass a predicate function (lambda expression)
+          // to the command_editor::run_event_hooks and have it check
+          // that and break out of the eval loop(s) if the condition
+          // is met?
+
+          // FIXME: We should also use a condition variable to manage
+          // the execution of entries in the queue and eliminate the
+          // need for the busy-wait loop.
+
+          command_editor::run_event_hooks ();
+
+          release_unreferenced_dynamic_libraries ();
+
+          sleep (0.1);
+        }
+      catch (const interrupt_exception&)
+        {
+          octave_interrupt_state = 1;
+          m_interpreter.recover_from_exception ();
+
+          // Required newline when the user does Ctrl+C at the prompt.
+          if (m_interpreter.interactive ())
+            octave_stdout << "\n";
+        }
+      catch (const index_exception& e)
+        {
+          m_interpreter.recover_from_exception ();
+
+          std::cerr << "error: unhandled index exception: "
+                    << e.message () << " -- trying to return to prompt"
+                    << std::endl;
+        }
+      catch (const execution_exception& ee)
+        {
+          error_system& es = m_interpreter.get_error_system ();
+
+          es.save_exception (ee);
+          es.display_exception (ee);
+
+          if (m_interpreter.interactive ())
+            {
+              m_interpreter.recover_from_exception ();
+            }
+          else
+            {
+              // We should exit with a nonzero status.
+              exit_status = 1;
+              break;
+            }
+        }
+      catch (const quit_debug_exception& qde)
+        {
+          if (qde.all ())
+            throw;
+
+          // Continue in this debug level.
+        }
+      catch (const std::bad_alloc&)
+        {
+          m_interpreter.recover_from_exception ();
+
+          std::cerr << "error: out of memory -- trying to return to prompt"
+                    << std::endl;
+        }
+    }
+  while (exit_status == 0);
+
+  if (exit_status == EOF)
+    {
+      if (m_interpreter.interactive ())
+        octave_stdout << "\n";
+
+      exit_status = 0;
+    }
+
+  return exit_status;
+}
+
+void debugger::repl (const std::string& prompt_arg)
+{
+  unwind_protect frame;
+
+  frame.protect_var (m_in_debug_repl);
+  frame.protect_var (m_execution_mode);
+
+  m_in_debug_repl = true;
+
+  tree_evaluator& tw = m_interpreter.get_evaluator ();
+
+  bool silent = tw.quiet_breakpoint_flag (false);
+
+  frame.add (&tree_evaluator::restore_frame, &tw,
+             tw.current_call_stack_frame_number ());
+
+  tw.goto_frame (tw.debug_frame ());
+
+  octave_user_code *caller = tw.current_user_code ();
+  std::string fcn_file_nm, fcn_nm;
+
+  if (caller)
+    {
+      fcn_file_nm = caller->fcn_file_name ();
+      fcn_nm = fcn_file_nm.empty () ? caller->name () : fcn_file_nm;
+    }
+
+  int curr_debug_line = tw.current_line ();
+
+  std::ostringstream buf;
+
+  input_system& input_sys = m_interpreter.get_input_system ();
+
+  event_manager& evmgr = m_interpreter.get_event_manager ();
+
+  if (! fcn_nm.empty ())
+    {
+      if (input_sys.gud_mode ())
+        {
+          static char ctrl_z = 'Z' & 0x1f;
+
+          buf << ctrl_z << ctrl_z << fcn_nm << ':' << curr_debug_line;
+        }
+      else
+        {
+          // FIXME: we should come up with a clean way to detect
+          // that we are stopped on the no-op command that marks the
+          // end of a function or script.
+
+          if (! silent)
+            {
+              std::shared_ptr<stack_frame> frm = tw.current_user_frame ();
+
+              frm->display_stopped_in_message (buf);
+            }
+
+          evmgr.enter_debugger_event (fcn_nm, fcn_file_nm, curr_debug_line);
+
+          evmgr.set_workspace ();
+
+          frame.add (&event_manager::execute_in_debugger_event, &evmgr,
+                     fcn_nm, curr_debug_line);
+
+          if (! silent)
+            {
+              std::string line_buf;
+
+              if (caller)
+                line_buf = caller->get_code_line (curr_debug_line);
+
+              if (! line_buf.empty ())
+                buf << curr_debug_line << ": " << line_buf;
+            }
+        }
+    }
+
+  if (silent)
+    command_editor::erase_empty_line (true);
+
+  std::string stopped_in_msg = buf.str ();
+
+  if (m_interpreter.server_mode ())
+    {
+      if (! stopped_in_msg.empty ())
+        octave_stdout << stopped_in_msg << std::endl;
+
+      evmgr.push_event_queue ();
+
+      frame.add (&event_manager::pop_event_queue, &evmgr);
+
+      frame.add (&tree_evaluator::set_parser, &tw, tw.get_parser ());
+
+      std::shared_ptr<push_parser>
+        debug_parser (new push_parser (m_interpreter));
+
+      tw.set_parser (debug_parser);
+
+      server_loop ();
+    }
+  else
+    {
+      if (! stopped_in_msg.empty ())
+        std::cerr << stopped_in_msg << std::endl;
+
+      std::string tmp_prompt = prompt_arg;
+      if (m_level > 0)
+        tmp_prompt = "[" + std::to_string (m_level) + "]" + prompt_arg;
+
+      frame.add (&interpreter::set_PS1, &m_interpreter, m_interpreter.PS1 ());
+      m_interpreter.PS1 (tmp_prompt);
+
+      if (! m_interpreter.interactive ())
+        {
+          void (interpreter::*interactive_fptr) (bool)
+            = &interpreter::interactive;
+          frame.add (interactive_fptr, &m_interpreter,
+                     m_interpreter.interactive ());
+
+          m_interpreter.interactive (true);
+
+          // FIXME: should debugging be possible in an embedded
+          // interpreter?
+
+          application *app = application::app ();
+
+          if (app)
+            {
+              void (application::*forced_interactive_fptr) (bool)
+                = &application::forced_interactive;
+              frame.add (forced_interactive_fptr, app,
+                         app->forced_interactive ());
+
+              app->forced_interactive (true);
+            }
+        }
 
 #if defined (OCTAVE_ENABLE_COMMAND_LINE_PUSH_PARSER)
 
-        input_reader reader (m_interpreter);
-
-        push_parser debug_parser (m_interpreter);
+      input_reader reader (m_interpreter);
+
+      push_parser debug_parser (m_interpreter);
 
 #else
 
-        parser debug_parser (m_interpreter);
+      parser debug_parser (m_interpreter);
 
 #endif
 
-        error_system& es = m_interpreter.get_error_system ();
-
-        while (m_in_debug_repl)
-          {
-            if (m_execution_mode == EX_CONTINUE || tw.dbstep_flag ())
-              break;
-
-            if (quitting_debugger ())
-              break;
-
-            try
-              {
-                debug_parser.reset ();
+      error_system& es = m_interpreter.get_error_system ();
+
+      while (m_in_debug_repl)
+        {
+          if (m_execution_mode == EX_CONTINUE || tw.dbstep_flag ())
+            break;
+
+          if (quitting_debugger ())
+            break;
+
+          try
+            {
+              debug_parser.reset ();
 
 #if defined (OCTAVE_ENABLE_COMMAND_LINE_PUSH_PARSER)
 
-                int retval = 0;
-
-                std::string prompt
-                  = command_editor::decode_prompt_string (tmp_prompt);
-
-                do
-                  {
-                    bool eof = false;
-                    std::string input_line = reader.get_input (prompt, eof);
-
-                    if (eof)
-                      {
-                        retval = EOF;
-                        break;
-                      }
-
-                    retval = debug_parser.run (input_line, false);
-
-                    prompt = command_editor::decode_prompt_string (m_interpreter.PS2 ());
-                  }
-                while (retval < 0);
+              int retval = 0;
+
+              std::string prompt
+                = command_editor::decode_prompt_string (tmp_prompt);
+
+              do
+                {
+                  bool eof = false;
+                  std::string input_line = reader.get_input (prompt, eof);
+
+                  if (eof)
+                    {
+                      retval = EOF;
+                      break;
+                    }
+
+                  retval = debug_parser.run (input_line, false);
+
+                  prompt = command_editor::decode_prompt_string (m_interpreter.PS2 ());
+                }
+              while (retval < 0);
 
 #else
 
-                int retval = debug_parser.run ();
+              int retval = debug_parser.run ();
 
 #endif
-                if (command_editor::interrupt (false))
-                  {
-                    // Break regardless of m_execution_mode value.
-
-                    quitting_debugger ();
-
-                    break;
-                  }
-                else
-                  {
-                    if (retval == 0)
-                      {
-                        std::shared_ptr<tree_statement_list> stmt_list
-                          = debug_parser.statement_list ();
-
-                        if (stmt_list)
-                          stmt_list->accept (tw);
-
-                        if (octave_completion_matches_called)
-                          octave_completion_matches_called = false;
-
-                        // FIXME: the following statement is here because
-                        // the last command may have been a dbup, dbdown, or
-                        // dbstep command that changed the current debug
-                        // frame.  If so, we need to reset the current frame
-                        // for the call stack.  But is this right way to do
-                        // this job?  What if the statement list was
-                        // something like "dbup; dbstack"?  Will the call to
-                        // dbstack use the right frame?  If not, how can we
-                        // fix this problem?
-                        tw.goto_frame (tw.debug_frame ());
-                      }
-
-                    octave_quit ();
-                  }
-              }
-            catch (const execution_exception& ee)
-              {
-                es.save_exception (ee);
-                es.display_exception (ee);
-
-                // Ignore errors when in debugging mode;
-                m_interpreter.recover_from_exception ();
-              }
-            catch (const quit_debug_exception& qde)
-              {
-                if (qde.all ())
-                  throw;
-
-                // Continue in this debug level.
-              }
-          }
-      }
-  }
-
-  bool debugger::quitting_debugger () const
-  {
-    if (m_execution_mode == EX_QUIT)
-      {
-        // If there is no enclosing debug level or the top-level
-        // repl is not active, handle dbquit the same as dbcont.
-
-        if (m_level > 0 || m_interpreter.server_mode ()
-            || m_interpreter.in_top_level_repl ())
-          throw quit_debug_exception ();
-        else
-          return true;
-      }
-
-    if (m_execution_mode == EX_QUIT_ALL)
-      {
-        // If the top-level repl is not active, handle "dbquit all"
-        // the same as dbcont.
-
-        if (m_interpreter.server_mode () || m_interpreter.in_top_level_repl ())
-          throw quit_debug_exception (true);
-        else
-          return true;
-      }
-
-    return false;
-  }
-
-  bool tree_evaluator::at_top_level () const
-  {
-    return m_call_stack.at_top_level ();
-  }
-
-  std::string
-  tree_evaluator::mfilename (const std::string& opt) const
-  {
-    std::string fname;
-
-    octave_user_code *fcn = m_call_stack.current_user_code ();
-
-    if (fcn)
-      {
-        fname = fcn->fcn_file_name ();
-
-        if (fname.empty ())
-          fname = fcn->name ();
-      }
-
-    if (opt == "fullpathext")
-      return fname;
-
-    std::size_t dpos = fname.rfind (sys::file_ops::dir_sep_char ());
-    std::size_t epos = fname.rfind ('.');
-
-    if (epos <= dpos+1)
-      epos = std::string::npos;
-
-    if (epos != std::string::npos)
-      fname = fname.substr (0, epos);
-
-    if (opt == "fullpath")
-      return fname;
-
-    if (dpos != std::string::npos)
-      fname = fname.substr (dpos+1);
-
+              if (command_editor::interrupt (false))
+                {
+                  // Break regardless of m_execution_mode value.
+
+                  quitting_debugger ();
+
+                  break;
+                }
+              else
+                {
+                  if (retval == 0)
+                    {
+                      std::shared_ptr<tree_statement_list> stmt_list
+                        = debug_parser.statement_list ();
+
+                      if (stmt_list)
+                        stmt_list->accept (tw);
+
+                      if (octave_completion_matches_called)
+                        octave_completion_matches_called = false;
+
+                      // FIXME: the following statement is here because
+                      // the last command may have been a dbup, dbdown, or
+                      // dbstep command that changed the current debug
+                      // frame.  If so, we need to reset the current frame
+                      // for the call stack.  But is this right way to do
+                      // this job?  What if the statement list was
+                      // something like "dbup; dbstack"?  Will the call to
+                      // dbstack use the right frame?  If not, how can we
+                      // fix this problem?
+                      tw.goto_frame (tw.debug_frame ());
+                    }
+
+                  octave_quit ();
+                }
+            }
+          catch (const execution_exception& ee)
+            {
+              es.save_exception (ee);
+              es.display_exception (ee);
+
+              // Ignore errors when in debugging mode;
+              m_interpreter.recover_from_exception ();
+            }
+          catch (const quit_debug_exception& qde)
+            {
+              if (qde.all ())
+                throw;
+
+              // Continue in this debug level.
+            }
+        }
+    }
+}
+
+bool debugger::quitting_debugger () const
+{
+  if (m_execution_mode == EX_QUIT)
+    {
+      // If there is no enclosing debug level or the top-level
+      // repl is not active, handle dbquit the same as dbcont.
+
+      if (m_level > 0 || m_interpreter.server_mode ()
+          || m_interpreter.in_top_level_repl ())
+        throw quit_debug_exception ();
+      else
+        return true;
+    }
+
+  if (m_execution_mode == EX_QUIT_ALL)
+    {
+      // If the top-level repl is not active, handle "dbquit all"
+      // the same as dbcont.
+
+      if (m_interpreter.server_mode () || m_interpreter.in_top_level_repl ())
+        throw quit_debug_exception (true);
+      else
+        return true;
+    }
+
+  return false;
+}
+
+bool tree_evaluator::at_top_level () const
+{
+  return m_call_stack.at_top_level ();
+}
+
+std::string
+tree_evaluator::mfilename (const std::string& opt) const
+{
+  std::string fname;
+
+  octave_user_code *fcn = m_call_stack.current_user_code ();
+
+  if (fcn)
+    {
+      fname = fcn->fcn_file_name ();
+
+      if (fname.empty ())
+        fname = fcn->name ();
+    }
+
+  if (opt == "fullpathext")
+    return fname;
+
+  std::size_t dpos = fname.rfind (sys::file_ops::dir_sep_char ());
+  std::size_t epos = fname.rfind ('.');
+
+  if (epos <= dpos+1)
+    epos = std::string::npos;
+
+  if (epos != std::string::npos)
+    fname = fname.substr (0, epos);
+
+  if (opt == "fullpath")
     return fname;
-  }
-
-  void tree_evaluator::parse_and_execute (const std::string& input,
-                                          bool& incomplete_parse)
-  {
-    incomplete_parse = false;
-
-    unwind_protect_var<bool> upv (m_in_top_level_repl, true);
-
-    if (at_top_level ())
-      {
-        dbstep_flag (0);
-        reset_debug_state ();
-      }
-
-    // FIXME: OK to do this job here, or should it be in the functions
-    // that do the actual prompting?
-
-    // Update the time stamp for the "prompt" so that automatically
-    // finding modified files based on file modification times will
-    // work.  In the future, we may do something completely different to
-    // check for changes to files but for now, we rely on the prompt
-    // time stamp to limit the checks for file modification times.
-
-    Vlast_prompt_time.stamp ();
-
-    bool eof = false;
-
-    event_manager& evmgr = m_interpreter.get_event_manager ();
-
-    if (command_history::add (input))
-      evmgr.append_history (input);
-
-    m_exit_status = m_parser->run (input, eof);
-
-    if (m_exit_status == 0)
-      {
-        std::shared_ptr<tree_statement_list>
-          stmt_list = m_parser->statement_list ();
-
-        if (stmt_list)
-          {
-            command_editor::increment_current_command_number ();
-
-            eval (stmt_list, m_interpreter.interactive ());
-
-            evmgr.set_workspace ();
-          }
-        else if (m_parser->at_end_of_input ())
-          m_exit_status = EOF;
-      }
-    else
-      incomplete_parse = true;
-
-    // FIXME: Should we be checking m_exit_status or incomplete_parse or
-    // both here?  Could EOF have a value other than -1, and is there
-    // possible confusion between that state and the parser returning -1?
-
-    if (m_exit_status == -1)
-      m_exit_status = 0;
-    else
-      m_parser->reset ();
-
-    evmgr.pre_input_event ();
-  }
-
-  void tree_evaluator::get_line_and_eval ()
-  {
-    std::mutex mtx;
-    std::unique_lock<std::mutex> lock (mtx);
-    std::condition_variable cv;
-    bool incomplete_parse = false;
-    bool evaluation_pending = false;
-    bool exiting = false;
-
-    event_manager& evmgr = m_interpreter.get_event_manager ();
-
-    while (true)
-      {
-        // FIXME: Detect EOF?  Use readline?  If
-        // so, then we need to disable idle event loop hook function
-        // execution.
-
-        std::string ps
-          = incomplete_parse ? m_interpreter.PS2 () : m_interpreter.PS1 ();
-
-        std::cout << command_editor::decode_prompt_string (ps);
-
-        std::string input;
-        std::getline (std::cin, input);
-
-        if (input.empty ())
-          continue;
-
-        incomplete_parse = false;
-        evaluation_pending = true;
-        exiting = false;
-
-        evmgr.post_event
-          ([&] (interpreter& interp)
-           {
-             // INTERPRETER THREAD
-
-             std::lock_guard<std::mutex> local_lock (mtx);
-
-             try
-               {
-                 interp.parse_and_execute (input, incomplete_parse);
-               }
-             catch (const exit_exception&)
-               {
-                 evaluation_pending = false;
-                 exiting = true;
-                 cv.notify_all ();
-                 throw;
-               }
-             catch (const execution_exception& ee)
-               {
-                 error_system& es = m_interpreter.get_error_system ();
-
-                 es.save_exception (ee);
-                 es.display_exception (ee);
-
-                 if (m_interpreter.interactive ())
-                   {
-                     m_interpreter.recover_from_exception ();
-                     m_parser->reset ();
-                     evaluation_pending = false;
-                     cv.notify_all ();
-                   }
-                 else
-                   {
-                     evaluation_pending = false;
-                     cv.notify_all ();
-                     throw exit_exception (1);
-                   }
-               }
-             catch (...)
-               {
-                 evaluation_pending = false;
-                 cv.notify_all ();
-                 throw;
-               }
-
-             evaluation_pending = false;
-             cv.notify_all ();
-           });
-
-        // Wait until evaluation is finished before prompting for input
-        // again.
-
-        cv.wait (lock, [&] { return ! evaluation_pending; });
-
-        if (exiting)
-          break;
-      }
-  }
-
-  int tree_evaluator::repl ()
-  {
-    // The big loop.  Read, Eval, Print, Loop.  Normally user
-    // interaction at the command line in a terminal session, but we may
-    // also end up here when reading from a pipe or when stdin is
-    // connected to a file by the magic of input redirection.
-
-    int exit_status = 0;
-
-    // FIXME: should this choice be a command-line option?  Note that we
-    // intend that the push parser interface only be used for
-    // interactive sessions.
+
+  if (dpos != std::string::npos)
+    fname = fname.substr (dpos+1);
+
+  return fname;
+}
+
+void tree_evaluator::parse_and_execute (const std::string& input,
+                                        bool& incomplete_parse)
+{
+  incomplete_parse = false;
+
+  unwind_protect_var<bool> upv (m_in_top_level_repl, true);
+
+  if (at_top_level ())
+    {
+      dbstep_flag (0);
+      reset_debug_state ();
+    }
+
+  // FIXME: OK to do this job here, or should it be in the functions
+  // that do the actual prompting?
+
+  // Update the time stamp for the "prompt" so that automatically
+  // finding modified files based on file modification times will
+  // work.  In the future, we may do something completely different to
+  // check for changes to files but for now, we rely on the prompt
+  // time stamp to limit the checks for file modification times.
+
+  Vlast_prompt_time.stamp ();
+
+  bool eof = false;
+
+  event_manager& evmgr = m_interpreter.get_event_manager ();
+
+  if (command_history::add (input))
+    evmgr.append_history (input);
+
+  m_exit_status = m_parser->run (input, eof);
+
+  if (m_exit_status == 0)
+    {
+      std::shared_ptr<tree_statement_list>
+        stmt_list = m_parser->statement_list ();
+
+      if (stmt_list)
+        {
+          command_editor::increment_current_command_number ();
+
+          eval (stmt_list, m_interpreter.interactive ());
+
+          evmgr.set_workspace ();
+        }
+      else if (m_parser->at_end_of_input ())
+        m_exit_status = EOF;
+    }
+  else
+    incomplete_parse = true;
+
+  // FIXME: Should we be checking m_exit_status or incomplete_parse or
+  // both here?  Could EOF have a value other than -1, and is there
+  // possible confusion between that state and the parser returning -1?
+
+  if (m_exit_status == -1)
+    m_exit_status = 0;
+  else
+    m_parser->reset ();
+
+  evmgr.pre_input_event ();
+}
+
+void tree_evaluator::get_line_and_eval ()
+{
+  std::mutex mtx;
+  std::unique_lock<std::mutex> lock (mtx);
+  std::condition_variable cv;
+  bool incomplete_parse = false;
+  bool evaluation_pending = false;
+  bool exiting = false;
+
+  event_manager& evmgr = m_interpreter.get_event_manager ();
+
+  while (true)
+    {
+      // FIXME: Detect EOF?  Use readline?  If
+      // so, then we need to disable idle event loop hook function
+      // execution.
+
+      std::string ps
+        = incomplete_parse ? m_interpreter.PS2 () : m_interpreter.PS1 ();
+
+      std::cout << command_editor::decode_prompt_string (ps);
+
+      std::string input;
+      std::getline (std::cin, input);
+
+      if (input.empty ())
+        continue;
+
+      incomplete_parse = false;
+      evaluation_pending = true;
+      exiting = false;
+
+      evmgr.post_event
+        ([&] (interpreter& interp)
+         {
+           // INTERPRETER THREAD
+
+           std::lock_guard<std::mutex> local_lock (mtx);
+
+           try
+             {
+               interp.parse_and_execute (input, incomplete_parse);
+             }
+           catch (const exit_exception&)
+             {
+               evaluation_pending = false;
+               exiting = true;
+               cv.notify_all ();
+               throw;
+             }
+           catch (const execution_exception& ee)
+             {
+               error_system& es = m_interpreter.get_error_system ();
+
+               es.save_exception (ee);
+               es.display_exception (ee);
+
+               if (m_interpreter.interactive ())
+                 {
+                   m_interpreter.recover_from_exception ();
+                   m_parser->reset ();
+                   evaluation_pending = false;
+                   cv.notify_all ();
+                 }
+               else
+                 {
+                   evaluation_pending = false;
+                   cv.notify_all ();
+                   throw exit_exception (1);
+                 }
+             }
+           catch (...)
+             {
+               evaluation_pending = false;
+               cv.notify_all ();
+               throw;
+             }
+
+           evaluation_pending = false;
+           cv.notify_all ();
+         });
+
+      // Wait until evaluation is finished before prompting for input
+      // again.
+
+      cv.wait (lock, [&] { return ! evaluation_pending; });
+
+      if (exiting)
+        break;
+    }
+}
+
+int tree_evaluator::repl ()
+{
+  // The big loop.  Read, Eval, Print, Loop.  Normally user
+  // interaction at the command line in a terminal session, but we may
+  // also end up here when reading from a pipe or when stdin is
+  // connected to a file by the magic of input redirection.
+
+  int exit_status = 0;
+
+  // FIXME: should this choice be a command-line option?  Note that we
+  // intend that the push parser interface only be used for
+  // interactive sessions.
 
 #if defined (OCTAVE_ENABLE_COMMAND_LINE_PUSH_PARSER)
-    static bool use_command_line_push_parser = true;
+  static bool use_command_line_push_parser = true;
 #else
-    static bool use_command_line_push_parser = false;
+  static bool use_command_line_push_parser = false;
 #endif
 
-    // The following logic is written as it is to allow easy transition
-    // to setting USE_COMMAND_LINE_PUSH_PARSER at run time and to
-    // simplify the logic of the main loop below by using the same
-    // base_parser::run interface for both push and pull parsers.
-
-    std::shared_ptr<base_parser> repl_parser;
-
-    if (m_interpreter.interactive ())
-      {
-        if (use_command_line_push_parser)
-          {
-            push_parser *pp
-              = new push_parser (m_interpreter,
-                                 new input_reader (m_interpreter));
-
-            repl_parser = std::shared_ptr<base_parser> (pp);
-          }
-        else
-          {
-            parser *pp = new parser (new lexer (m_interpreter));
-            repl_parser = std::shared_ptr<base_parser> (pp);
-          }
-      }
-    else
-      {
-        parser *pp = new parser (new lexer (stdin, m_interpreter));
-        repl_parser = std::shared_ptr<base_parser> (pp);
-      }
-
-    do
-      {
-        try
-          {
-            unwind_protect_var<bool> upv (m_in_top_level_repl, true);
-
-            repl_parser->reset ();
-
-            if (at_top_level ())
-              {
-                dbstep_flag (0);
-                reset_debug_state ();
-              }
-
-            exit_status = repl_parser->run ();
-
-            if (exit_status == 0)
-              {
-                std::shared_ptr<tree_statement_list>
-                  stmt_list = repl_parser->statement_list ();
-
-                if (stmt_list)
-                  {
-                    command_editor::increment_current_command_number ();
-
-                    eval (stmt_list, m_interpreter.interactive ());
-                  }
-                else if (repl_parser->at_end_of_input ())
-                  {
-                    exit_status = EOF;
-                    break;
-                  }
-              }
-          }
-        catch (const interrupt_exception&)
-          {
-            m_interpreter.recover_from_exception ();
-
-            // Required newline when the user does Ctrl+C at the prompt.
-            if (m_interpreter.interactive ())
-              octave_stdout << "\n";
-          }
-        catch (const index_exception& ie)
-          {
-            m_interpreter.recover_from_exception ();
-
-            std::cerr << "error: unhandled index exception: "
-                      << ie.message () << " -- trying to return to prompt"
-                      << std::endl;
-          }
-        catch (const execution_exception& ee)
-          {
-            error_system& es = m_interpreter.get_error_system ();
-
-            es.save_exception (ee);
-            es.display_exception (ee);
-
-            if (m_interpreter.interactive ())
-              m_interpreter.recover_from_exception ();
-            else
-              {
-                // We should exit with a nonzero status.
-                exit_status = 1;
-                break;
-              }
-          }
-        catch (const quit_debug_exception&)
-          {
-            m_interpreter.recover_from_exception ();
-
-            // FIXME: Does anything else need to happen here?
-          }
-        catch (const std::bad_alloc&)
-          {
-            m_interpreter.recover_from_exception ();
-
-            std::cerr << "error: out of memory -- trying to return to prompt"
-                      << std::endl;
-          }
-      }
-    while (exit_status == 0);
-
-    if (exit_status == EOF)
-      {
-        if (m_interpreter.interactive ())
-          octave_stdout << "\n";
-
-        exit_status = 0;
-      }
-
-    return exit_status;
-  }
-
-  int tree_evaluator::server_loop ()
-  {
-    // Process events from the event queue.
-
-    unwind_protect_var<bool> upv1 (m_server_mode, true);
-
-    m_exit_status = 0;
-
-    std::shared_ptr<push_parser> parser (new push_parser (m_interpreter));
-    unwind_protect_var<std::shared_ptr<push_parser>> upv2 (m_parser, parser);
-
-    // FIXME: We are currently resetting the parser after every call to
-    // recover_from_exception.  This action should probably be handled
-    // in a more consistent way, but resetting the parser in every call
-    // to interpreter::recover_from_exception appears to cause
-    // segfaults in the test suite.
-
-    do
-      {
-        try
-          {
-            // FIXME: Should we call octave_quit in the octave::sleep
-            // and/or command_editor::run_event_hooks functions?
-
-            octave_quit ();
-
-            // FIXME: Running the event queue should be decoupled from
-            // the command_editor.  We should also use a condition
-            // variable to manage the execution of entries in the queue
-            // and eliminate the need for the busy-wait loop.
-
-            command_editor::run_event_hooks ();
-
-            release_unreferenced_dynamic_libraries ();
-
-            sleep (0.1);
-          }
-        catch (const interrupt_exception&)
-          {
-            octave_interrupt_state = 1;
-            m_interpreter.recover_from_exception ();
-            m_parser->reset ();
-
-            // Required newline when the user does Ctrl+C at the prompt.
-            if (m_interpreter.interactive ())
-              octave_stdout << "\n";
-          }
-        catch (const index_exception& e)
-          {
+  // The following logic is written as it is to allow easy transition
+  // to setting USE_COMMAND_LINE_PUSH_PARSER at run time and to
+  // simplify the logic of the main loop below by using the same
+  // base_parser::run interface for both push and pull parsers.
+
+  std::shared_ptr<base_parser> repl_parser;
+
+  if (m_interpreter.interactive ())
+    {
+      if (use_command_line_push_parser)
+        {
+          push_parser *pp
+            = new push_parser (m_interpreter,
+                               new input_reader (m_interpreter));
+
+          repl_parser = std::shared_ptr<base_parser> (pp);
+        }
+      else
+        {
+          parser *pp = new parser (new lexer (m_interpreter));
+          repl_parser = std::shared_ptr<base_parser> (pp);
+        }
+    }
+  else
+    {
+      parser *pp = new parser (new lexer (stdin, m_interpreter));
+      repl_parser = std::shared_ptr<base_parser> (pp);
+    }
+
+  do
+    {
+      try
+        {
+          unwind_protect_var<bool> upv (m_in_top_level_repl, true);
+
+          repl_parser->reset ();
+
+          if (at_top_level ())
+            {
+              dbstep_flag (0);
+              reset_debug_state ();
+            }
+
+          exit_status = repl_parser->run ();
+
+          if (exit_status == 0)
+            {
+              std::shared_ptr<tree_statement_list>
+                stmt_list = repl_parser->statement_list ();
+
+              if (stmt_list)
+                {
+                  command_editor::increment_current_command_number ();
+
+                  eval (stmt_list, m_interpreter.interactive ());
+                }
+              else if (repl_parser->at_end_of_input ())
+                {
+                  exit_status = EOF;
+                  break;
+                }
+            }
+        }
+      catch (const interrupt_exception&)
+        {
+          m_interpreter.recover_from_exception ();
+
+          // Required newline when the user does Ctrl+C at the prompt.
+          if (m_interpreter.interactive ())
+            octave_stdout << "\n";
+        }
+      catch (const index_exception& ie)
+        {
+          m_interpreter.recover_from_exception ();
+
+          std::cerr << "error: unhandled index exception: "
+                    << ie.message () << " -- trying to return to prompt"
+                    << std::endl;
+        }
+      catch (const execution_exception& ee)
+        {
+          error_system& es = m_interpreter.get_error_system ();
+
+          es.save_exception (ee);
+          es.display_exception (ee);
+
+          if (m_interpreter.interactive ())
             m_interpreter.recover_from_exception ();
-            m_parser->reset ();
-
-            std::cerr << "error: unhandled index exception: "
-                      << e.message () << " -- trying to return to prompt"
-                      << std::endl;
-          }
-        catch (const execution_exception& ee)
-          {
-            error_system& es = m_interpreter.get_error_system ();
-
-            es.save_exception (ee);
-            es.display_exception (ee);
-
-            if (m_interpreter.interactive ())
-              {
-                m_interpreter.recover_from_exception ();
-                m_parser->reset ();
-              }
-            else
-              {
-                // We should exit with a nonzero status.
-                m_exit_status = 1;
+          else
+            {
+              // We should exit with a nonzero status.
+              exit_status = 1;
+              break;
+            }
+        }
+      catch (const quit_debug_exception&)
+        {
+          m_interpreter.recover_from_exception ();
+
+          // FIXME: Does anything else need to happen here?
+        }
+      catch (const std::bad_alloc&)
+        {
+          m_interpreter.recover_from_exception ();
+
+          std::cerr << "error: out of memory -- trying to return to prompt"
+                    << std::endl;
+        }
+    }
+  while (exit_status == 0);
+
+  if (exit_status == EOF)
+    {
+      if (m_interpreter.interactive ())
+        octave_stdout << "\n";
+
+      exit_status = 0;
+    }
+
+  return exit_status;
+}
+
+int tree_evaluator::server_loop ()
+{
+  // Process events from the event queue.
+
+  unwind_protect_var<bool> upv1 (m_server_mode, true);
+
+  m_exit_status = 0;
+
+  std::shared_ptr<push_parser> parser (new push_parser (m_interpreter));
+  unwind_protect_var<std::shared_ptr<push_parser>> upv2 (m_parser, parser);
+
+  // FIXME: We are currently resetting the parser after every call to
+  // recover_from_exception.  This action should probably be handled
+  // in a more consistent way, but resetting the parser in every call
+  // to interpreter::recover_from_exception appears to cause
+  // segfaults in the test suite.
+
+  do
+    {
+      try
+        {
+          // FIXME: Should we call octave_quit in the octave::sleep
+          // and/or command_editor::run_event_hooks functions?
+
+          octave_quit ();
+
+          // FIXME: Running the event queue should be decoupled from
+          // the command_editor.  We should also use a condition
+          // variable to manage the execution of entries in the queue
+          // and eliminate the need for the busy-wait loop.
+
+          command_editor::run_event_hooks ();
+
+          release_unreferenced_dynamic_libraries ();
+
+          sleep (0.1);
+        }
+      catch (const interrupt_exception&)
+        {
+          octave_interrupt_state = 1;
+          m_interpreter.recover_from_exception ();
+          m_parser->reset ();
+
+          // Required newline when the user does Ctrl+C at the prompt.
+          if (m_interpreter.interactive ())
+            octave_stdout << "\n";
+        }
+      catch (const index_exception& e)
+        {
+          m_interpreter.recover_from_exception ();
+          m_parser->reset ();
+
+          std::cerr << "error: unhandled index exception: "
+                    << e.message () << " -- trying to return to prompt"
+                    << std::endl;
+        }
+      catch (const execution_exception& ee)
+        {
+          error_system& es = m_interpreter.get_error_system ();
+
+          es.save_exception (ee);
+          es.display_exception (ee);
+
+          if (m_interpreter.interactive ())
+            {
+              m_interpreter.recover_from_exception ();
+              m_parser->reset ();
+            }
+          else
+            {
+              // We should exit with a nonzero status.
+              m_exit_status = 1;
+              break;
+            }
+        }
+      catch (const quit_debug_exception&)
+        {
+          octave_interrupt_state = 1;
+          m_interpreter.recover_from_exception ();
+          m_parser->reset ();
+        }
+      catch (const exit_exception& xe)
+        {
+          m_exit_status = xe.exit_status ();
+          break;
+        }
+      catch (const std::bad_alloc&)
+        {
+          m_interpreter.recover_from_exception ();
+          m_parser->reset ();
+
+          std::cerr << "error: out of memory -- trying to return to prompt"
+                    << std::endl;
+        }
+    }
+  while (m_exit_status == 0);
+
+  if (m_exit_status == EOF)
+    {
+      if (m_interpreter.interactive ())
+        octave_stdout << "\n";
+
+      m_exit_status = 0;
+    }
+
+  return m_exit_status;
+}
+
+void tree_evaluator::eval (std::shared_ptr<tree_statement_list>& stmt_list,
+                           bool interactive)
+{
+  try
+    {
+      stmt_list->accept (*this);
+
+      octave_quit ();
+
+      if (! interactive)
+        {
+          bool quit = (m_returning || m_breaking);
+
+          if (m_returning)
+            m_returning = 0;
+
+          if (m_breaking)
+            m_breaking--;
+
+          if (quit)
+            return;
+        }
+
+      if (octave_completion_matches_called)
+        octave_completion_matches_called = false;
+    }
+  catch (const quit_debug_exception&)
+    {
+      m_interpreter.recover_from_exception ();
+    }
+}
+
+octave_value_list
+tree_evaluator::eval_string (const std::string& eval_str, bool silent,
+                             int& parse_status, int nargout)
+{
+  octave_value_list retval;
+
+  parser eval_parser (eval_str, m_interpreter);
+
+  do
+    {
+      eval_parser.reset ();
+
+      // If we are looking at
+      //
+      //   val = eval ("code");
+      //
+      // then don't allow code to be parsed as a command.
+
+      if (nargout > 0)
+        eval_parser.disallow_command_syntax ();
+
+      parse_status = eval_parser.run ();
+
+      if (parse_status == 0)
+        {
+          std::shared_ptr<tree_statement_list> stmt_list
+            = eval_parser.statement_list ();
+
+          if (stmt_list)
+            {
+              tree_statement *stmt = nullptr;
+
+              if (stmt_list->length () == 1
+                  && (stmt = stmt_list->front ())
+                  && stmt->is_expression ())
+                {
+                  tree_expression *expr = stmt->expression ();
+
+                  if (silent)
+                    expr->set_print_flag (false);
+
+                  retval = expr->evaluate_n (*this, nargout);
+
+                  bool do_bind_ans = false;
+
+                  if (expr->is_identifier ())
+                    do_bind_ans = ! is_variable (expr);
+                  else
+                    do_bind_ans = ! expr->is_assignment_expression ();
+
+                  if (do_bind_ans && ! retval.empty ())
+                    bind_ans (retval(0), expr->print_result ());
+
+                  if (nargout == 0)
+                    retval = octave_value_list ();
+                }
+              else if (nargout == 0)
+                stmt_list->accept (*this);
+              else
+                error ("eval: invalid use of statement list");
+
+              if (returning () || breaking () || continuing ())
                 break;
-              }
-          }
-        catch (const quit_debug_exception&)
-          {
-            octave_interrupt_state = 1;
-            m_interpreter.recover_from_exception ();
-            m_parser->reset ();
-          }
-        catch (const exit_exception& xe)
-          {
-            m_exit_status = xe.exit_status ();
+            }
+          else if (eval_parser.at_end_of_input ())
             break;
-          }
-        catch (const std::bad_alloc&)
-          {
-            m_interpreter.recover_from_exception ();
-            m_parser->reset ();
-
-            std::cerr << "error: out of memory -- trying to return to prompt"
-                      << std::endl;
-          }
-      }
-    while (m_exit_status == 0);
-
-    if (m_exit_status == EOF)
-      {
-        if (m_interpreter.interactive ())
-          octave_stdout << "\n";
-
-        m_exit_status = 0;
-      }
-
-    return m_exit_status;
-  }
-
-  void tree_evaluator::eval (std::shared_ptr<tree_statement_list>& stmt_list,
-                             bool interactive)
-  {
-    try
-      {
-        stmt_list->accept (*this);
-
-        octave_quit ();
-
-        if (! interactive)
-          {
-            bool quit = (m_returning || m_breaking);
-
-            if (m_returning)
-              m_returning = 0;
-
-            if (m_breaking)
-              m_breaking--;
-
-            if (quit)
-              return;
-          }
-
-        if (octave_completion_matches_called)
-          octave_completion_matches_called = false;
-      }
-    catch (const quit_debug_exception&)
-      {
-        m_interpreter.recover_from_exception ();
-      }
-  }
-
-  octave_value_list
-  tree_evaluator::eval_string (const std::string& eval_str, bool silent,
-                               int& parse_status, int nargout)
-  {
-    octave_value_list retval;
-
-    parser eval_parser (eval_str, m_interpreter);
-
-    do
-      {
-        eval_parser.reset ();
-
-        // If we are looking at
-        //
-        //   val = eval ("code");
-        //
-        // then don't allow code to be parsed as a command.
-
-        if (nargout > 0)
-          eval_parser.disallow_command_syntax ();
-
-        parse_status = eval_parser.run ();
-
-        if (parse_status == 0)
-          {
-            std::shared_ptr<tree_statement_list> stmt_list
-              = eval_parser.statement_list ();
-
-            if (stmt_list)
-              {
-                tree_statement *stmt = nullptr;
-
-                if (stmt_list->length () == 1
-                    && (stmt = stmt_list->front ())
-                    && stmt->is_expression ())
-                  {
-                    tree_expression *expr = stmt->expression ();
-
-                    if (silent)
-                      expr->set_print_flag (false);
-
-                    retval = expr->evaluate_n (*this, nargout);
-
-                    bool do_bind_ans = false;
-
-                    if (expr->is_identifier ())
-                      do_bind_ans = ! is_variable (expr);
-                    else
-                      do_bind_ans = ! expr->is_assignment_expression ();
-
-                    if (do_bind_ans && ! retval.empty ())
-                      bind_ans (retval(0), expr->print_result ());
-
-                    if (nargout == 0)
-                      retval = octave_value_list ();
-                  }
-                else if (nargout == 0)
-                  stmt_list->accept (*this);
-                else
-                  error ("eval: invalid use of statement list");
-
-                if (returning () || breaking () || continuing ())
-                  break;
-              }
-            else if (eval_parser.at_end_of_input ())
-              break;
-          }
-      }
-    while (parse_status == 0);
-
-    return retval;
-  }
-
-  octave_value tree_evaluator::eval_string (const std::string& eval_str,
-                                            bool silent, int& parse_status)
-  {
-    octave_value retval;
-
-    octave_value_list tmp = eval_string (eval_str, silent, parse_status, 1);
-
-    if (! tmp.empty ())
-      retval = tmp(0);
-
-    return retval;
-  }
-
-  octave_value_list tree_evaluator::eval_string (const octave_value& arg,
-                                                 bool silent, int& parse_status,
-                                                 int nargout)
-  {
-    std::string s = arg.xstring_value ("eval: expecting string argument");
-
-    return eval_string (s, silent, parse_status, nargout);
-  }
-
-  octave_value_list tree_evaluator::eval (const std::string& try_code,
+        }
+    }
+  while (parse_status == 0);
+
+  return retval;
+}
+
+octave_value tree_evaluator::eval_string (const std::string& eval_str,
+                                          bool silent, int& parse_status)
+{
+  octave_value retval;
+
+  octave_value_list tmp = eval_string (eval_str, silent, parse_status, 1);
+
+  if (! tmp.empty ())
+    retval = tmp(0);
+
+  return retval;
+}
+
+octave_value_list tree_evaluator::eval_string (const octave_value& arg,
+                                               bool silent, int& parse_status,
+                                               int nargout)
+{
+  std::string s = arg.xstring_value ("eval: expecting string argument");
+
+  return eval_string (s, silent, parse_status, nargout);
+}
+
+octave_value_list tree_evaluator::eval (const std::string& try_code,
+                                        int nargout)
+{
+  int parse_status = 0;
+
+  return eval_string (try_code, nargout > 0, parse_status, nargout);
+}
+
+octave_value_list tree_evaluator::eval (const std::string& try_code,
+                                        const std::string& catch_code,
+                                        int nargout)
+{
+  octave_value_list retval;
+
+  error_system& es = m_interpreter.get_error_system ();
+
+  int parse_status = 0;
+
+  bool execution_error = false;
+
+  octave_value_list tmp;
+
+  try
+    {
+      tmp = eval_string (try_code, nargout > 0, parse_status, nargout);
+    }
+  catch (const execution_exception& ee)
+    {
+      es.save_exception (ee);
+      m_interpreter.recover_from_exception ();
+
+      execution_error = true;
+    }
+
+  if (parse_status != 0 || execution_error)
+    {
+      tmp = eval_string (catch_code, nargout > 0, parse_status, nargout);
+
+      retval = (nargout > 0) ? tmp : octave_value_list ();
+    }
+  else
+    {
+      if (nargout > 0)
+        retval = tmp;
+
+      // FIXME: we should really be rethrowing whatever
+      // exception occurred, not just throwing an
+      // execution exception.
+      if (execution_error)
+        throw execution_exception ();
+    }
+
+  return retval;
+}
+
+octave_value_list tree_evaluator::evalin (const std::string& context,
+                                          const std::string& try_code,
                                           int nargout)
-  {
-    int parse_status = 0;
-
-    return eval_string (try_code, nargout > 0, parse_status, nargout);
-  }
-
-  octave_value_list tree_evaluator::eval (const std::string& try_code,
+{
+  unwind_action act ([=] (std::size_t frm)
+                     {
+                       m_call_stack.restore_frame (frm);
+                     }, m_call_stack.current_frame ());
+
+  if (context == "caller")
+    m_call_stack.goto_caller_frame ();
+  else if (context == "base")
+    m_call_stack.goto_base_frame ();
+  else
+    error (R"(evalin: CONTEXT must be "caller" or "base")");
+
+  int parse_status = 0;
+
+  return eval_string (try_code, nargout > 0, parse_status, nargout);
+}
+
+octave_value_list tree_evaluator::evalin (const std::string& context,
+                                          const std::string& try_code,
                                           const std::string& catch_code,
                                           int nargout)
-  {
-    octave_value_list retval;
-
-    error_system& es = m_interpreter.get_error_system ();
-
-    int parse_status = 0;
-
-    bool execution_error = false;
-
-    octave_value_list tmp;
-
-    try
-      {
-        tmp = eval_string (try_code, nargout > 0, parse_status, nargout);
-      }
-    catch (const execution_exception& ee)
-      {
-        es.save_exception (ee);
-        m_interpreter.recover_from_exception ();
-
-        execution_error = true;
-      }
-
-    if (parse_status != 0 || execution_error)
-      {
-        tmp = eval_string (catch_code, nargout > 0, parse_status, nargout);
-
-        retval = (nargout > 0) ? tmp : octave_value_list ();
-      }
-    else
-      {
-        if (nargout > 0)
-          retval = tmp;
-
-        // FIXME: we should really be rethrowing whatever
-        // exception occurred, not just throwing an
-        // execution exception.
-        if (execution_error)
-          throw execution_exception ();
-      }
-
-    return retval;
-  }
-
-  octave_value_list tree_evaluator::evalin (const std::string& context,
-                                            const std::string& try_code,
-                                            int nargout)
-  {
-    unwind_action act ([=] (std::size_t frm)
-                       {
-                         m_call_stack.restore_frame (frm);
-                       }, m_call_stack.current_frame ());
-
-    if (context == "caller")
-      m_call_stack.goto_caller_frame ();
-    else if (context == "base")
-      m_call_stack.goto_base_frame ();
-    else
-      error (R"(evalin: CONTEXT must be "caller" or "base")");
-
-    int parse_status = 0;
-
-    return eval_string (try_code, nargout > 0, parse_status, nargout);
-  }
-
-  octave_value_list tree_evaluator::evalin (const std::string& context,
-                                            const std::string& try_code,
-                                            const std::string& catch_code,
-                                            int nargout)
-  {
-    octave_value_list retval;
-
-    unwind_action act1 ([=] (std::size_t frm)
-                        {
-                          m_call_stack.restore_frame (frm);
-                        }, m_call_stack.current_frame ());
-
-    if (context == "caller")
-      m_call_stack.goto_caller_frame ();
-    else if (context == "base")
-      m_call_stack.goto_base_frame ();
-    else
-      error (R"(evalin: CONTEXT must be "caller" or "base")");
-
-    error_system& es = m_interpreter.get_error_system ();
-
-    int parse_status = 0;
-
-    bool execution_error = false;
-
-    octave_value_list tmp;
-
-    try
-      {
-        tmp = eval_string (try_code, nargout > 0, parse_status, nargout);
-      }
-    catch (const execution_exception& ee)
-      {
-        es.save_exception (ee);
-        m_interpreter.recover_from_exception ();
-
-        execution_error = true;
-      }
-
-    if (parse_status != 0 || execution_error)
-      {
-        tmp = eval_string (catch_code, nargout > 0, parse_status, nargout);
-
-        retval = (nargout > 0) ? tmp : octave_value_list ();
-      }
-    else
-      {
-        if (nargout > 0)
-          retval = tmp;
-
-        // FIXME: we should really be rethrowing whatever
-        // exception occurred, not just throwing an
-        // execution exception.
-        if (execution_error)
-          throw execution_exception ();
-      }
-
+{
+  octave_value_list retval;
+
+  unwind_action act1 ([=] (std::size_t frm)
+                      {
+                        m_call_stack.restore_frame (frm);
+                      }, m_call_stack.current_frame ());
+
+  if (context == "caller")
+    m_call_stack.goto_caller_frame ();
+  else if (context == "base")
+    m_call_stack.goto_base_frame ();
+  else
+    error (R"(evalin: CONTEXT must be "caller" or "base")");
+
+  error_system& es = m_interpreter.get_error_system ();
+
+  int parse_status = 0;
+
+  bool execution_error = false;
+
+  octave_value_list tmp;
+
+  try
+    {
+      tmp = eval_string (try_code, nargout > 0, parse_status, nargout);
+    }
+  catch (const execution_exception& ee)
+    {
+      es.save_exception (ee);
+      m_interpreter.recover_from_exception ();
+
+      execution_error = true;
+    }
+
+  if (parse_status != 0 || execution_error)
+    {
+      tmp = eval_string (catch_code, nargout > 0, parse_status, nargout);
+
+      retval = (nargout > 0) ? tmp : octave_value_list ();
+    }
+  else
+    {
+      if (nargout > 0)
+        retval = tmp;
+
+      // FIXME: we should really be rethrowing whatever
+      // exception occurred, not just throwing an
+      // execution exception.
+      if (execution_error)
+        throw execution_exception ();
+    }
+
+  return retval;
+}
+
+void
+tree_evaluator::visit_anon_fcn_handle (tree_anon_fcn_handle&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_argument_list (tree_argument_list&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_arguments_block (tree_arguments_block&)
+{
+  warning ("function arguments validation blocks are not supported; INCORRECT RESULTS ARE POSSIBLE");
+}
+
+void
+tree_evaluator::visit_args_block_attribute_list (tree_args_block_attribute_list&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_args_block_validation_list (tree_args_block_validation_list&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_arg_validation (tree_arg_validation&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_arg_size_spec (tree_arg_size_spec&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_arg_validation_fcns (tree_arg_validation_fcns&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_binary_expression (tree_binary_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_boolean_expression (tree_boolean_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_compound_binary_expression (tree_compound_binary_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_break_command (tree_break_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  if (m_debug_mode)
+    do_breakpoint (cmd.is_active_breakpoint (*this));
+
+  if (m_in_loop_command)
+    m_breaking = 1;
+  else
+    error ("break must appear in a loop in the same file as loop command");
+}
+
+void
+tree_evaluator::visit_colon_expression (tree_colon_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_continue_command (tree_continue_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  if (m_debug_mode)
+    do_breakpoint (cmd.is_active_breakpoint (*this));
+
+  if (m_in_loop_command)
+    m_continuing = 1;
+}
+
+bool
+tree_evaluator::statement_printing_enabled ()
+{
+  return ! (m_silent_functions && (m_statement_context == SC_FUNCTION
+                                   || m_statement_context == SC_SCRIPT));
+}
+
+void
+tree_evaluator::reset_debug_state ()
+{
+  m_debug_mode = (m_bp_table.have_breakpoints ()
+                  || m_dbstep_flag != 0
+                  || m_break_on_next_stmt
+                  || in_debug_repl ());
+}
+
+void
+tree_evaluator::reset_debug_state (bool mode)
+{
+  m_debug_mode = mode;
+}
+
+void
+tree_evaluator::enter_debugger (const std::string& prompt)
+{
+  unwind_protect frame;
+
+  frame.add (command_history::ignore_entries,
+             command_history::ignoring_entries ());
+
+  command_history::ignore_entries (false);
+
+  frame.add (&call_stack::restore_frame, &m_call_stack,
+             m_call_stack.current_frame ());
+
+  // Don't allow errors or warnings at the debug prompt to push us
+  // into deeper levels of debugging.
+
+  error_system& es = m_interpreter.get_error_system ();
+
+  frame.add (&error_system::set_debug_on_error, &es, es.debug_on_error ());
+
+  frame.add (&error_system::set_debug_on_warning, &es,
+             es.debug_on_warning ());
+
+  es.debug_on_error (false);
+  es.debug_on_warning (false);
+
+  // Go up to the nearest user code frame.
+
+  m_debug_frame = m_call_stack.dbupdown (0);
+
+  // FIXME: probably we just want to print one line, not the
+  // entire statement, which might span many lines...
+  //
+  // tree_print_code tpc (octave_stdout);
+  // stmt.accept (tpc);
+
+  debugger *dbgr = new debugger (m_interpreter, m_debugger_stack.size ());
+
+  m_debugger_stack.push (dbgr);
+
+  frame.add ([=] ()
+             {
+               delete m_debugger_stack.top ();
+               m_debugger_stack.pop ();
+               reset_debug_state ();
+             });
+
+  dbgr->repl (prompt);
+}
+
+void
+tree_evaluator::keyboard (const std::string& prompt)
+{
+  enter_debugger (prompt);
+}
+
+void
+tree_evaluator::dbupdown (int n, bool verbose)
+{
+  m_debug_frame = m_call_stack.dbupdown (n, verbose);
+}
+
+Matrix
+tree_evaluator::ignored_fcn_outputs () const
+{
+  Matrix retval;
+
+  const std::list<octave_lvalue> *lvalues = m_lvalue_list;
+
+  if (! lvalues)
     return retval;
-  }
-
-  void
-  tree_evaluator::visit_anon_fcn_handle (tree_anon_fcn_handle&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_argument_list (tree_argument_list&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_arguments_block (tree_arguments_block&)
-  {
-    warning ("function arguments validation blocks are not supported; INCORRECT RESULTS ARE POSSIBLE");
-  }
-
-  void
-  tree_evaluator::visit_args_block_attribute_list (tree_args_block_attribute_list&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_args_block_validation_list (tree_args_block_validation_list&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_arg_validation (tree_arg_validation&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_arg_size_spec (tree_arg_size_spec&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_arg_validation_fcns (tree_arg_validation_fcns&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_binary_expression (tree_binary_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_boolean_expression (tree_boolean_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_compound_binary_expression (tree_compound_binary_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_break_command (tree_break_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    if (m_debug_mode)
-      do_breakpoint (cmd.is_active_breakpoint (*this));
-
-    if (m_in_loop_command)
-      m_breaking = 1;
-    else
-      error ("break must appear in a loop in the same file as loop command");
-  }
-
-  void
-  tree_evaluator::visit_colon_expression (tree_colon_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_continue_command (tree_continue_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    if (m_debug_mode)
-      do_breakpoint (cmd.is_active_breakpoint (*this));
-
-    if (m_in_loop_command)
-      m_continuing = 1;
-  }
-
-  bool
-  tree_evaluator::statement_printing_enabled ()
-  {
-    return ! (m_silent_functions && (m_statement_context == SC_FUNCTION
-                                     || m_statement_context == SC_SCRIPT));
-  }
-
-  void
-  tree_evaluator::reset_debug_state ()
-  {
-    m_debug_mode = (m_bp_table.have_breakpoints ()
-                    || m_dbstep_flag != 0
-                    || m_break_on_next_stmt
-                    || in_debug_repl ());
-  }
-
-  void
-  tree_evaluator::reset_debug_state (bool mode)
-  {
-    m_debug_mode = mode;
-  }
-
-  void
-  tree_evaluator::enter_debugger (const std::string& prompt)
-  {
-    unwind_protect frame;
-
-    frame.add (command_history::ignore_entries,
-               command_history::ignoring_entries ());
-
-    command_history::ignore_entries (false);
-
-    frame.add (&call_stack::restore_frame, &m_call_stack,
-               m_call_stack.current_frame ());
-
-    // Don't allow errors or warnings at the debug prompt to push us
-    // into deeper levels of debugging.
-
-    error_system& es = m_interpreter.get_error_system ();
-
-    frame.add (&error_system::set_debug_on_error, &es, es.debug_on_error ());
-
-    frame.add (&error_system::set_debug_on_warning, &es,
-               es.debug_on_warning ());
-
-    es.debug_on_error (false);
-    es.debug_on_warning (false);
-
-    // Go up to the nearest user code frame.
-
-    m_debug_frame = m_call_stack.dbupdown (0);
-
-    // FIXME: probably we just want to print one line, not the
-    // entire statement, which might span many lines...
-    //
-    // tree_print_code tpc (octave_stdout);
-    // stmt.accept (tpc);
-
-    debugger *dbgr = new debugger (m_interpreter, m_debugger_stack.size ());
-
-    m_debugger_stack.push (dbgr);
-
-    frame.add ([=] ()
-               {
-                 delete m_debugger_stack.top ();
-                 m_debugger_stack.pop ();
-                 reset_debug_state ();
-               });
-
-    dbgr->repl (prompt);
-  }
-
-  void
-  tree_evaluator::keyboard (const std::string& prompt)
-  {
-    enter_debugger (prompt);
-  }
-
-  void
-  tree_evaluator::dbupdown (int n, bool verbose)
-  {
-    m_debug_frame = m_call_stack.dbupdown (n, verbose);
-  }
-
-  Matrix
-  tree_evaluator::ignored_fcn_outputs () const
-  {
-    Matrix retval;
-
-    const std::list<octave_lvalue> *lvalues = m_lvalue_list;
-
-    if (! lvalues)
-      return retval;
-
-    octave_idx_type nbh = 0;
-
-    for (const auto& lval : *lvalues)
-      nbh += lval.is_black_hole ();
-
-    if (nbh > 0)
-      {
-        retval.resize (1, nbh);
-
-        octave_idx_type k = 0;
-        octave_idx_type l = 0;
-
-        for (const auto& lval : *lvalues)
-          {
-            if (lval.is_black_hole ())
-              retval(l++) = k+1;
-
-            k += lval.numel ();
-          }
-      }
-
-    return retval;
-  }
-
-  // If NAME is an operator (like "+", "-", ...), convert it to the
-  // corresponding function name ("plus", "minus", ...).
-
-  static std::string
-  get_operator_function_name (const std::string& name)
-  {
-    // Bow to the god of compatibility.
-
-    // FIXME: it seems ugly to put this here, but there is no single
-    // function in the parser that converts from the operator name to
-    // the corresponding function name.  At least try to do it without N
-    // string compares.
-
-    std::size_t len = name.length ();
-
-    if (len == 2)
-      {
-        if (name[0] == '.')
-          {
-            switch (name[1])
-              {
-              case '\'':
-                return "transpose";
-
-              case '*':
-                return "times";
-
-              case '/':
-                return "rdivide";
-
-              case '^':
-                return "power";
-
-              case '\\':
-                return "ldivide";
-
-              default:
-                break;
-              }
-          }
-        else if (name[1] == '=')
-          {
-            switch (name[0])
-              {
-              case '<':
-                return "le";
-
-              case '=':
-                return "eq";
-
-              case '>':
-                return "ge";
-
-              case '~':
-              case '!':
-                return "ne";
-
-              default:
-                break;
-              }
-          }
-      }
-    else if (len == 1)
-      {
-        switch (name[0])
-          {
-          case '~':
-          case '!':
-            return "not";
-
-          case '\'':
-            return "ctranspose";
-
-          case '+':
-            return "plus";
-
-          case '-':
-            return "minus";
-
-          case '*':
-            return "mtimes";
-
-          case '/':
-            return "mrdivide";
-
-          case '^':
-            return "mpower";
-
-          case '\\':
-            return "mldivide";
-
-          case '<':
-            return "lt";
-
-          case '>':
-            return "gt";
-
-          case '&':
-            return "and";
-
-          case '|':
-            return "or";
-
-          default:
-            break;
-          }
-      }
-
-    return name;
-  }
-
-  // Creates a function handle that takes into account the context,
-  // finding local, nested, private, or sub functions.
-
-  octave_value
-  tree_evaluator::make_fcn_handle (const std::string& name)
-  {
-    octave_value retval;
-
-    // The str2func function can create a function handle with the name
-    // of an operator (for example, "+").  If so, it is converted to the
-    // name of the corresponding function ("+" -> "plus") and we create
-    // a simple function handle using that name.
-
-    std::string fcn_name = get_operator_function_name (name);
-
-    // If FCN_NAME is different from NAME, then NAME is an operator.  As
-    // of version 2020a, Matlab apparently uses the function name
-    // corresponding to the operator to search for private and local
-    // functions in the current scope but not(!) nested functions.
-
-    bool name_is_operator = fcn_name != name;
-
-    std::size_t pos = fcn_name.find ('.');
-
-    if (pos != std::string::npos)
-      {
-        // Recognize (some of?  which ones?) the following cases
-        // and create something other than a simple function handle?
-        // Should we just be checking for the last two when the first
-        // element of the dot-separated list is an object?  If so, then
-        // should this syntax be limited to a dot-separated list with
-        // exactly two elements?
-        //
-        //   object . method
-        //   object . static-method
-        //
-        // Code to do that duplicates some of simple_fcn_handle::call.
-
-        // Only accept expressions that contain one '.' separator.
-
-        // FIXME: The logic here is a bit complicated.  Is there a good
-        // way to simplify it?
-
-        std::string meth_nm = fcn_name.substr (pos+1);
-
-        if (meth_nm.find ('.') == std::string::npos)
-          {
-            std::string obj_nm = fcn_name.substr (0, pos);
-
-            // If obj_nm is an object in the current scope with a
-            // method named meth_nm, create a classsimple handle.
-
-            octave_value object = varval (obj_nm);
-
-            if (object.is_defined () && object.is_classdef_object ())
-              {
-                octave_classdef *cdef = object.classdef_object_value ();
-
-                if (cdef)
-                  {
-                    std::string class_nm = cdef->class_name ();
-
-                    cdef_object cdef_obj = cdef->get_object ();
-
-                    cdef_class cls = cdef_obj.get_class ();
-
-                    cdef_method meth = cls.find_method (meth_nm);
-
-                    if (meth.ok ())
-                      {
-                        // If the method we found is static, create a
-                        // new function name from the class name and
-                        // method name and create a simple function
-                        // handle below.  Otherwise, create a class
-                        // simple function handle.
-
-                        if (meth.is_static ())
-                          fcn_name = class_nm + '.' + meth_nm;
-                        else
-                          {
-                            octave_value meth_fcn = meth.get_function ();
-
-                            octave_fcn_handle *fh
-                              = new octave_fcn_handle (object, meth_fcn,
-                                                       class_nm, meth_nm);
-
-                            return octave_value (fh);
-                          }
-                      }
-                  }
-              }
-          }
-
-        // We didn't match anything above, so create handle to SIMPLE
-        // package function or static class method.  Function resolution
-        // is performed when the handle is used.
-
-        return octave_value (new octave_fcn_handle (fcn_name));
-      }
-
-    // If the function name refers to a sub/local/private function or a
-    // class method/constructor, create scoped function handle that is
-    // bound to that function.  Use the same precedence list as
-    // fcn_info::find but limit search to the following types of
-    // functions:
-    //
-    //   nested functions (and subfunctions)
-    //   local functions in the current file
-    //   private function
-    //   class method
-    //
-    // For anything else we create a simple function handle that will be
-    // resolved dynamically in the scope where it is evaluated.
-
-    symbol_scope curr_scope = get_current_scope ();
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    if (curr_scope)
-      {
-        octave_value ov_fcn
-          = symtab.find_scoped_function (fcn_name, curr_scope);
-
-        // If name is operator, we are in Fstr2func, so skip the stack
-        // frame for that function.
-
-        bool skip_first = name_is_operator;
-        octave_function *curr_fcn = current_function (skip_first);
-
-        if (ov_fcn.is_defined ())
-          {
-            octave_function *fcn = ov_fcn.function_value ();
-
-            if (fcn->is_nested_function ())
-              {
-                if (! name_is_operator)
-                  {
-                    // Get current stack frame and return handle to nested
-                    // function.
-
-                    std::shared_ptr<stack_frame> frame
-                      = m_call_stack.get_current_stack_frame ();
-
-                    // If we are creating a handle to the current
-                    // function or a handle to a sibling function (i.e.,
-                    // not a child of the current function), then use
-                    // the calling stack frame as the context instead of
-                    // the current stack frame.
-
-                    // FIXME:  Do we need both checks here or is it
-                    // sufficient to check that the parent of curr_fcn
-                    // is the same as the parent of fcn?  Is there any
-                    // case where curr_fcn could be nullptr, or does
-                    // that indicate an internal error of some kind?
-
-                    if (curr_fcn
-                        && (fcn_name == curr_fcn->name ()
-                            || fcn->parent_fcn_name () == curr_fcn->parent_fcn_name ()))
-                      frame = frame->access_link ();
-
-                    octave_fcn_handle *fh
-                      = new octave_fcn_handle (ov_fcn, fcn_name, frame);
-
-                    return octave_value (fh);
-                  }
-              }
-            else if (fcn->is_subfunction ()
-                     /* || fcn->is_localfunction () */
-                     || fcn->is_private_function ())
-              {
-                // Create handle to SCOPED function (sub/local function
-                // or private function).
-
-                std::list<std::string> parentage = fcn->parent_fcn_names ();
-
-                octave_fcn_handle *fh
-                  = new octave_fcn_handle (ov_fcn, fcn_name, parentage);
-
-                return octave_value (fh);
-              }
-          }
-
-        if (curr_fcn && (curr_fcn->is_class_method ()
-                         || curr_fcn->is_class_constructor ()))
-          {
-            std::string dispatch_class = curr_fcn->dispatch_class ();
-
-            octave_value ov_meth
-              = symtab.find_method (fcn_name, dispatch_class);
-
-            if (ov_meth.is_defined ())
-              {
-                octave_function *fcn = ov_meth.function_value ();
-
-                // FIXME: do we need to check that it is a method of
-                // dispatch_class, or is it sufficient to just check
-                // that it is a method?
-
-                if (fcn->is_class_method ())
-                  {
-                    // Create CLASSSIMPLE handle to method but don't
-                    // bind to the method.  Lookup will be done later.
-
-                    octave_fcn_handle *fh
-                      = new octave_fcn_handle (dispatch_class, fcn_name);
-
-                    return octave_value (fh);
-                  }
-              }
-          }
-      }
-
-    octave_value ov_fcn = symtab.find_user_function (fcn_name);
-
-    // Create handle to SIMPLE function.  If the function is not found
-    // now, then we will look for it again when the handle is used.
-
-    return octave_value (new octave_fcn_handle (ov_fcn, fcn_name));
-  }
+
+  octave_idx_type nbh = 0;
+
+  for (const auto& lval : *lvalues)
+    nbh += lval.is_black_hole ();
+
+  if (nbh > 0)
+    {
+      retval.resize (1, nbh);
+
+      octave_idx_type k = 0;
+      octave_idx_type l = 0;
+
+      for (const auto& lval : *lvalues)
+        {
+          if (lval.is_black_hole ())
+            retval(l++) = k+1;
+
+          k += lval.numel ();
+        }
+    }
+
+  return retval;
+}
+
+// If NAME is an operator (like "+", "-", ...), convert it to the
+// corresponding function name ("plus", "minus", ...).
+
+static std::string
+get_operator_function_name (const std::string& name)
+{
+  // Bow to the god of compatibility.
+
+  // FIXME: it seems ugly to put this here, but there is no single
+  // function in the parser that converts from the operator name to
+  // the corresponding function name.  At least try to do it without N
+  // string compares.
+
+  std::size_t len = name.length ();
+
+  if (len == 2)
+    {
+      if (name[0] == '.')
+        {
+          switch (name[1])
+            {
+            case '\'':
+              return "transpose";
+
+            case '*':
+              return "times";
+
+            case '/':
+              return "rdivide";
+
+            case '^':
+              return "power";
+
+            case '\\':
+              return "ldivide";
+
+            default:
+              break;
+            }
+        }
+      else if (name[1] == '=')
+        {
+          switch (name[0])
+            {
+            case '<':
+              return "le";
+
+            case '=':
+              return "eq";
+
+            case '>':
+              return "ge";
+
+            case '~':
+            case '!':
+              return "ne";
+
+            default:
+              break;
+            }
+        }
+    }
+  else if (len == 1)
+    {
+      switch (name[0])
+        {
+        case '~':
+        case '!':
+          return "not";
+
+        case '\'':
+          return "ctranspose";
+
+        case '+':
+          return "plus";
+
+        case '-':
+          return "minus";
+
+        case '*':
+          return "mtimes";
+
+        case '/':
+          return "mrdivide";
+
+        case '^':
+          return "mpower";
+
+        case '\\':
+          return "mldivide";
+
+        case '<':
+          return "lt";
+
+        case '>':
+          return "gt";
+
+        case '&':
+          return "and";
+
+        case '|':
+          return "or";
+
+        default:
+          break;
+        }
+    }
+
+  return name;
+}
+
+// Creates a function handle that takes into account the context,
+// finding local, nested, private, or sub functions.
+
+octave_value
+tree_evaluator::make_fcn_handle (const std::string& name)
+{
+  octave_value retval;
+
+  // The str2func function can create a function handle with the name
+  // of an operator (for example, "+").  If so, it is converted to the
+  // name of the corresponding function ("+" -> "plus") and we create
+  // a simple function handle using that name.
+
+  std::string fcn_name = get_operator_function_name (name);
+
+  // If FCN_NAME is different from NAME, then NAME is an operator.  As
+  // of version 2020a, Matlab apparently uses the function name
+  // corresponding to the operator to search for private and local
+  // functions in the current scope but not(!) nested functions.
+
+  bool name_is_operator = fcn_name != name;
+
+  std::size_t pos = fcn_name.find ('.');
+
+  if (pos != std::string::npos)
+    {
+      // Recognize (some of?  which ones?) the following cases
+      // and create something other than a simple function handle?
+      // Should we just be checking for the last two when the first
+      // element of the dot-separated list is an object?  If so, then
+      // should this syntax be limited to a dot-separated list with
+      // exactly two elements?
+      //
+      //   object . method
+      //   object . static-method
+      //
+      // Code to do that duplicates some of simple_fcn_handle::call.
+
+      // Only accept expressions that contain one '.' separator.
+
+      // FIXME: The logic here is a bit complicated.  Is there a good
+      // way to simplify it?
+
+      std::string meth_nm = fcn_name.substr (pos+1);
+
+      if (meth_nm.find ('.') == std::string::npos)
+        {
+          std::string obj_nm = fcn_name.substr (0, pos);
+
+          // If obj_nm is an object in the current scope with a
+          // method named meth_nm, create a classsimple handle.
+
+          octave_value object = varval (obj_nm);
+
+          if (object.is_defined () && object.is_classdef_object ())
+            {
+              octave_classdef *cdef = object.classdef_object_value ();
+
+              if (cdef)
+                {
+                  std::string class_nm = cdef->class_name ();
+
+                  cdef_object cdef_obj = cdef->get_object ();
+
+                  cdef_class cls = cdef_obj.get_class ();
+
+                  cdef_method meth = cls.find_method (meth_nm);
+
+                  if (meth.ok ())
+                    {
+                      // If the method we found is static, create a
+                      // new function name from the class name and
+                      // method name and create a simple function
+                      // handle below.  Otherwise, create a class
+                      // simple function handle.
+
+                      if (meth.is_static ())
+                        fcn_name = class_nm + '.' + meth_nm;
+                      else
+                        {
+                          octave_value meth_fcn = meth.get_function ();
+
+                          octave_fcn_handle *fh
+                            = new octave_fcn_handle (object, meth_fcn,
+                                                     class_nm, meth_nm);
+
+                          return octave_value (fh);
+                        }
+                    }
+                }
+            }
+        }
+
+      // We didn't match anything above, so create handle to SIMPLE
+      // package function or static class method.  Function resolution
+      // is performed when the handle is used.
+
+      return octave_value (new octave_fcn_handle (fcn_name));
+    }
+
+  // If the function name refers to a sub/local/private function or a
+  // class method/constructor, create scoped function handle that is
+  // bound to that function.  Use the same precedence list as
+  // fcn_info::find but limit search to the following types of
+  // functions:
+  //
+  //   nested functions (and subfunctions)
+  //   local functions in the current file
+  //   private function
+  //   class method
+  //
+  // For anything else we create a simple function handle that will be
+  // resolved dynamically in the scope where it is evaluated.
+
+  symbol_scope curr_scope = get_current_scope ();
+
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  if (curr_scope)
+    {
+      octave_value ov_fcn
+        = symtab.find_scoped_function (fcn_name, curr_scope);
+
+      // If name is operator, we are in Fstr2func, so skip the stack
+      // frame for that function.
+
+      bool skip_first = name_is_operator;
+      octave_function *curr_fcn = current_function (skip_first);
+
+      if (ov_fcn.is_defined ())
+        {
+          octave_function *fcn = ov_fcn.function_value ();
+
+          if (fcn->is_nested_function ())
+            {
+              if (! name_is_operator)
+                {
+                  // Get current stack frame and return handle to nested
+                  // function.
+
+                  std::shared_ptr<stack_frame> frame
+                    = m_call_stack.get_current_stack_frame ();
+
+                  // If we are creating a handle to the current
+                  // function or a handle to a sibling function (i.e.,
+                  // not a child of the current function), then use
+                  // the calling stack frame as the context instead of
+                  // the current stack frame.
+
+                  // FIXME:  Do we need both checks here or is it
+                  // sufficient to check that the parent of curr_fcn
+                  // is the same as the parent of fcn?  Is there any
+                  // case where curr_fcn could be nullptr, or does
+                  // that indicate an internal error of some kind?
+
+                  if (curr_fcn
+                      && (fcn_name == curr_fcn->name ()
+                          || fcn->parent_fcn_name () == curr_fcn->parent_fcn_name ()))
+                    frame = frame->access_link ();
+
+                  octave_fcn_handle *fh
+                    = new octave_fcn_handle (ov_fcn, fcn_name, frame);
+
+                  return octave_value (fh);
+                }
+            }
+          else if (fcn->is_subfunction ()
+                   /* || fcn->is_localfunction () */
+                   || fcn->is_private_function ())
+            {
+              // Create handle to SCOPED function (sub/local function
+              // or private function).
+
+              std::list<std::string> parentage = fcn->parent_fcn_names ();
+
+              octave_fcn_handle *fh
+                = new octave_fcn_handle (ov_fcn, fcn_name, parentage);
+
+              return octave_value (fh);
+            }
+        }
+
+      if (curr_fcn && (curr_fcn->is_class_method ()
+                       || curr_fcn->is_class_constructor ()))
+        {
+          std::string dispatch_class = curr_fcn->dispatch_class ();
+
+          octave_value ov_meth
+            = symtab.find_method (fcn_name, dispatch_class);
+
+          if (ov_meth.is_defined ())
+            {
+              octave_function *fcn = ov_meth.function_value ();
+
+              // FIXME: do we need to check that it is a method of
+              // dispatch_class, or is it sufficient to just check
+              // that it is a method?
+
+              if (fcn->is_class_method ())
+                {
+                  // Create CLASSSIMPLE handle to method but don't
+                  // bind to the method.  Lookup will be done later.
+
+                  octave_fcn_handle *fh
+                    = new octave_fcn_handle (dispatch_class, fcn_name);
+
+                  return octave_value (fh);
+                }
+            }
+        }
+    }
+
+  octave_value ov_fcn = symtab.find_user_function (fcn_name);
+
+  // Create handle to SIMPLE function.  If the function is not found
+  // now, then we will look for it again when the handle is used.
+
+  return octave_value (new octave_fcn_handle (ov_fcn, fcn_name));
+}
 
 /*
 %!test
@@ -1831,405 +1831,405 @@
 %! endfor
 */
 
-  octave_value
-  tree_evaluator::evaluate (tree_decl_elt *elt)
-  {
-    // Do not allow functions to return null values.
-
-    tree_identifier *id = elt->ident ();
-
-    return id ? id->evaluate (*this).storable_value () : octave_value ();
-  }
-
-  bool
-  tree_evaluator::is_variable (const std::string& name) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->is_variable (name);
-  }
-
-  bool
-  tree_evaluator::is_local_variable (const std::string& name) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->is_local_variable (name);
-  }
-
-  bool
-  tree_evaluator::is_variable (const tree_expression *expr) const
-  {
-    if (expr->is_identifier ())
-      {
-        const tree_identifier *id
-          = dynamic_cast<const tree_identifier *> (expr);
-
-        if (id->is_black_hole ())
-          return false;
-
-        return is_variable (id->symbol ());
-      }
-
-    return false;
-  }
-
-  bool
-  tree_evaluator::is_defined (const tree_expression *expr) const
-  {
-    if (expr->is_identifier ())
-      {
-        const tree_identifier *id
-          = dynamic_cast<const tree_identifier *> (expr);
-
-        return is_defined (id->symbol ());
-      }
-
-    return false;
-  }
-
-  bool
-  tree_evaluator::is_variable (const symbol_record& sym) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->is_variable (sym);
-  }
-
-  bool
-  tree_evaluator::is_defined (const symbol_record& sym) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->is_defined (sym);
-  }
-
-  bool tree_evaluator::is_global (const std::string& name) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->is_global (name);
-  }
-
-  octave_value
-  tree_evaluator::varval (const symbol_record& sym) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->varval (sym);
-  }
-
-  octave_value
-  tree_evaluator::varval (const std::string& name) const
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->varval (name);
-  }
-
-  void tree_evaluator::install_variable (const std::string& name,
-                                         const octave_value& value,
-                                         bool global)
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    return frame->install_variable (name, value, global);
-  }
-
-  octave_value
-  tree_evaluator::global_varval (const std::string& name) const
-  {
-    return m_call_stack.global_varval (name);
-  }
-
-  octave_value&
-  tree_evaluator::global_varref (const std::string& name)
-  {
-    return m_call_stack.global_varref (name);
-  }
-
-  void
-  tree_evaluator::global_assign (const std::string& name,
-                                 const octave_value& val)
-  {
-    m_call_stack.global_varref (name) = val;
-  }
-
-  octave_value
-  tree_evaluator::top_level_varval (const std::string& name) const
-  {
-    return m_call_stack.get_top_level_value (name);
-  }
-
-  void
-  tree_evaluator::top_level_assign (const std::string& name,
-                                    const octave_value& val)
-  {
-    m_call_stack.set_top_level_value (name, val);
-  }
-
-  void
-  tree_evaluator::assign (const std::string& name, const octave_value& val)
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    frame->assign (name, val);
-  }
-
-  void
-  tree_evaluator::assignin (const std::string& context,
-                            const std::string& name, const octave_value& val)
-  {
-    // FIXME: Can this be done without an unwind-protect frame, simply
-    // by getting a reference to the caller or base stack frame and
-    // calling assign on that?
-
-    unwind_action act ([=] (std::size_t frm)
-                       {
-                         m_call_stack.restore_frame (frm);
-                       }, m_call_stack.current_frame ());
-
-    if (context == "caller")
-      m_call_stack.goto_caller_frame ();
-    else if (context == "base")
-      m_call_stack.goto_base_frame ();
-    else
-      error (R"(assignin: CONTEXT must be "caller" or "base")");
-
-    if (valid_identifier (name))
-      {
-        // Put the check here so that we don't slow down assignments
-        // generally.  Any that go through Octave's parser should have
-        // already been checked.
-
-        if (iskeyword (name))
-          error ("assignin: invalid assignment to keyword '%s'",
-                 name.c_str ());
-
-        assign (name, val);
-      }
-    else
-      error ("assignin: invalid variable name '%s'", name.c_str ());
-  }
-
-  void
-  tree_evaluator::source_file (const std::string& file_name,
-                               const std::string& context,
-                               bool verbose, bool require_file)
-  {
-    // Map from absolute name of script file to recursion level.  We
-    // use a map instead of simply placing a limit on recursion in the
-    // source_file function so that two mutually recursive scripts
-    // written as
-    //
-    //   foo1.m:
-    //   ------
-    //   foo2
-    //
-    //   foo2.m:
-    //   ------
-    //   foo1
-    //
-    // and called with
-    //
-    //   foo1
-    //
-    // (for example) will behave the same if they are written as
-    //
-    //   foo1.m:
-    //   ------
-    //   source ("foo2.m")
-    //
-    //   foo2.m:
-    //   ------
-    //   source ("foo1.m")
-    //
-    // and called with
-    //
-    //   source ("foo1.m")
-    //
-    // (for example).
-
-    static std::map<std::string, int> source_call_depth;
-
-    std::string file_full_name
-      = sys::file_ops::tilde_expand (file_name);
-
-    std::size_t pos
-      = file_full_name.find_last_of (sys::file_ops::dir_sep_str ());
-
-    std::string dir_name = file_full_name.substr (0, pos);
-
-    file_full_name = sys::env::make_absolute (file_full_name);
-
-    unwind_protect frame;
-
-    if (source_call_depth.find (file_full_name) == source_call_depth.end ())
-      source_call_depth[file_full_name] = -1;
-
-    frame.protect_var (source_call_depth[file_full_name]);
-
-    source_call_depth[file_full_name]++;
-
-    if (source_call_depth[file_full_name] >= max_recursion_depth ())
-      error ("max_recursion_depth exceeded");
-
-    if (! context.empty ())
-      {
-        frame.add (&call_stack::restore_frame, &m_call_stack,
-                   m_call_stack.current_frame ());
-
-        if (context == "caller")
-          m_call_stack.goto_caller_frame ();
-        else if (context == "base")
-          m_call_stack.goto_base_frame ();
-        else
-          error (R"(source: CONTEXT must be "caller" or "base")");
-      }
-
-    // Find symbol name that would be in symbol_table, if it were loaded.
-    std::size_t dir_end
-      = file_name.find_last_of (sys::file_ops::dir_sep_chars ());
-    dir_end = (dir_end == std::string::npos) ? 0 : dir_end + 1;
-
-    std::size_t extension = file_name.find_last_of ('.');
-    if (extension == std::string::npos)
-      extension = file_name.length ();
-
-    std::string symbol = file_name.substr (dir_end, extension - dir_end);
-    std::string full_name = sys::canonicalize_file_name (file_name);
-
-    // Check if this file is already loaded (or in the path)
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-    octave_value ov_code = symtab.fcn_table_find (symbol);
-
-    // For compatibility with Matlab, accept both scripts and
-    // functions.
-
-    if (ov_code.is_user_code ())
-      {
-        octave_user_code *code = ov_code.user_code_value ();
-
-        if (! code
-            || (sys::canonicalize_file_name (code->fcn_file_name ())
-                != full_name))
-          {
-            // Wrong file, so load it below.
-            ov_code = octave_value ();
-          }
-      }
-    else
-      {
-        // Not a script, so load it below.
-        ov_code = octave_value ();
-      }
-
-    // If no symbol of this name, or the symbol is for a different
-    // file, load.
-
-    if (ov_code.is_undefined ())
-      {
-        try
-          {
-            ov_code = parse_fcn_file (m_interpreter, file_full_name,
-                                      file_name, dir_name, "", "",
-                                      require_file, true, false, false);
-          }
-        catch (execution_exception& ee)
-          {
-            error (ee, "source: error sourcing file '%s'",
-                   file_full_name.c_str ());
-          }
-      }
-
-    // Return or error if we don't have a valid script or function.
-
-    if (ov_code.is_undefined ())
-      return;
-
-    if (! ov_code.is_user_code ())
-      error ("source: %s is not a script", full_name.c_str ());
-
-    if (verbose)
-      {
-        octave_stdout << "executing commands from " << full_name << " ... ";
-        octave_stdout.flush ();
-      }
-
-    octave_user_code *code = ov_code.user_code_value ();
-
-    code->call (*this, 0, octave_value_list ());
-
-    if (verbose)
-      octave_stdout << "done." << std::endl;
-  }
-
-  void
-  tree_evaluator::set_auto_fcn_var (stack_frame::auto_var_type avt,
-                                    const octave_value& val)
-  {
-    m_call_stack.set_auto_fcn_var (avt, val);
-  }
-
-  octave_value
-  tree_evaluator::get_auto_fcn_var (stack_frame::auto_var_type avt) const
-  {
-    return m_call_stack.get_auto_fcn_var (avt);
-  }
-
-  void
-  tree_evaluator::define_parameter_list_from_arg_vector
-    (tree_parameter_list *param_list, const octave_value_list& args)
-  {
-    if (! param_list || param_list->varargs_only ())
-      return;
-
-    int i = -1;
-
-    for (tree_decl_elt *elt : *param_list)
-      {
-        i++;
-
-        octave_lvalue ref = elt->lvalue (*this);
-
-        if (i < args.length ())
-          {
-            if (args(i).is_defined () && args(i).is_magic_colon ())
-              {
-                if (! eval_decl_elt (elt))
-                  error ("no default value for argument %d", i+1);
-              }
-            else
-              ref.define (args(i));
-          }
-        else
-          eval_decl_elt (elt);
-      }
-  }
-
-  void
-  tree_evaluator::undefine_parameter_list (tree_parameter_list *param_list)
-  {
-    for (tree_decl_elt *elt : *param_list)
-      {
-        octave_lvalue ref = elt->lvalue (*this);
-
-        ref.assign (octave_value::op_asn_eq, octave_value ());
-      }
-  }
+octave_value
+tree_evaluator::evaluate (tree_decl_elt *elt)
+{
+  // Do not allow functions to return null values.
+
+  tree_identifier *id = elt->ident ();
+
+  return id ? id->evaluate (*this).storable_value () : octave_value ();
+}
+
+bool
+tree_evaluator::is_variable (const std::string& name) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->is_variable (name);
+}
+
+bool
+tree_evaluator::is_local_variable (const std::string& name) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->is_local_variable (name);
+}
+
+bool
+tree_evaluator::is_variable (const tree_expression *expr) const
+{
+  if (expr->is_identifier ())
+    {
+      const tree_identifier *id
+        = dynamic_cast<const tree_identifier *> (expr);
+
+      if (id->is_black_hole ())
+        return false;
+
+      return is_variable (id->symbol ());
+    }
+
+  return false;
+}
+
+bool
+tree_evaluator::is_defined (const tree_expression *expr) const
+{
+  if (expr->is_identifier ())
+    {
+      const tree_identifier *id
+        = dynamic_cast<const tree_identifier *> (expr);
+
+      return is_defined (id->symbol ());
+    }
+
+  return false;
+}
+
+bool
+tree_evaluator::is_variable (const symbol_record& sym) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->is_variable (sym);
+}
+
+bool
+tree_evaluator::is_defined (const symbol_record& sym) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->is_defined (sym);
+}
+
+bool tree_evaluator::is_global (const std::string& name) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->is_global (name);
+}
+
+octave_value
+tree_evaluator::varval (const symbol_record& sym) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->varval (sym);
+}
+
+octave_value
+tree_evaluator::varval (const std::string& name) const
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->varval (name);
+}
+
+void tree_evaluator::install_variable (const std::string& name,
+                                       const octave_value& value,
+                                       bool global)
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  return frame->install_variable (name, value, global);
+}
+
+octave_value
+tree_evaluator::global_varval (const std::string& name) const
+{
+  return m_call_stack.global_varval (name);
+}
+
+octave_value&
+tree_evaluator::global_varref (const std::string& name)
+{
+  return m_call_stack.global_varref (name);
+}
+
+void
+tree_evaluator::global_assign (const std::string& name,
+                               const octave_value& val)
+{
+  m_call_stack.global_varref (name) = val;
+}
+
+octave_value
+tree_evaluator::top_level_varval (const std::string& name) const
+{
+  return m_call_stack.get_top_level_value (name);
+}
+
+void
+tree_evaluator::top_level_assign (const std::string& name,
+                                  const octave_value& val)
+{
+  m_call_stack.set_top_level_value (name, val);
+}
+
+void
+tree_evaluator::assign (const std::string& name, const octave_value& val)
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  frame->assign (name, val);
+}
+
+void
+tree_evaluator::assignin (const std::string& context,
+                          const std::string& name, const octave_value& val)
+{
+  // FIXME: Can this be done without an unwind-protect frame, simply
+  // by getting a reference to the caller or base stack frame and
+  // calling assign on that?
+
+  unwind_action act ([=] (std::size_t frm)
+                     {
+                       m_call_stack.restore_frame (frm);
+                     }, m_call_stack.current_frame ());
+
+  if (context == "caller")
+    m_call_stack.goto_caller_frame ();
+  else if (context == "base")
+    m_call_stack.goto_base_frame ();
+  else
+    error (R"(assignin: CONTEXT must be "caller" or "base")");
+
+  if (valid_identifier (name))
+    {
+      // Put the check here so that we don't slow down assignments
+      // generally.  Any that go through Octave's parser should have
+      // already been checked.
+
+      if (iskeyword (name))
+        error ("assignin: invalid assignment to keyword '%s'",
+               name.c_str ());
+
+      assign (name, val);
+    }
+  else
+    error ("assignin: invalid variable name '%s'", name.c_str ());
+}
+
+void
+tree_evaluator::source_file (const std::string& file_name,
+                             const std::string& context,
+                             bool verbose, bool require_file)
+{
+  // Map from absolute name of script file to recursion level.  We
+  // use a map instead of simply placing a limit on recursion in the
+  // source_file function so that two mutually recursive scripts
+  // written as
+  //
+  //   foo1.m:
+  //   ------
+  //   foo2
+  //
+  //   foo2.m:
+  //   ------
+  //   foo1
+  //
+  // and called with
+  //
+  //   foo1
+  //
+  // (for example) will behave the same if they are written as
+  //
+  //   foo1.m:
+  //   ------
+  //   source ("foo2.m")
+  //
+  //   foo2.m:
+  //   ------
+  //   source ("foo1.m")
+  //
+  // and called with
+  //
+  //   source ("foo1.m")
+  //
+  // (for example).
+
+  static std::map<std::string, int> source_call_depth;
+
+  std::string file_full_name
+    = sys::file_ops::tilde_expand (file_name);
+
+  std::size_t pos
+    = file_full_name.find_last_of (sys::file_ops::dir_sep_str ());
+
+  std::string dir_name = file_full_name.substr (0, pos);
+
+  file_full_name = sys::env::make_absolute (file_full_name);
+
+  unwind_protect frame;
+
+  if (source_call_depth.find (file_full_name) == source_call_depth.end ())
+    source_call_depth[file_full_name] = -1;
+
+  frame.protect_var (source_call_depth[file_full_name]);
+
+  source_call_depth[file_full_name]++;
+
+  if (source_call_depth[file_full_name] >= max_recursion_depth ())
+    error ("max_recursion_depth exceeded");
+
+  if (! context.empty ())
+    {
+      frame.add (&call_stack::restore_frame, &m_call_stack,
+                 m_call_stack.current_frame ());
+
+      if (context == "caller")
+        m_call_stack.goto_caller_frame ();
+      else if (context == "base")
+        m_call_stack.goto_base_frame ();
+      else
+        error (R"(source: CONTEXT must be "caller" or "base")");
+    }
+
+  // Find symbol name that would be in symbol_table, if it were loaded.
+  std::size_t dir_end
+    = file_name.find_last_of (sys::file_ops::dir_sep_chars ());
+  dir_end = (dir_end == std::string::npos) ? 0 : dir_end + 1;
+
+  std::size_t extension = file_name.find_last_of ('.');
+  if (extension == std::string::npos)
+    extension = file_name.length ();
+
+  std::string symbol = file_name.substr (dir_end, extension - dir_end);
+  std::string full_name = sys::canonicalize_file_name (file_name);
+
+  // Check if this file is already loaded (or in the path)
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+  octave_value ov_code = symtab.fcn_table_find (symbol);
+
+  // For compatibility with Matlab, accept both scripts and
+  // functions.
+
+  if (ov_code.is_user_code ())
+    {
+      octave_user_code *code = ov_code.user_code_value ();
+
+      if (! code
+          || (sys::canonicalize_file_name (code->fcn_file_name ())
+              != full_name))
+        {
+          // Wrong file, so load it below.
+          ov_code = octave_value ();
+        }
+    }
+  else
+    {
+      // Not a script, so load it below.
+      ov_code = octave_value ();
+    }
+
+  // If no symbol of this name, or the symbol is for a different
+  // file, load.
+
+  if (ov_code.is_undefined ())
+    {
+      try
+        {
+          ov_code = parse_fcn_file (m_interpreter, file_full_name,
+                                    file_name, dir_name, "", "",
+                                    require_file, true, false, false);
+        }
+      catch (execution_exception& ee)
+        {
+          error (ee, "source: error sourcing file '%s'",
+                 file_full_name.c_str ());
+        }
+    }
+
+  // Return or error if we don't have a valid script or function.
+
+  if (ov_code.is_undefined ())
+    return;
+
+  if (! ov_code.is_user_code ())
+    error ("source: %s is not a script", full_name.c_str ());
+
+  if (verbose)
+    {
+      octave_stdout << "executing commands from " << full_name << " ... ";
+      octave_stdout.flush ();
+    }
+
+  octave_user_code *code = ov_code.user_code_value ();
+
+  code->call (*this, 0, octave_value_list ());
+
+  if (verbose)
+    octave_stdout << "done." << std::endl;
+}
+
+void
+tree_evaluator::set_auto_fcn_var (stack_frame::auto_var_type avt,
+                                  const octave_value& val)
+{
+  m_call_stack.set_auto_fcn_var (avt, val);
+}
+
+octave_value
+tree_evaluator::get_auto_fcn_var (stack_frame::auto_var_type avt) const
+{
+  return m_call_stack.get_auto_fcn_var (avt);
+}
+
+void
+tree_evaluator::define_parameter_list_from_arg_vector
+  (tree_parameter_list *param_list, const octave_value_list& args)
+{
+  if (! param_list || param_list->varargs_only ())
+    return;
+
+  int i = -1;
+
+  for (tree_decl_elt *elt : *param_list)
+    {
+      i++;
+
+      octave_lvalue ref = elt->lvalue (*this);
+
+      if (i < args.length ())
+        {
+          if (args(i).is_defined () && args(i).is_magic_colon ())
+            {
+              if (! eval_decl_elt (elt))
+                error ("no default value for argument %d", i+1);
+            }
+          else
+            ref.define (args(i));
+        }
+      else
+        eval_decl_elt (elt);
+    }
+}
+
+void
+tree_evaluator::undefine_parameter_list (tree_parameter_list *param_list)
+{
+  for (tree_decl_elt *elt : *param_list)
+    {
+      octave_lvalue ref = elt->lvalue (*this);
+
+      ref.assign (octave_value::op_asn_eq, octave_value ());
+    }
+}
 
 // END is documented in op-kw-docs.
 DEFMETHOD (end, interp, args, ,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn {} {} end
 Last element of an array or the end of any @code{for}, @code{parfor},
 @code{if}, @code{do}, @code{while}, @code{function}, @code{switch},
@@ -2244,19 +2244,19 @@
 @group
 @var{x} = [ 1 2 3; 4 5 6 ];
 @var{x}(1,end)
-   @result{} 3
+ @result{} 3
 @var{x}(end,1)
-   @result{} 4
+ @result{} 4
 @var{x}(end,end)
-   @result{} 6
+ @result{} 6
 @end group
 @end example
 @seealso{for, parfor, if, do, while, function, switch, try, unwind_protect}
 @end deftypefn */)
 {
-  tree_evaluator& tw = interp.get_evaluator ();
-
-  return tw.evaluate_end_expression (args);
+tree_evaluator& tw = interp.get_evaluator ();
+
+return tw.evaluate_end_expression (args);
 }
 
 /*
@@ -2271,2991 +2271,2991 @@
 %! assert (x(minus (minus (end, 1), 1)), 8);
 */
 
-  octave_value_list
-  tree_evaluator::convert_to_const_vector (tree_argument_list *args)
-  {
-    std::list<octave_value> arg_vals;
-
-    for (auto elt : *args)
+octave_value_list
+tree_evaluator::convert_to_const_vector (tree_argument_list *args)
+{
+  std::list<octave_value> arg_vals;
+
+  for (auto elt : *args)
+    {
+      // FIXME: is it possible for elt to be invalid?
+
+      if (! elt)
+        break;
+
+      octave_value tmp = elt->evaluate (*this);
+
+      if (tmp.is_cs_list ())
+        {
+          octave_value_list tmp_ovl = tmp.list_value ();
+
+          for (octave_idx_type i = 0; i < tmp_ovl.length (); i++)
+            arg_vals.push_back (tmp_ovl(i));
+        }
+      else if (tmp.is_defined ())
+        arg_vals.push_back (tmp);
+    }
+
+  return octave_value_list (arg_vals);
+}
+
+octave_value_list
+tree_evaluator::convert_return_list_to_const_vector
+  (tree_parameter_list *ret_list, int nargout, const Matrix& ignored_outputs,
+   const Cell& varargout)
+{
+  octave_idx_type vlen = varargout.numel ();
+  int len = ret_list->length ();
+
+  // Special case.  Will do a shallow copy.
+  if (len == 0)
+    return varargout;
+  else
+    {
+      int i = 0;
+      int k = 0;
+      int num_ignored = ignored_outputs.numel ();
+      int ignored = num_ignored > 0 ? ignored_outputs(k) - 1 : -1;
+
+      if (nargout <= len)
+        {
+          int nout = nargout > 0 ? nargout : 1;
+          octave_value_list retval (nout);
+
+          for (tree_decl_elt *elt : *ret_list)
+            {
+              if (nargout == 0 && ! is_defined (elt->ident ()))
+                break;
+
+              if (ignored >= 0 && i == ignored)
+                {
+                  i++;
+                  k++;
+                  ignored = k < num_ignored ? ignored_outputs(k) - 1 : -1;
+                }
+              else
+                retval(i++) = evaluate (elt);
+
+              if (i == nout)
+                break;
+            }
+
+          return retval;
+        }
+      else
+        {
+          octave_value_list retval (len + vlen);
+
+          for (tree_decl_elt *elt : *ret_list)
+            {
+              if (ignored >= 0 && i == ignored)
+                {
+                  i++;
+                  k++;
+                  ignored = k < num_ignored ? ignored_outputs(k) - 1 : -1;
+                }
+              else
+                retval(i++) = evaluate (elt);
+            }
+
+          for (octave_idx_type j = 0; j < vlen; j++)
+            retval(i++) = varargout(j);
+
+          return retval;
+        }
+    }
+}
+
+bool
+tree_evaluator::eval_decl_elt (tree_decl_elt *elt)
+{
+  bool retval = false;
+
+  tree_identifier *id = elt->ident ();
+  tree_expression *expr = elt->expression ();
+
+  if (id && expr)
+    {
+      octave_lvalue ult = id->lvalue (*this);
+
+      octave_value init_val = expr->evaluate (*this);
+
+      ult.assign (octave_value::op_asn_eq, init_val);
+
+      retval = true;
+    }
+
+  return retval;
+}
+
+bool
+tree_evaluator::switch_case_label_matches (tree_switch_case *expr,
+                                           const octave_value& val)
+{
+  tree_expression *label = expr->case_label ();
+
+  octave_value label_value = label->evaluate (*this);
+
+  if (label_value.is_defined ())
+    {
+      if (label_value.iscell ())
+        {
+          Cell cell (label_value.cell_value ());
+
+          for (octave_idx_type i = 0; i < cell.rows (); i++)
+            {
+              for (octave_idx_type j = 0; j < cell.columns (); j++)
+                {
+                  bool match = val.is_equal (cell(i,j));
+
+                  if (match)
+                    return true;
+                }
+            }
+        }
+      else
+        return val.is_equal (label_value);
+    }
+
+  return false;
+}
+
+void tree_evaluator::push_stack_frame (const symbol_scope& scope)
+{
+  m_call_stack.push (scope);
+}
+
+void tree_evaluator::push_stack_frame (octave_user_function *fcn,
+                                       const std::shared_ptr<stack_frame>& closure_frames)
+{
+  m_call_stack.push (fcn, closure_frames);
+}
+
+void tree_evaluator::push_stack_frame (octave_user_function *fcn,
+                                       const stack_frame::local_vars_map& local_vars,
+                                       const std::shared_ptr<stack_frame>& closure_frames)
+{
+  m_call_stack.push (fcn, local_vars, closure_frames);
+}
+
+void tree_evaluator::push_stack_frame (octave_user_script *script)
+{
+  m_call_stack.push (script);
+}
+
+void tree_evaluator::push_stack_frame (octave_function *fcn)
+{
+  m_call_stack.push (fcn);
+}
+
+void tree_evaluator::pop_stack_frame ()
+{
+  m_call_stack.pop ();
+}
+
+int tree_evaluator::current_line () const
+{
+  return m_call_stack.current_line ();
+}
+
+int tree_evaluator::current_column () const
+{
+  return m_call_stack.current_column ();
+}
+
+int tree_evaluator::debug_user_code_line () const
+{
+  return m_call_stack.debug_user_code_line ();
+}
+
+int tree_evaluator::debug_user_code_column () const
+{
+  return m_call_stack.debug_user_code_column ();
+}
+
+void tree_evaluator::debug_where (std::ostream& os) const
+{
+  std::shared_ptr<stack_frame> frm = m_call_stack.current_user_frame ();
+
+  frm->display_stopped_in_message (os);
+}
+
+octave_user_code * tree_evaluator::current_user_code () const
+{
+  return m_call_stack.current_user_code ();
+}
+
+unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame ()
+{
+  return m_call_stack.curr_fcn_unwind_protect_frame ();
+}
+
+octave_user_code * tree_evaluator::debug_user_code () const
+{
+  return m_call_stack.debug_user_code ();
+}
+
+octave_function * tree_evaluator::current_function (bool skip_first) const
+{
+  return m_call_stack.current_function (skip_first);
+}
+
+octave_function * tree_evaluator::caller_function () const
+{
+  return m_call_stack.current_function (true);
+}
+
+bool tree_evaluator::goto_frame (std::size_t n, bool verbose)
+{
+  return m_call_stack.goto_frame (n, verbose);
+}
+
+void tree_evaluator::goto_caller_frame ()
+{
+  m_call_stack.goto_caller_frame ();
+}
+
+void tree_evaluator::goto_base_frame ()
+{
+  m_call_stack.goto_base_frame ();
+}
+
+void tree_evaluator::restore_frame (std::size_t n)
+{
+  return m_call_stack.restore_frame (n);
+}
+
+std::string tree_evaluator::get_dispatch_class () const
+{
+  return m_call_stack.get_dispatch_class ();
+}
+
+void tree_evaluator::set_dispatch_class (const std::string& class_name)
+{
+  m_call_stack.set_dispatch_class (class_name);
+}
+
+bool
+tree_evaluator::is_class_method_executing (std::string& dclass) const
+{
+  return m_call_stack.is_class_method_executing (dclass);
+}
+
+bool
+tree_evaluator::is_class_constructor_executing (std::string& dclass) const
+{
+  return m_call_stack.is_class_constructor_executing (dclass);
+}
+
+std::list<std::shared_ptr<stack_frame>>
+tree_evaluator::backtrace_frames (octave_idx_type& curr_user_frame) const
+{
+  return m_call_stack.backtrace_frames (curr_user_frame);
+}
+
+std::list<std::shared_ptr<stack_frame>>
+tree_evaluator::backtrace_frames () const
+{
+  return m_call_stack.backtrace_frames ();
+}
+
+std::list<frame_info>
+tree_evaluator::backtrace_info (octave_idx_type& curr_user_frame,
+                                bool print_subfn) const
+{
+  return m_call_stack.backtrace_info (curr_user_frame, print_subfn);
+}
+
+std::list<frame_info> tree_evaluator::backtrace_info () const
+{
+  return m_call_stack.backtrace_info ();
+}
+
+octave_map
+tree_evaluator::backtrace (octave_idx_type& curr_user_frame,
+                           bool print_subfn) const
+{
+  return m_call_stack.backtrace (curr_user_frame, print_subfn);
+}
+
+octave_map tree_evaluator::backtrace () const
+{
+  return m_call_stack.backtrace ();
+}
+
+octave_map tree_evaluator::empty_backtrace () const
+{
+  return m_call_stack.empty_backtrace ();
+}
+
+std::string tree_evaluator::backtrace_message () const
+{
+  std::list<frame_info> frames = backtrace_info ();
+
+  std::ostringstream buf;
+
+  for (const auto& frm : frames)
+    {
+      buf << "    " << frm.fcn_name ();
+
+      int line = frm.line ();
+
+      if (line > 0)
+        {
+          buf << " at line " << line;
+
+          int column = frm.column ();
+
+          if (column > 0)
+            buf << " column " << column;
+
+          buf << "\n";
+        }
+    }
+
+  return buf.str ();
+}
+
+void tree_evaluator::push_dummy_scope (const std::string& name)
+{
+  symbol_scope dummy_scope (name + "$dummy");
+
+  m_call_stack.push (dummy_scope);
+}
+
+void tree_evaluator::pop_scope ()
+{
+  m_call_stack.pop ();
+}
+
+symbol_scope tree_evaluator::get_top_scope () const
+{
+  return m_call_stack.top_scope ();
+}
+
+symbol_scope tree_evaluator::get_current_scope () const
+{
+  return m_call_stack.current_scope ();
+}
+
+void tree_evaluator::mlock (bool skip_first) const
+{
+  octave_function *fcn = m_call_stack.current_function (skip_first);
+
+  if (! fcn)
+    error ("mlock: invalid use outside a function");
+
+  if (fcn->is_builtin_function ())
+    {
+      warning ("mlock: locking built-in function has no effect");
+      return;
+    }
+
+  fcn->lock ();
+}
+
+void tree_evaluator::munlock (bool skip_first) const
+{
+  octave_function *fcn = m_call_stack.current_function (skip_first);
+
+  if (! fcn)
+    error ("munlock: invalid use outside a function");
+
+  if (fcn->is_builtin_function ())
+    {
+      warning ("munlock: unlocking built-in function has no effect");
+      return;
+    }
+
+  fcn->unlock ();
+}
+
+bool tree_evaluator::mislocked (bool skip_first) const
+{
+  octave_function *fcn = m_call_stack.current_function (skip_first);
+
+  if (! fcn)
+    error ("mislocked: invalid use outside a function");
+
+  return fcn->islocked ();
+}
+
+octave_value
+tree_evaluator::max_stack_depth (const octave_value_list& args, int nargout)
+{
+  return m_call_stack.max_stack_depth (args, nargout);
+}
+
+void tree_evaluator::display_call_stack () const
+{
+  m_call_stack.display ();
+}
+
+octave_value tree_evaluator::find (const std::string& name)
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  octave_value val = frame->varval (name);
+
+  if (val.is_defined ())
+    return val;
+
+  // Subfunction.  I think it only makes sense to check for
+  // subfunctions if we are currently executing a function defined
+  // from a .m file.
+
+  octave_value fcn = frame->find_subfunction (name);
+
+  if (fcn.is_defined ())
+    return fcn;
+
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  return symtab.fcn_table_find (name, ovl ());
+}
+
+void tree_evaluator::clear_objects ()
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  frame->clear_objects ();
+}
+
+void tree_evaluator::clear_variable (const std::string& name)
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  frame->clear_variable (name);
+}
+
+void tree_evaluator::clear_variable_pattern (const std::string& pattern)
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  frame->clear_variable_pattern (pattern);
+}
+
+void tree_evaluator::clear_variable_regexp (const std::string& pattern)
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  frame->clear_variable_regexp (pattern);
+}
+
+void tree_evaluator::clear_variables ()
+{
+  std::shared_ptr<stack_frame> frame
+    = m_call_stack.get_current_stack_frame ();
+
+  frame->clear_variables ();
+}
+
+void tree_evaluator::clear_global_variable (const std::string& name)
+{
+  m_call_stack.clear_global_variable (name);
+}
+
+void
+tree_evaluator::clear_global_variable_pattern (const std::string& pattern)
+{
+  m_call_stack.clear_global_variable_pattern (pattern);
+}
+
+void tree_evaluator::clear_global_variable_regexp(const std::string& pattern)
+{
+  m_call_stack.clear_global_variable_regexp (pattern);
+}
+
+void tree_evaluator::clear_global_variables ()
+{
+  m_call_stack.clear_global_variables ();
+}
+
+void tree_evaluator::clear_all (bool force)
+{
+  // FIXME: should this also clear objects?
+
+  clear_variables ();
+  clear_global_variables ();
+
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  symtab.clear_functions (force);
+}
+
+void tree_evaluator::clear_symbol (const std::string& name)
+{
+  // FIXME: are we supposed to do both here?
+
+  clear_variable (name);
+
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  symtab.clear_function (name);
+}
+
+void tree_evaluator::clear_symbol_pattern (const std::string& pattern)
+{
+  // FIXME: are we supposed to do both here?
+
+  clear_variable_pattern (pattern);
+
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  symtab.clear_function_pattern (pattern);
+}
+
+void tree_evaluator::clear_symbol_regexp (const std::string& pattern)
+{
+  // FIXME: are we supposed to do both here?
+
+  clear_variable_regexp (pattern);
+
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  symtab.clear_function_regexp (pattern);
+}
+
+std::list<std::string> tree_evaluator::global_variable_names () const
+{
+  return m_call_stack.global_variable_names ();
+}
+
+std::list<std::string> tree_evaluator::top_level_variable_names () const
+{
+  return m_call_stack.top_level_variable_names ();
+}
+
+std::list<std::string> tree_evaluator::variable_names () const
+{
+  return m_call_stack.variable_names ();
+}
+
+// Return a pointer to the user-defined function FNAME.  If FNAME is empty,
+// search backward for the first user-defined function in the
+// current call stack.
+
+octave_user_code *
+tree_evaluator::get_user_code (const std::string& fname,
+                               const std::string& class_name)
+{
+  octave_user_code *user_code = nullptr;
+
+  if (fname.empty ())
+    user_code = m_call_stack.debug_user_code ();
+  else
+    {
+      std::string name = fname;
+
+      if (sys::file_ops::dir_sep_char () != '/' && name[0] == '@')
+        {
+          auto beg = name.begin () + 2;  // never have @/method
+          auto end = name.end () - 1;    // never have trailing '/'
+          std::replace (beg, end, '/', sys::file_ops::dir_sep_char ());
+        }
+
+      std::size_t name_len = name.length ();
+
+      if (name_len > 2 && name.substr (name_len-2) == ".m")
+        name = name.substr (0, name_len-2);
+
+      if (name.empty ())
+        return nullptr;
+
+      symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+      octave_value fcn;
+      std::size_t p2 = std::string::npos;
+
+      if (name[0] == '@')
+        {
+          std::size_t p1 = name.find (sys::file_ops::dir_sep_char (), 1);
+
+          if (p1 == std::string::npos)
+            return nullptr;
+
+          std::string dispatch_type = name.substr (1, p1-1);
+
+          p2 = name.find ('>', p1);
+
+          std::string method = name.substr (p1+1, p2-1);
+
+          fcn = symtab.find_method (method, dispatch_type);
+        }
+      else if (! class_name.empty ())
+        {
+          cdef_manager& cdm = m_interpreter.get_cdef_manager ();
+
+          fcn = cdm.find_method (class_name, name);
+
+          // If there is no classdef method, then try legacy classes.
+          if (fcn.is_undefined ())
+            fcn = symtab.find_method (name, class_name);
+        }
+      else
+        {
+          p2 = name.find ('>');
+
+          std::string main_fcn = name.substr (0, p2);
+
+          fcn = symtab.find_function (main_fcn);
+        }
+
+      // List of function names sub1>sub2>...
+      std::string subfuns;
+
+      if (p2 != std::string::npos)
+        subfuns = name.substr (p2+1);
+
+      if (fcn.is_defined () && fcn.is_user_code ())
+        user_code = fcn.user_code_value ();
+
+      if (! user_code || subfuns.empty ())
+        return user_code;
+
+      fcn = user_code->find_subfunction (subfuns);
+
+      if (fcn.is_undefined ())
+        return nullptr;
+
+      user_code = fcn.user_code_value ();
+    }
+
+  return user_code;
+}
+
+std::string
+tree_evaluator::current_function_name (bool skip_first) const
+{
+  octave_function *curfcn = m_call_stack.current_function (skip_first);
+
+  if (curfcn)
+    return curfcn->name ();
+
+  return "";
+}
+
+bool
+tree_evaluator::in_user_code () const
+{
+  return m_call_stack.current_user_code () != nullptr;
+}
+
+void
+tree_evaluator::visit_decl_command (tree_decl_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  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)
+    init_list->accept (*this);
+}
+
+void
+tree_evaluator::visit_decl_elt (tree_decl_elt& elt)
+{
+  tree_identifier *id = elt.ident ();
+
+  if (id)
+    {
+      if (elt.is_global ())
+        m_call_stack.make_global (id->symbol ());
+      else if (elt.is_persistent ())
+        m_call_stack.make_persistent (id->symbol ());
+      else
+        error ("declaration list element not global or persistent");
+
+      octave_lvalue ult = id->lvalue (*this);
+
+      if (ult.is_undefined ())
+        {
+          tree_expression *expr = elt.expression ();
+
+          octave_value init_val;
+
+          if (expr)
+            init_val = expr->evaluate (*this);
+          else
+            init_val = Matrix ();
+
+          ult.assign (octave_value::op_asn_eq, init_val);
+        }
+    }
+}
+
+template <typename T>
+void
+tree_evaluator::execute_range_loop (const range<T>& rng, int line,
+                                    octave_lvalue& ult,
+                                    tree_statement_list *loop_body)
+{
+  octave_idx_type steps = rng.numel ();
+
+  if (math::isinf (rng.limit ()))
+    warning_with_id ("Octave:infinite-loop",
+                     "FOR loop limit is infinite, will stop after %"
+                     OCTAVE_IDX_TYPE_FORMAT " steps", steps);
+
+  for (octave_idx_type i = 0; i < steps; i++)
+    {
+      if (m_echo_state)
+        m_echo_file_pos = line;
+
+      octave_value val (rng.elem (i));
+
+      ult.assign (octave_value::op_asn_eq, val);
+
+      if (loop_body)
+        loop_body->accept (*this);
+
+      if (quit_loop_now ())
+        break;
+    }
+}
+
+void
+tree_evaluator::visit_simple_for_command (tree_simple_for_command& cmd)
+{
+  int line = cmd.line ();
+  if (line < 0)
+    line = 1;
+
+  if (m_echo_state)
+    {
+      echo_code (line);
+      line++;
+    }
+
+  if (m_debug_mode)
+    do_breakpoint (cmd.is_active_breakpoint (*this));
+
+  // FIXME: need to handle PARFOR loops here using cmd.in_parallel ()
+  // and cmd.maxproc_expr ();
+
+  unwind_protect_var<bool> upv (m_in_loop_command, true);
+
+  tree_expression *expr = cmd.control_expr ();
+
+  octave_value rhs = expr->evaluate (*this);
+
+  if (rhs.is_undefined ())
+    return;
+
+  tree_expression *lhs = cmd.left_hand_side ();
+
+  octave_lvalue ult = lhs->lvalue (*this);
+
+  tree_statement_list *loop_body = cmd.body ();
+
+  if (rhs.is_range ())
+    {
+      // FIXME: is there a better way to dispatch here?
+
+      if (rhs.is_double_type ())
+        {
+          execute_range_loop (rhs.range_value (), line, ult, loop_body);
+          return;
+        }
+
+      // For now, disable all but range<double>.
+
+#if 0
+      if (rhs.is_int64_type ())
+        {
+          execute_range_loop (rhs.int64_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_uint64_type ())
+        {
+          execute_range_loop (rhs.uint64_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_int32_type ())
+        {
+          execute_range_loop (rhs.int32_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_uint32_type ())
+        {
+          execute_range_loop (rhs.uint32_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_int16_type ())
+        {
+          execute_range_loop (rhs.int16_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_uint16_type ())
+        {
+          execute_range_loop (rhs.uint16_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_int8_type ())
+        {
+          execute_range_loop (rhs.int8_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_uint8_type ())
+        {
+          execute_range_loop (rhs.uint8_range_value (), line, ult, loop_body);
+          return;
+        }
+
+      if (rhs.is_single_type ())
+        {
+          execute_range_loop (rhs.float_range_value (), line, ult, loop_body);
+          return;
+        }
+#endif
+    }
+
+  if (rhs.is_scalar_type ())
+    {
+      if (m_echo_state)
+        m_echo_file_pos = line;
+
+      ult.assign (octave_value::op_asn_eq, rhs);
+
+      if (loop_body)
+        loop_body->accept (*this);
+
+      // Maybe decrement break and continue states.
+      quit_loop_now ();
+
+      return;
+    }
+
+  // Also handle any range types not explicitly handled above, though
+  // not as efficiently as the specialized code above.
+
+  if (rhs.is_range () || rhs.is_matrix_type () || rhs.iscell ()
+      || rhs.is_string () || rhs.isstruct ())
+    {
+      // A matrix or cell is reshaped to 2 dimensions and iterated by
+      // columns.
+
+      dim_vector dv = rhs.dims ().redim (2);
+
+      octave_idx_type nrows = dv(0);
+      octave_idx_type steps = dv(1);
+
+      octave_value arg = rhs;
+      if (rhs.ndims () > 2)
+        arg = arg.reshape (dv);
+
+      if (nrows > 0 && steps > 0)
+        {
+          octave_value_list idx;
+          octave_idx_type iidx;
+
+          // for row vectors, use single index to speed things up.
+          if (nrows == 1)
+            {
+              idx.resize (1);
+              iidx = 0;
+            }
+          else
+            {
+              idx.resize (2);
+              idx(0) = octave_value::magic_colon_t;
+              iidx = 1;
+            }
+
+          for (octave_idx_type i = 1; i <= steps; i++)
+            {
+              if (m_echo_state)
+                m_echo_file_pos = line;
+
+              // index_op expects one-based indices.
+              idx(iidx) = i;
+              octave_value val = arg.index_op (idx);
+
+              ult.assign (octave_value::op_asn_eq, val);
+
+              if (loop_body)
+                loop_body->accept (*this);
+
+              if (quit_loop_now ())
+                break;
+            }
+        }
+      else
+        {
+          // Handle empty cases, while still assigning to loop var.
+          ult.assign (octave_value::op_asn_eq, arg);
+        }
+
+      return;
+    }
+
+  error ("invalid type in for loop expression near line %d, column %d",
+         cmd.line (), cmd.column ());
+}
+
+void
+tree_evaluator::visit_complex_for_command (tree_complex_for_command& cmd)
+{
+  int line = cmd.line ();
+  if (line < 0)
+    line = 1;
+
+  if (m_echo_state)
+    {
+      echo_code (line);
+      line++;
+    }
+
+  if (m_debug_mode)
+    do_breakpoint (cmd.is_active_breakpoint (*this));
+
+  unwind_protect_var<bool> upv (m_in_loop_command, true);
+
+  tree_expression *expr = cmd.control_expr ();
+
+  octave_value rhs = expr->evaluate (*this);
+
+  if (rhs.is_undefined ())
+    return;
+
+  if (! rhs.isstruct ())
+    error ("in statement 'for [X, Y] = VAL', VAL must be a structure");
+
+  // Cycle through structure elements.  First element of id_list
+  // is set to value and the second is set to the name of the
+  // structure element.
+
+  tree_argument_list *lhs = cmd.left_hand_side ();
+
+  auto p = lhs->begin ();
+
+  tree_expression *elt = *p++;
+
+  octave_lvalue val_ref = elt->lvalue (*this);
+
+  elt = *p;
+
+  octave_lvalue key_ref = elt->lvalue (*this);
+
+  const octave_map tmp_val = rhs.map_value ();
+
+  tree_statement_list *loop_body = cmd.body ();
+
+  string_vector keys = tmp_val.keys ();
+
+  octave_idx_type nel = keys.numel ();
+
+  for (octave_idx_type i = 0; i < nel; i++)
+    {
+      if (m_echo_state)
+        m_echo_file_pos = line;
+
+      std::string key = keys[i];
+
+      const Cell val_lst = tmp_val.contents (key);
+
+      octave_idx_type n = val_lst.numel ();
+
+      octave_value val = (n == 1) ? val_lst(0) : octave_value (val_lst);
+
+      val_ref.assign (octave_value::op_asn_eq, val);
+      key_ref.assign (octave_value::op_asn_eq, key);
+
+      if (loop_body)
+        loop_body->accept (*this);
+
+      if (quit_loop_now ())
+        break;
+    }
+}
+
+void tree_evaluator::visit_spmd_command (tree_spmd_command& cmd)
+{
+  // For now, we just execute the commands serially.
+
+  tree_statement_list *body = cmd.body ();
+
+  if (body)
+    body->accept (*this);
+}
+
+octave_value
+tree_evaluator::evaluate_anon_fcn_handle (tree_anon_fcn_handle& afh)
+{
+  // FIXME: should CMD_LIST be limited to a single expression?
+  // I think that is what Matlab does.
+
+  symbol_scope new_scope;
+  symbol_scope scope = afh.scope ();
+  if (scope)
+    new_scope = scope.dup ();
+
+  tree_parameter_list *param_list = afh.parameter_list ();
+  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);
+
+  tree_expression *expr = afh.expression ();
+  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 (afh);
+
+  std::set<std::string> free_vars = anon_fcn_ctx.free_variables ();
+
+  stack_frame::local_vars_map local_vars;
+
+  std::shared_ptr<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_vars[name] = val;
+    }
+
+  octave_user_function *af
+    = new octave_user_function (new_scope, param_list_dup, ret_list,
+                                stmt_list);
+
+  octave_function *curr_fcn = m_call_stack.current_function ();
+
+  bool is_nested = false;
+
+  if (curr_fcn)
+    {
+      // FIXME: maybe it would be better to just stash curr_fcn
+      // instead of individual bits of info about it?
+
+      // An anonymous function defined inside another nested function
+      // or parent of a nested function also behaves like a nested
+      // function.
+
+      if (curr_fcn->is_parent_function () || curr_fcn->is_nested_function ())
+        {
+          is_nested = true;
+          af->mark_as_nested_function ();
+          new_scope.set_nesting_depth (parent_scope.nesting_depth () + 1);
+        }
+
+      af->stash_dir_name (curr_fcn->dir_name ());
+
+      new_scope.cache_fcn_file_name (curr_fcn->fcn_file_name ());
+      new_scope.cache_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);
+
+  return (is_nested
+          ? octave_value (new octave_fcn_handle (ov_fcn, local_vars, frame))
+          : octave_value (new octave_fcn_handle (ov_fcn, local_vars)));
+}
+
+octave_value_list
+tree_evaluator::execute_builtin_function (octave_builtin& builtin_function,
+                                          int nargout,
+                                          const octave_value_list& args)
+{
+  octave_value_list retval;
+
+  if (args.has_magic_colon ())
+    error ("invalid use of colon in function argument list");
+
+  profiler::enter<octave_builtin> block (m_profiler, builtin_function);
+
+  octave_builtin::fcn fcn = builtin_function.function ();
+
+  if (fcn)
+    retval = (*fcn) (args, nargout);
+  else
+    {
+      octave_builtin::meth meth = builtin_function.method ();
+
+      retval = (*meth) (m_interpreter, args, nargout);
+    }
+
+  // Do not allow null values to be returned from functions.
+  // FIXME: perhaps true builtins should be allowed?
+
+  retval.make_storable_values ();
+
+  // Fix the case of a single undefined value.
+  // This happens when a compiled function uses
+  //
+  //   octave_value retval;
+  //
+  // instead of
+  //
+  //   octave_value_list retval;
+  //
+  // the idiom is very common, so we solve that here.
+
+  if (retval.length () == 1 && retval.xelem (0).is_undefined ())
+    retval.clear ();
+
+  return retval;
+}
+
+octave_value_list
+tree_evaluator::execute_mex_function (octave_mex_function& mex_function,
+                                      int nargout,
+                                      const octave_value_list& args)
+{
+  octave_value_list retval;
+
+  if (args.has_magic_colon ())
+    error ("invalid use of colon in function argument list");
+
+  profiler::enter<octave_mex_function> block (m_profiler, mex_function);
+
+  retval = call_mex (mex_function, args, nargout);
+
+  return retval;
+}
+
+octave_value_list
+tree_evaluator::execute_user_script (octave_user_script& user_script,
+                                     int nargout,
+                                     const octave_value_list& args)
+{
+  octave_value_list retval;
+
+  std::string file_name = user_script.fcn_file_name ();
+
+  if (args.length () != 0 || nargout != 0)
+    error ("invalid call to script %s", file_name.c_str ());
+
+  tree_statement_list *cmd_list = user_script.body ();
+
+  if (! cmd_list)
+    return retval;
+
+  // FIXME: Maybe this check belongs in the places where we push a new
+  // stack frame?  Or in the call_stack push method itself?
+
+  if (m_call_stack.size () >= static_cast<std::size_t> (m_max_recursion_depth))
+    error ("max_recursion_depth exceeded");
+
+  unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_SCRIPT);
+
+  profiler::enter<octave_user_script> block (m_profiler, user_script);
+
+  if (echo ())
+    push_echo_state (tree_evaluator::ECHO_SCRIPTS, file_name);
+
+  // FIXME: Should we be using tree_evaluator::eval here?
+
+  cmd_list->accept (*this);
+
+  if (m_returning)
+    m_returning = 0;
+
+  if (m_breaking)
+    m_breaking--;
+
+  return retval;
+}
+
+void
+tree_evaluator::visit_octave_user_script (octave_user_script&)
+{
+  // ??
+  panic_impossible ();
+}
+
+octave_value_list
+tree_evaluator::execute_user_function (octave_user_function& user_function,
+                                       int nargout,
+                                       const octave_value_list& xargs)
+{
+  octave_value_list retval;
+
+  // If this function is a classdef constructor, extract the first input
+  // argument, which must be the partially constructed object instance.
+
+  octave_value_list args (xargs);
+  octave_value_list ret_args;
+
+  int nargin = args.length ();
+
+  if (user_function.is_classdef_constructor ())
+    {
+      if (nargin > 0)
+        {
+          ret_args = args.slice (0, 1, true);
+          --nargin;
+          args = args.slice (1, nargin, true);
+        }
+      else
+        panic_impossible ();
+    }
+
+  // FIXME: this probably shouldn't be a double-precision matrix.
+  Matrix ignored_outputs = ignored_fcn_outputs ();
+
+  tree_parameter_list *param_list = user_function.parameter_list ();
+
+  bool takes_varargs = false;
+  int max_inputs = 0;
+
+  if (param_list)
+    {
+      takes_varargs = param_list->takes_varargs ();
+      max_inputs = param_list->length ();
+    }
+
+  if (! takes_varargs && nargin > max_inputs)
+    {
+      std::string name = user_function.name ();
+
+      if (name.empty ())
+        name = "@<anonymous>";
+
+      error_with_id ("Octave:invalid-fun-call",
+                     "%s: function called with too many inputs",
+                     name.c_str ());
+    }
+
+  define_parameter_list_from_arg_vector (param_list, args);
+
+  tree_parameter_list *ret_list = user_function.return_list ();
+
+  if (ret_list && ! ret_list->takes_varargs ())
+    {
+      int max_outputs = ret_list->length ();
+
+      if (nargout > max_outputs)
+        {
+          std::string name = user_function.name ();
+
+          error_with_id ("Octave:invalid-fun-call",
+                         "%s: function called with too many outputs",
+                         name.c_str ());
+        }
+    }
+
+  bind_auto_fcn_vars (xargs.name_tags (), ignored_outputs, nargin,
+                      nargout, user_function.takes_varargs (),
+                      user_function.all_va_args (args));
+
+  // For classdef constructor, pre-populate the output arguments
+  // with the pre-initialized object instance, extracted above.
+
+  if (user_function.is_classdef_constructor ())
+    {
+      if (! ret_list)
+        error ("%s: invalid classdef constructor, no output argument defined",
+               user_function.dispatch_class ().c_str ());
+
+      define_parameter_list_from_arg_vector (ret_list, ret_args);
+    }
+
+  // FIXME: Maybe this check belongs in the places where we push a
+  // new stack frame?  Or in the call_stack push method itself?
+
+  if (m_call_stack.size () >= static_cast<std::size_t> (m_max_recursion_depth))
+    error ("max_recursion_depth exceeded");
+
+  unwind_action act2 ([&user_function] () {
+                        user_function.restore_warning_states ();
+                      });
+
+  // Evaluate the commands that make up the function.
+
+  unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_FUNCTION);
+
+  tree_statement_list *cmd_list = user_function.body ();
+
+  if (cmd_list)
+    {
+      profiler::enter<octave_user_function>
+        block (m_profiler, user_function);
+
+      if (echo ())
+        push_echo_state (tree_evaluator::ECHO_FUNCTIONS,
+                         user_function.fcn_file_name ());
+
+      if (user_function.is_special_expr ())
+        {
+          panic_if (cmd_list->length () != 1);
+
+          tree_statement *stmt = cmd_list->front ();
+
+          tree_expression *expr = stmt->expression ();
+
+          if (expr)
+            {
+              m_call_stack.set_location (stmt->line (), stmt->column ());
+
+              retval = expr->evaluate_n (*this, nargout);
+            }
+        }
+      else
+        cmd_list->accept (*this);
+
+      if (m_returning)
+        m_returning = 0;
+
+      if (m_breaking)
+        m_breaking--;
+    }
+
+  // Copy return values out.
+
+  if (ret_list && ! user_function.is_special_expr ())
+    {
+      Cell varargout;
+
+      if (ret_list->takes_varargs ())
+        {
+          octave_value varargout_varval = varval ("varargout");
+
+          if (varargout_varval.is_defined ())
+            varargout = varargout_varval.xcell_value ("varargout must be a cell array object");
+        }
+
+      retval = convert_return_list_to_const_vector (ret_list, nargout,
+                                                    ignored_outputs,
+                                                    varargout);
+    }
+
+  return retval;
+}
+
+void
+tree_evaluator::visit_octave_user_function (octave_user_function&)
+{
+  // ??
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_octave_user_function_header (octave_user_function&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_octave_user_function_trailer (octave_user_function&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_function_def (tree_function_def& cmd)
+{
+  octave_value fcn = cmd.function ();
+
+  octave_function *f = fcn.function_value ();
+
+  if (f)
+    {
+      std::string nm = f->name ();
+
+      symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+      symtab.install_cmdline_function (nm, fcn);
+
+      // Make sure that any variable with the same name as the new
+      // function is cleared.
+
+      assign (nm);
+    }
+}
+
+void
+tree_evaluator::visit_identifier (tree_identifier&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_if_clause (tree_if_clause&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_if_command (tree_if_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      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)
+    lst->accept (*this);
+}
+
+void
+tree_evaluator::visit_if_command_list (tree_if_command_list& lst)
+{
+  for (tree_if_clause *tic : lst)
+    {
+      tree_expression *expr = tic->condition ();
+
+      if (! (in_debug_repl ()
+             && m_call_stack.current_frame () == m_debug_frame))
+        m_call_stack.set_location (tic->line (), tic->column ());
+
+      if (m_debug_mode && ! tic->is_else_clause ())
+        do_breakpoint (tic->is_active_breakpoint (*this));
+
+      if (tic->is_else_clause () || is_logically_true (expr, "if"))
+        {
+          tree_statement_list *stmt_lst = tic->commands ();
+
+          if (stmt_lst)
+            stmt_lst->accept (*this);
+
+          break;
+        }
+    }
+}
+
+void
+tree_evaluator::visit_index_expression (tree_index_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_matrix (tree_matrix&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_cell (tree_cell&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_multi_assignment (tree_multi_assignment&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_no_op_command (tree_no_op_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  if (m_debug_mode && cmd.is_end_of_fcn_or_script ())
+    do_breakpoint (cmd.is_active_breakpoint (*this), true);
+}
+
+void
+tree_evaluator::visit_constant (tree_constant&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_fcn_handle (tree_fcn_handle&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_parameter_list (tree_parameter_list&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_postfix_expression (tree_postfix_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_prefix_expression (tree_prefix_expression&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_return_command (tree_return_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  if (m_debug_mode)
+    do_breakpoint (cmd.is_active_breakpoint (*this));
+
+  // Act like dbcont.
+
+  if (in_debug_repl () && m_call_stack.current_frame () == m_debug_frame)
+    dbcont ();
+  else if (m_statement_context == SC_FUNCTION
+           || m_statement_context == SC_SCRIPT
+           || m_in_loop_command)
+    m_returning = 1;
+}
+
+void
+tree_evaluator::visit_simple_assignment (tree_simple_assignment&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_statement (tree_statement& stmt)
+{
+  tree_command *cmd = stmt.command ();
+  tree_expression *expr = stmt.expression ();
+
+  if (cmd || expr)
+    {
+      if (! (in_debug_repl ()
+             && m_call_stack.current_frame () == m_debug_frame))
+        m_call_stack.set_location (stmt.line (), stmt.column ());
+
+      try
+        {
+          if (cmd)
+            {
+              unwind_protect_var<const std::list<octave_lvalue> *>
+                upv (m_lvalue_list, nullptr);
+
+              cmd->accept (*this);
+            }
+          else
+            {
+              if (m_echo_state)
+                {
+                  int line = stmt.line ();
+                  if (line < 0)
+                    line = 1;
+                  echo_code (line);
+                  m_echo_file_pos = line + 1;
+                }
+
+              if (m_debug_mode)
+                do_breakpoint (expr->is_active_breakpoint (*this));
+
+              // FIXME: maybe all of this should be packaged in
+              // one virtual function that returns a flag saying whether
+              // or not the expression will take care of binding ans and
+              // printing the result.
+
+              // FIXME: it seems that we should just have to
+              // evaluate the expression and that should take care of
+              // everything, binding ans as necessary?
+
+              octave_value tmp_result = expr->evaluate (*this, 0);
+
+              if (tmp_result.is_defined ())
+                {
+                  bool do_bind_ans = false;
+
+                  if (expr->is_identifier ())
+                    do_bind_ans = ! is_variable (expr);
+                  else
+                    do_bind_ans = ! expr->is_assignment_expression ();
+
+                  if (do_bind_ans)
+                    bind_ans (tmp_result, expr->print_result ()
+                              && statement_printing_enabled ());
+                }
+            }
+        }
+      catch (const std::bad_alloc&)
+        {
+          // FIXME: We want to use error_with_id here so that give users
+          // control over this error message but error_with_id will
+          // require some memory allocations.  Is there anything we can
+          // do to make those more likely to succeed?
+
+          error_with_id ("Octave:bad-alloc",
+                         "out of memory or dimension too large for Octave's index type");
+        }
+      catch (const interrupt_exception&)
+        {
+          // If we are debugging, then continue with next statement.
+          // Otherwise, jump out of here.
+
+          if (m_debug_mode)
+            m_interpreter.recover_from_exception ();
+          else
+            throw;
+        }
+      catch (const execution_exception& ee)
+        {
+          error_system& es = m_interpreter.get_error_system ();
+
+          if ((m_interpreter.interactive ()
+               || application::forced_interactive ())
+              && ((es.debug_on_error ()
+                   && m_bp_table.debug_on_err (es.last_error_id ()))
+                  || (es.debug_on_caught ()
+                      && m_bp_table.debug_on_caught (es.last_error_id ())))
+              && in_user_code ())
+            {
+              es.save_exception (ee);
+              es.display_exception (ee);
+
+              enter_debugger ();
+
+              // It doesn't make sense to continue execution after an
+              // error occurs so force the debugger to quit all debug
+              // levels and return the the top prompt.
+
+              throw quit_debug_exception (true);
+            }
+          else
+            throw;
+        }
+    }
+}
+
+void
+tree_evaluator::visit_statement_list (tree_statement_list& lst)
+{
+  // FIXME: commented out along with else clause below.
+  // static octave_value_list empty_list;
+
+  auto p = lst.begin ();
+
+  if (p != lst.end ())
+    {
+      while (true)
+        {
+          tree_statement *elt = *p++;
+
+          if (! elt)
+            error ("invalid statement found in statement list!");
+
+          octave_quit ();
+
+          elt->accept (*this);
+
+          if (m_breaking || m_continuing)
+            break;
+
+          if (m_returning)
+            break;
+
+          if (p == lst.end ())
+            break;
+          else
+            {
+              // Clear previous values before next statement is
+              // evaluated so that we aren't holding an extra
+              // reference to a value that may be used next.  For
+              // example, in code like this:
+              //
+              //   X = rand (N);  # refcount for X should be 1
+              //                  # after this statement
+              //
+              //   X(idx) = val;  # no extra copy of X should be
+              //                  # needed, but we will be faked
+              //                  # out if retval is not cleared
+              //                  # between statements here
+
+              //              result_values = empty_list;
+            }
+        }
+    }
+}
+
+void
+tree_evaluator::visit_switch_case (tree_switch_case&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_switch_case_list (tree_switch_case_list&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_switch_command (tree_switch_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  if (m_debug_mode)
+    do_breakpoint (cmd.is_active_breakpoint (*this));
+
+  tree_expression *expr = cmd.switch_value ();
+
+  if (! expr)
+    error ("missing value in switch command near line %d, column %d",
+           cmd.line (), cmd.column ());
+
+  octave_value val = expr->evaluate (*this);
+
+  tree_switch_case_list *lst = cmd.case_list ();
+
+  if (lst)
+    {
+      for (tree_switch_case *t : *lst)
+        {
+          if (t->is_default_case () || switch_case_label_matches (t, val))
+            {
+              tree_statement_list *stmt_lst = t->commands ();
+
+              if (stmt_lst)
+                stmt_lst->accept (*this);
+
+              break;
+            }
+        }
+    }
+}
+
+void
+tree_evaluator::visit_try_catch_command (tree_try_catch_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  bool execution_error = false;
+  octave_scalar_map err_map;
+
+  tree_statement_list *try_code = cmd.body ();
+
+  if (try_code)
+    {
+      // unwind frame before catch block
+
+      unwind_protect frame;
+
+      interpreter_try (frame);
+
+      // The catch code is *not* added to unwind_protect stack; it
+      // doesn't need to be run on interrupts.
+
+      try
+        {
+          try_code->accept (*this);
+        }
+      catch (const execution_exception& ee)
+        {
+          execution_error = true;
+
+          error_system& es = m_interpreter.get_error_system ();
+
+          es.save_exception (ee);
+
+          err_map.assign ("message", es.last_error_message ());
+          err_map.assign ("identifier", es.last_error_id ());
+          err_map.assign ("stack", es.last_error_stack ());
+
+          m_interpreter.recover_from_exception ();
+        }
+
+      // Actions attached to unwind_protect frame will run here, prior
+      // to executing the catch block.
+    }
+
+  if (execution_error)
+    {
+      tree_statement_list *catch_code = cmd.cleanup ();
+
+      if (catch_code)
+        {
+          tree_identifier *expr_id = cmd.identifier ();
+
+          if (expr_id)
+            {
+              octave_lvalue ult = expr_id->lvalue (*this);
+
+              ult.assign (octave_value::op_asn_eq, err_map);
+            }
+
+          // perform actual "catch" block
+          catch_code->accept (*this);
+        }
+    }
+}
+
+void
+tree_evaluator::do_unwind_protect_cleanup_code (tree_statement_list *list)
+{
+  unwind_protect frame;
+
+  frame.protect_var (octave_interrupt_state);
+  octave_interrupt_state = 0;
+
+  // We want to preserve the last location info for possible
+  // backtracking.
+
+  frame.add (&call_stack::set_line, &m_call_stack,
+             m_call_stack.current_line ());
+
+  frame.add (&call_stack::set_column, &m_call_stack,
+             m_call_stack.current_column ());
+
+  // Similarly, if we have seen a return or break statement, allow all
+  // the cleanup code to run before returning or handling the break.
+  // We don't have to worry about continue statements because they can
+  // only occur in loops.
+
+  frame.protect_var (m_returning);
+  m_returning = 0;
+
+  frame.protect_var (m_breaking);
+  m_breaking = 0;
+
+  try
+    {
+      if (list)
+        list->accept (*this);
+    }
+  catch (const execution_exception& ee)
+    {
+      error_system& es = m_interpreter.get_error_system ();
+
+      es.save_exception (ee);
+      m_interpreter.recover_from_exception ();
+
+      if (m_breaking || m_returning)
+        frame.discard (2);
+      else
+        frame.run (2);
+
+      frame.discard (2);
+
+      throw;
+    }
+
+  // The unwind_protects are popped off the stack in the reverse of
+  // the order they are pushed on.
+
+  // FIXME: these statements say that if we see a break or
+  // return statement in the cleanup block, that we want to use the
+  // new value of the breaking or returning flag instead of restoring
+  // the previous value.  Is that the right thing to do?  I think so.
+  // Consider the case of
+  //
+  //   function foo ()
+  //     unwind_protect
+  //       fprintf (stderr, "1: this should always be executed\n");
+  //       break;
+  //       fprintf (stderr, "1: this should never be executed\n");
+  //     unwind_protect_cleanup
+  //       fprintf (stderr, "2: this should always be executed\n");
+  //       return;
+  //       fprintf (stderr, "2: this should never be executed\n");
+  //     end_unwind_protect
+  //   endfunction
+  //
+  // If we reset the value of the breaking flag, both the returning
+  // flag and the breaking flag will be set, and we shouldn't have
+  // both.  So, use the most recent one.  If there is no return or
+  // break in the cleanup block, the values should be reset to
+  // whatever they were when the cleanup block was entered.
+
+  if (m_breaking || m_returning)
+    frame.discard (2);
+  else
+    frame.run (2);
+}
+
+void
+tree_evaluator::visit_unwind_protect_command (tree_unwind_protect_command& cmd)
+{
+  if (m_echo_state)
+    {
+      int line = cmd.line ();
+      if (line < 0)
+        line = 1;
+      echo_code (line);
+      m_echo_file_pos = line + 1;
+    }
+
+  tree_statement_list *cleanup_code = cmd.cleanup ();
+
+  tree_statement_list *unwind_protect_code = cmd.body ();
+
+  if (unwind_protect_code)
+    {
+      try
+        {
+          unwind_protect_code->accept (*this);
+        }
+      catch (const execution_exception& ee)
+        {
+          error_system& es = m_interpreter.get_error_system ();
+
+          // FIXME: Maybe we should be able to temporarily set the
+          // interpreter's exception handling state to something "safe"
+          // while the cleanup block runs instead of just resetting it
+          // here?
+          es.save_exception (ee);
+          m_interpreter.recover_from_exception ();
+
+          // Run the cleanup code on exceptions, so that it is run even
+          // in case of interrupt or out-of-memory.
+          do_unwind_protect_cleanup_code (cleanup_code);
+
+          // If an error occurs inside the cleanup code, a new
+          // exception will be thrown instead of the original.
+          throw;
+        }
+      catch (const interrupt_exception&)
+        {
+          // The comments above apply here as well.
+          m_interpreter.recover_from_exception ();
+          do_unwind_protect_cleanup_code (cleanup_code);
+          throw;
+        }
+
+      // Also execute the unwind_protect_cleanump code if the
+      // unwind_protect block runs without error.
+      do_unwind_protect_cleanup_code (cleanup_code);
+    }
+}
+
+void
+tree_evaluator::visit_while_command (tree_while_command& cmd)
+{
+  int line = cmd.line ();
+  if (line < 0)
+    line = 1;
+
+  if (m_echo_state)
+    {
+      echo_code (line);
+      line++;
+    }
+
+  unwind_protect_var<bool> upv (m_in_loop_command, true);
+
+  tree_expression *expr = cmd.condition ();
+
+  if (! expr)
+    panic_impossible ();
+
+  for (;;)
+    {
+      if (m_echo_state)
+        m_echo_file_pos = line;
+
+      if (m_debug_mode)
+        do_breakpoint (cmd.is_active_breakpoint (*this));
+
+      if (is_logically_true (expr, "while"))
+        {
+          tree_statement_list *loop_body = cmd.body ();
+
+          if (loop_body)
+            loop_body->accept (*this);
+
+          if (quit_loop_now ())
+            break;
+        }
+      else
+        break;
+    }
+}
+
+void
+tree_evaluator::visit_do_until_command (tree_do_until_command& cmd)
+{
+  int line = cmd.line ();
+  if (line < 0)
+    line = 1;
+
+  if (m_echo_state)
+    {
+      echo_code (line);
+      line++;
+    }
+
+  unwind_protect_var<bool> upv (m_in_loop_command, true);
+
+  tree_expression *expr = cmd.condition ();
+
+  if (! expr)
+    panic_impossible ();
+
+  for (;;)
+    {
+      if (m_echo_state)
+        m_echo_file_pos = line;
+
+      tree_statement_list *loop_body = cmd.body ();
+
+      if (loop_body)
+        loop_body->accept (*this);
+
+      if (quit_loop_now ())
+        break;
+
+      if (m_debug_mode)
+        do_breakpoint (cmd.is_active_breakpoint (*this));
+
+      if (is_logically_true (expr, "do-until"))
+        break;
+    }
+}
+
+void
+tree_evaluator::visit_superclass_ref (tree_superclass_ref&)
+{
+  panic_impossible ();
+}
+
+void
+tree_evaluator::visit_metaclass_query (tree_metaclass_query&)
+{
+  panic_impossible ();
+}
+
+void tree_evaluator::bind_ans (const octave_value& val, bool print)
+{
+  static std::string ans = "ans";
+
+  if (val.is_defined ())
+    {
+      if (val.is_cs_list ())
+        {
+          octave_value_list lst = val.list_value ();
+
+          for (octave_idx_type i = 0; i < lst.length (); i++)
+            bind_ans (lst(i), print);
+        }
+      else
+        {
+          // FIXME: Maybe assign could also return the assigned value,
+          // just for convenience?
+
+          assign (ans, val);
+
+          if (print)
+            {
+              // Use varval instead of displaying VAL directly so that
+              // we get the right type and value for things like
+              // magic_int values that may mutate when stored.
+
+              octave_value_list args = ovl (varval (ans));
+              args.stash_name_tags (string_vector (ans));
+              m_interpreter.feval ("display", args);
+            }
+        }
+    }
+}
+
+void
+tree_evaluator::do_breakpoint (tree_statement& stmt)
+{
+  do_breakpoint (stmt.is_active_breakpoint (*this),
+                 stmt.is_end_of_fcn_or_script ());
+}
+
+void
+tree_evaluator::do_breakpoint (bool is_breakpoint,
+                               bool is_end_of_fcn_or_script)
+{
+  bool break_on_this_statement = false;
+
+  if (is_breakpoint)
+    break_on_this_statement = true;
+  else if (m_dbstep_flag > 0)
+    {
+      if (m_call_stack.current_frame () == m_debug_frame)
+        {
+          if (m_dbstep_flag == 1 || is_end_of_fcn_or_script)
+            {
+              // We get here if we are doing a "dbstep" or a "dbstep N" and
+              // the count has reached 1 so that we must stop and return to
+              // debug prompt.  Alternatively, "dbstep N" has been used but
+              // the end of the frame has been reached so we stop at the last
+              // line and return to prompt.
+
+              break_on_this_statement = true;
+            }
+          else
+            {
+              // Executing "dbstep N".  Decrease N by one and continue.
+
+              m_dbstep_flag--;
+            }
+
+        }
+      else if (m_dbstep_flag == 1
+               && m_call_stack.current_frame () < m_debug_frame)
+        {
+          // We stepped out from the end of a function.
+
+          m_debug_frame = m_call_stack.current_frame ();
+
+          break_on_this_statement = true;
+        }
+    }
+  else if (m_dbstep_flag == -1)
+    {
+      // We get here if we are doing a "dbstep in".
+
+      break_on_this_statement = true;
+
+      m_debug_frame = m_call_stack.current_frame ();
+    }
+  else if (m_dbstep_flag == -2)
+    {
+      // We get here if we are doing a "dbstep out".  Check for end of
+      // function and whether the current frame is the same as the
+      // cached value because we want to step out from the frame where
+      // "dbstep out" was evaluated, not from any functions called from
+      // that frame.
+
+      if (is_end_of_fcn_or_script
+          && m_call_stack.current_frame () == m_debug_frame)
+        m_dbstep_flag = -1;
+    }
+
+  if (! break_on_this_statement)
+    break_on_this_statement = m_break_on_next_stmt;
+
+  m_break_on_next_stmt = false;
+
+  if (break_on_this_statement)
+    {
+      m_dbstep_flag = 0;
+
+      enter_debugger ();
+    }
+}
+
+bool
+tree_evaluator::is_logically_true (tree_expression *expr,
+                                   const char *warn_for)
+{
+  bool expr_value = false;
+
+  m_call_stack.set_location (expr->line (), expr->column ());
+
+  octave_value t1 = expr->evaluate (*this);
+
+  if (t1.is_defined ())
+    return t1.is_true ();
+  else
+    error ("%s: undefined value used in conditional expression", warn_for);
+
+  return expr_value;
+}
+
+octave_value
+tree_evaluator::max_recursion_depth (const octave_value_list& args,
+                                     int nargout)
+{
+  return set_internal_variable (m_max_recursion_depth, args, nargout,
+                                "max_recursion_depth", 0);
+}
+
+symbol_info_list
+tree_evaluator::glob_symbol_info (const std::string& pattern) const
+{
+  return m_call_stack.glob_symbol_info (pattern);
+}
+
+symbol_info_list
+tree_evaluator::regexp_symbol_info (const std::string& pattern) const
+{
+  return m_call_stack.regexp_symbol_info (pattern);
+}
+
+symbol_info_list
+tree_evaluator::get_symbol_info ()
+{
+  return m_call_stack.get_symbol_info ();
+}
+
+symbol_info_list
+tree_evaluator::top_scope_symbol_info () const
+{
+  return m_call_stack.top_scope_symbol_info ();
+}
+
+octave_map tree_evaluator::get_autoload_map () const
+{
+  Cell fcn_names (dim_vector (m_autoload_map.size (), 1));
+  Cell file_names (dim_vector (m_autoload_map.size (), 1));
+
+  octave_idx_type i = 0;
+  for (const auto& fcn_fname : m_autoload_map)
+    {
+      fcn_names(i) = fcn_fname.first;
+      file_names(i) = fcn_fname.second;
+
+      i++;
+    }
+
+  octave_map m;
+
+  m.assign ("function", fcn_names);
+  m.assign ("file", file_names);
+
+  return m;
+}
+
+std::string tree_evaluator::lookup_autoload (const std::string& nm) const
+{
+  std::string retval;
+
+  auto p = m_autoload_map.find (nm);
+
+  if (p != m_autoload_map.end ())
+    {
+      load_path& lp = m_interpreter.get_load_path ();
+
+      retval = lp.find_file (p->second);
+    }
+
+  return retval;
+}
+
+std::list<std::string> tree_evaluator::autoloaded_functions () const
+{
+  std::list<std::string> names;
+
+  for (const auto& fcn_fname : m_autoload_map)
+    names.push_back (fcn_fname.first);
+
+  return names;
+}
+
+std::list<std::string>
+tree_evaluator::reverse_lookup_autoload (const std::string& nm) const
+{
+  std::list<std::string> names;
+
+  for (const auto& fcn_fname : m_autoload_map)
+    if (nm == fcn_fname.second)
+      names.push_back (fcn_fname.first);
+
+  return names;
+}
+
+void tree_evaluator::add_autoload (const std::string& fcn,
+                                   const std::string& nm)
+{
+  std::string file_name = check_autoload_file (nm);
+
+  m_autoload_map[fcn] = file_name;
+}
+
+void tree_evaluator::remove_autoload (const std::string& fcn,
+                                      const std::string& nm)
+{
+  check_autoload_file (nm);
+
+  // Remove function from symbol table and autoload map.
+  symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+  symtab.clear_dld_function (fcn);
+
+  m_autoload_map.erase (fcn);
+}
+
+octave_value
+tree_evaluator::whos_line_format (const octave_value_list& args, int nargout)
+{
+  return set_internal_variable (m_whos_line_format, args, nargout,
+                                "whos_line_format");
+}
+
+octave_value
+tree_evaluator::silent_functions (const octave_value_list& args, int nargout)
+{
+  return set_internal_variable (m_silent_functions, args, nargout,
+                                "silent_functions");
+}
+
+octave_value
+tree_evaluator::string_fill_char (const octave_value_list& args, int nargout)
+{
+  return set_internal_variable (m_string_fill_char, args, nargout,
+                                "string_fill_char");
+}
+
+// Final step of processing an indexing error.  Add the name of the
+// variable being indexed, if any, then issue an error.  (Will this also
+// be needed by pt-lvalue, which calls subsref?)
+
+void tree_evaluator::final_index_error (index_exception& ie,
+                                        const tree_expression *expr)
+{
+  std::string extra_message;
+
+  if (is_variable (expr))
+    {
+      std::string var = expr->name ();
+
+      ie.set_var (var);
+
+      symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+      octave_value fcn = symtab.find_function (var);
+
+      if (fcn.is_function ())
+        {
+          octave_function *fp = fcn.function_value ();
+
+          if (fp && fp->name () == var)
+            extra_message
+              = " (note: variable '" + var + "' shadows function)";
+        }
+    }
+
+  std::string msg = ie.message () + extra_message;
+
+  error_with_id (ie.err_id (), "%s", msg.c_str ());
+}
+
+octave_value
+tree_evaluator::do_who (int argc, const string_vector& argv,
+                        bool return_list, bool verbose)
+{
+  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)
+{
+  octave_value_list retval;
+
+  if (args)
+    {
+      unwind_protect_var<const std::list<octave_lvalue> *>
+        upv (m_lvalue_list, nullptr);
+
+      int len = args->length ();
+
+      unwind_protect_var<int> upv2 (m_index_position);
+      unwind_protect_var<int> upv3 (m_num_indices);
+
+      m_num_indices = len;
+
+      std::list<octave_value> arg_vals;
+
+      int k = 0;
+
+      for (auto elt : *args)
+        {
+          // FIXME: is it possible for elt to be invalid?
+
+          if (! elt)
+            break;
+
+          m_index_position = k++;
+
+          octave_value tmp = elt->evaluate (*this);
+
+          if (tmp.is_cs_list ())
+            {
+              octave_value_list tmp_ovl = tmp.list_value ();
+
+              for (octave_idx_type i = 0; i < tmp_ovl.length (); i++)
+                arg_vals.push_back (tmp_ovl(i));
+            }
+          else if (tmp.is_defined ())
+            arg_vals.push_back (tmp);
+        }
+
+      retval = octave_value_list (arg_vals);
+    }
+
+  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 (int type, const std::string& file_name,
+                                 int pos)
+{
+  unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame ();
+
+  if (frame)
+    {
+      push_echo_state_cleanup (*frame);
+
+      set_echo_state (type, file_name, pos);
+    }
+}
+
+void
+tree_evaluator::set_echo_state (int type, const std::string& file_name,
+                                int pos)
+{
+  m_echo_state = echo_this_file (file_name, type);
+  m_echo_file_name = file_name;
+  m_echo_file_pos = pos;
+}
+
+void
+tree_evaluator::uwp_set_echo_state (bool state, const std::string& file_name,
+                                    int pos)
+{
+  m_echo_state = state;
+  m_echo_file_name = file_name;
+  m_echo_file_pos = pos;
+}
+
+void
+tree_evaluator::maybe_set_echo_state ()
+{
+  octave_function *caller = caller_function ();
+
+  if (caller && caller->is_user_code ())
+    {
+      octave_user_code *fcn = dynamic_cast<octave_user_code *> (caller);
+
+      int type = fcn->is_user_function () ? ECHO_FUNCTIONS : ECHO_SCRIPTS;
+
+      std::string file_name = fcn->fcn_file_name ();
+
+      // We want the line where "echo" was called, not the line number
+      // stored in the stack frame that was created for the echo
+      // function (that will always be -1).
+
+      int pos = m_call_stack.current_user_code_line ();
+
+      if (pos < 0)
+        pos = 1;
+
+      set_echo_state (type, file_name, pos);
+    }
+}
+
+void
+tree_evaluator::push_echo_state_cleanup (unwind_protect& frame)
+{
+  frame.add (&tree_evaluator::uwp_set_echo_state, this,
+             m_echo_state, m_echo_file_name, m_echo_file_pos);
+}
+
+bool tree_evaluator::maybe_push_echo_state_cleanup ()
+{
+  // This function is expected to be called from ECHO, which would be
+  // the top of the call stack.  If the caller of ECHO is a
+  // user-defined function or script, then set up unwind-protect
+  // elements to restore echo state.
+
+  unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame ();
+
+  if (frame)
+    {
+      push_echo_state_cleanup (*frame);
+      return true;
+    }
+
+  return false;
+}
+
+
+octave_value
+tree_evaluator::echo (const octave_value_list& args, int)
+{
+  bool cleanup_pushed = maybe_push_echo_state_cleanup ();
+
+  string_vector argv = args.make_argv ();
+
+  switch (args.length ())
+    {
+    case 0:
+      if ((m_echo & ECHO_SCRIPTS) || (m_echo & ECHO_FUNCTIONS))
+        {
+          m_echo = ECHO_OFF;
+          m_echo_files.clear ();
+        }
+      else
+        m_echo = ECHO_SCRIPTS;
+      break;
+
+    case 1:
       {
-        // FIXME: is it possible for elt to be invalid?
-
-        if (! elt)
-          break;
-
-        octave_value tmp = elt->evaluate (*this);
-
-        if (tmp.is_cs_list ())
+        std::string arg0 = argv[0];
+
+        if (arg0 == "on")
+          m_echo = ECHO_SCRIPTS;
+        else if (arg0 == "off")
+          m_echo = ECHO_OFF;
+        else
           {
-            octave_value_list tmp_ovl = tmp.list_value ();
-
-            for (octave_idx_type i = 0; i < tmp_ovl.length (); i++)
-              arg_vals.push_back (tmp_ovl(i));
-          }
-        else if (tmp.is_defined ())
-          arg_vals.push_back (tmp);
-      }
-
-    return octave_value_list (arg_vals);
-  }
-
-  octave_value_list
-  tree_evaluator::convert_return_list_to_const_vector
-    (tree_parameter_list *ret_list, int nargout, const Matrix& ignored_outputs,
-     const Cell& varargout)
-  {
-    octave_idx_type vlen = varargout.numel ();
-    int len = ret_list->length ();
-
-    // Special case.  Will do a shallow copy.
-    if (len == 0)
-      return varargout;
-    else
-      {
-        int i = 0;
-        int k = 0;
-        int num_ignored = ignored_outputs.numel ();
-        int ignored = num_ignored > 0 ? ignored_outputs(k) - 1 : -1;
-
-        if (nargout <= len)
-          {
-            int nout = nargout > 0 ? nargout : 1;
-            octave_value_list retval (nout);
-
-            for (tree_decl_elt *elt : *ret_list)
+            std::string file = fcn_file_in_path (arg0);
+            file = sys::env::make_absolute (file);
+
+            if (file.empty ())
+              error ("echo: no such file %s", arg0.c_str ());
+
+            if (m_echo & ECHO_ALL)
               {
-                if (nargout == 0 && ! is_defined (elt->ident ()))
-                  break;
-
-                if (ignored >= 0 && i == ignored)
+                // Echo is enabled for all functions, so turn it off
+                // for this one.
+
+                m_echo_files[file] = false;
+              }
+            else
+              {
+                // Echo may be enabled for specific functions.
+
+                auto p = m_echo_files.find (file);
+
+                if (p == m_echo_files.end ())
                   {
-                    i++;
-                    k++;
-                    ignored = k < num_ignored ? ignored_outputs(k) - 1 : -1;
+                    // Not this one, so enable it.
+
+                    m_echo |= ECHO_FUNCTIONS;
+                    m_echo_files[file] = true;
                   }
                 else
-                  retval(i++) = evaluate (elt);
-
-                if (i == nout)
-                  break;
-              }
-
-            return retval;
-          }
-        else
-          {
-            octave_value_list retval (len + vlen);
-
-            for (tree_decl_elt *elt : *ret_list)
-              {
-                if (ignored >= 0 && i == ignored)
                   {
-                    i++;
-                    k++;
-                    ignored = k < num_ignored ? ignored_outputs(k) - 1 : -1;
-                  }
-                else
-                  retval(i++) = evaluate (elt);
-              }
-
-            for (octave_idx_type j = 0; j < vlen; j++)
-              retval(i++) = varargout(j);
-
-            return retval;
-          }
-      }
-  }
-
-  bool
-  tree_evaluator::eval_decl_elt (tree_decl_elt *elt)
-  {
-    bool retval = false;
-
-    tree_identifier *id = elt->ident ();
-    tree_expression *expr = elt->expression ();
-
-    if (id && expr)
-      {
-        octave_lvalue ult = id->lvalue (*this);
-
-        octave_value init_val = expr->evaluate (*this);
-
-        ult.assign (octave_value::op_asn_eq, init_val);
-
-        retval = true;
-      }
-
-    return retval;
-  }
-
-  bool
-  tree_evaluator::switch_case_label_matches (tree_switch_case *expr,
-                                             const octave_value& val)
-  {
-    tree_expression *label = expr->case_label ();
-
-    octave_value label_value = label->evaluate (*this);
-
-    if (label_value.is_defined ())
-      {
-        if (label_value.iscell ())
-          {
-            Cell cell (label_value.cell_value ());
-
-            for (octave_idx_type i = 0; i < cell.rows (); i++)
-              {
-                for (octave_idx_type j = 0; j < cell.columns (); j++)
-                  {
-                    bool match = val.is_equal (cell(i,j));
-
-                    if (match)
-                      return true;
+                    // This one is already in the list.  Flip the
+                    // status for it.
+
+                    p->second = ! p->second;
                   }
               }
           }
-        else
-          return val.is_equal (label_value);
       }
-
-    return false;
-  }
-
-  void tree_evaluator::push_stack_frame (const symbol_scope& scope)
-  {
-    m_call_stack.push (scope);
-  }
-
-  void tree_evaluator::push_stack_frame (octave_user_function *fcn,
-                                         const std::shared_ptr<stack_frame>& closure_frames)
-  {
-    m_call_stack.push (fcn, closure_frames);
-  }
-
-  void tree_evaluator::push_stack_frame (octave_user_function *fcn,
-                                         const stack_frame::local_vars_map& local_vars,
-                                         const std::shared_ptr<stack_frame>& closure_frames)
-  {
-    m_call_stack.push (fcn, local_vars, closure_frames);
-  }
-
-  void tree_evaluator::push_stack_frame (octave_user_script *script)
-  {
-    m_call_stack.push (script);
-  }
-
-  void tree_evaluator::push_stack_frame (octave_function *fcn)
-  {
-    m_call_stack.push (fcn);
-  }
-
-  void tree_evaluator::pop_stack_frame ()
-  {
-    m_call_stack.pop ();
-  }
-
-  int tree_evaluator::current_line () const
-  {
-    return m_call_stack.current_line ();
-  }
-
-  int tree_evaluator::current_column () const
-  {
-    return m_call_stack.current_column ();
-  }
-
-  int tree_evaluator::debug_user_code_line () const
-  {
-    return m_call_stack.debug_user_code_line ();
-  }
-
-  int tree_evaluator::debug_user_code_column () const
-  {
-    return m_call_stack.debug_user_code_column ();
-  }
-
-  void tree_evaluator::debug_where (std::ostream& os) const
-  {
-    std::shared_ptr<stack_frame> frm = m_call_stack.current_user_frame ();
-
-    frm->display_stopped_in_message (os);
-  }
-
-  octave_user_code * tree_evaluator::current_user_code () const
-  {
-    return m_call_stack.current_user_code ();
-  }
-
-  unwind_protect * tree_evaluator::curr_fcn_unwind_protect_frame ()
-  {
-    return m_call_stack.curr_fcn_unwind_protect_frame ();
-  }
-
-  octave_user_code * tree_evaluator::debug_user_code () const
-  {
-    return m_call_stack.debug_user_code ();
-  }
-
-  octave_function * tree_evaluator::current_function (bool skip_first) const
-  {
-    return m_call_stack.current_function (skip_first);
-  }
-
-  octave_function * tree_evaluator::caller_function () const
-  {
-    return m_call_stack.current_function (true);
-  }
-
-  bool tree_evaluator::goto_frame (std::size_t n, bool verbose)
-  {
-    return m_call_stack.goto_frame (n, verbose);
-  }
-
-  void tree_evaluator::goto_caller_frame ()
-  {
-    m_call_stack.goto_caller_frame ();
-  }
-
-  void tree_evaluator::goto_base_frame ()
-  {
-    m_call_stack.goto_base_frame ();
-  }
-
-  void tree_evaluator::restore_frame (std::size_t n)
-  {
-    return m_call_stack.restore_frame (n);
-  }
-
-  std::string tree_evaluator::get_dispatch_class () const
-  {
-    return m_call_stack.get_dispatch_class ();
-  }
-
-  void tree_evaluator::set_dispatch_class (const std::string& class_name)
-  {
-    m_call_stack.set_dispatch_class (class_name);
-  }
-
-  bool
-  tree_evaluator::is_class_method_executing (std::string& dclass) const
-  {
-    return m_call_stack.is_class_method_executing (dclass);
-  }
-
-  bool
-  tree_evaluator::is_class_constructor_executing (std::string& dclass) const
-  {
-    return m_call_stack.is_class_constructor_executing (dclass);
-  }
-
-  std::list<std::shared_ptr<stack_frame>>
-  tree_evaluator::backtrace_frames (octave_idx_type& curr_user_frame) const
-  {
-    return m_call_stack.backtrace_frames (curr_user_frame);
-  }
-
-  std::list<std::shared_ptr<stack_frame>>
-  tree_evaluator::backtrace_frames () const
-  {
-    return m_call_stack.backtrace_frames ();
-  }
-
-  std::list<frame_info>
-  tree_evaluator::backtrace_info (octave_idx_type& curr_user_frame,
-                                  bool print_subfn) const
-  {
-    return m_call_stack.backtrace_info (curr_user_frame, print_subfn);
-  }
-
-  std::list<frame_info> tree_evaluator::backtrace_info () const
-  {
-    return m_call_stack.backtrace_info ();
-  }
-
-  octave_map
-  tree_evaluator::backtrace (octave_idx_type& curr_user_frame,
-                             bool print_subfn) const
-  {
-    return m_call_stack.backtrace (curr_user_frame, print_subfn);
-  }
-
-  octave_map tree_evaluator::backtrace () const
-  {
-    return m_call_stack.backtrace ();
-  }
-
-  octave_map tree_evaluator::empty_backtrace () const
-  {
-    return m_call_stack.empty_backtrace ();
-  }
-
-  std::string tree_evaluator::backtrace_message () const
-  {
-    std::list<frame_info> frames = backtrace_info ();
-
-    std::ostringstream buf;
-
-    for (const auto& frm : frames)
-      {
-        buf << "    " << frm.fcn_name ();
-
-        int line = frm.line ();
-
-        if (line > 0)
-          {
-            buf << " at line " << line;
-
-            int column = frm.column ();
-
-            if (column > 0)
-              buf << " column " << column;
-
-            buf << "\n";
-          }
-      }
-
-    return buf.str ();
-  }
-
-  void tree_evaluator::push_dummy_scope (const std::string& name)
-  {
-    symbol_scope dummy_scope (name + "$dummy");
-
-    m_call_stack.push (dummy_scope);
-  }
-
-  void tree_evaluator::pop_scope ()
-  {
-    m_call_stack.pop ();
-  }
-
-  symbol_scope tree_evaluator::get_top_scope () const
-  {
-    return m_call_stack.top_scope ();
-  }
-
-  symbol_scope tree_evaluator::get_current_scope () const
-  {
-    return m_call_stack.current_scope ();
-  }
-
-  void tree_evaluator::mlock (bool skip_first) const
-  {
-    octave_function *fcn = m_call_stack.current_function (skip_first);
-
-    if (! fcn)
-      error ("mlock: invalid use outside a function");
-
-    if (fcn->is_builtin_function ())
-      {
-        warning ("mlock: locking built-in function has no effect");
-        return;
-      }
-
-    fcn->lock ();
-  }
-
-  void tree_evaluator::munlock (bool skip_first) const
-  {
-    octave_function *fcn = m_call_stack.current_function (skip_first);
-
-    if (! fcn)
-      error ("munlock: invalid use outside a function");
-
-    if (fcn->is_builtin_function ())
+      break;
+
+    case 2:
       {
-        warning ("munlock: unlocking built-in function has no effect");
-        return;
-      }
-
-    fcn->unlock ();
-  }
-
-  bool tree_evaluator::mislocked (bool skip_first) const
-  {
-    octave_function *fcn = m_call_stack.current_function (skip_first);
-
-    if (! fcn)
-      error ("mislocked: invalid use outside a function");
-
-    return fcn->islocked ();
-  }
-
-  octave_value
-  tree_evaluator::max_stack_depth (const octave_value_list& args, int nargout)
-  {
-    return m_call_stack.max_stack_depth (args, nargout);
-  }
-
-  void tree_evaluator::display_call_stack () const
-  {
-    m_call_stack.display ();
-  }
-
-  octave_value tree_evaluator::find (const std::string& name)
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    octave_value val = frame->varval (name);
-
-    if (val.is_defined ())
-      return val;
-
-    // Subfunction.  I think it only makes sense to check for
-    // subfunctions if we are currently executing a function defined
-    // from a .m file.
-
-    octave_value fcn = frame->find_subfunction (name);
-
-    if (fcn.is_defined ())
-      return fcn;
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    return symtab.fcn_table_find (name, ovl ());
-  }
-
-  void tree_evaluator::clear_objects ()
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    frame->clear_objects ();
-  }
-
-  void tree_evaluator::clear_variable (const std::string& name)
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    frame->clear_variable (name);
-  }
-
-  void tree_evaluator::clear_variable_pattern (const std::string& pattern)
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    frame->clear_variable_pattern (pattern);
-  }
-
-  void tree_evaluator::clear_variable_regexp (const std::string& pattern)
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    frame->clear_variable_regexp (pattern);
-  }
-
-  void tree_evaluator::clear_variables ()
-  {
-    std::shared_ptr<stack_frame> frame
-      = m_call_stack.get_current_stack_frame ();
-
-    frame->clear_variables ();
-  }
-
-  void tree_evaluator::clear_global_variable (const std::string& name)
-  {
-    m_call_stack.clear_global_variable (name);
-  }
-
-  void
-  tree_evaluator::clear_global_variable_pattern (const std::string& pattern)
-  {
-    m_call_stack.clear_global_variable_pattern (pattern);
-  }
-
-  void tree_evaluator::clear_global_variable_regexp(const std::string& pattern)
-  {
-    m_call_stack.clear_global_variable_regexp (pattern);
-  }
-
-  void tree_evaluator::clear_global_variables ()
-  {
-    m_call_stack.clear_global_variables ();
-  }
-
-  void tree_evaluator::clear_all (bool force)
-  {
-    // FIXME: should this also clear objects?
-
-    clear_variables ();
-    clear_global_variables ();
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.clear_functions (force);
-  }
-
-  void tree_evaluator::clear_symbol (const std::string& name)
-  {
-    // FIXME: are we supposed to do both here?
-
-    clear_variable (name);
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.clear_function (name);
-  }
-
-  void tree_evaluator::clear_symbol_pattern (const std::string& pattern)
-  {
-    // FIXME: are we supposed to do both here?
-
-    clear_variable_pattern (pattern);
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.clear_function_pattern (pattern);
-  }
-
-  void tree_evaluator::clear_symbol_regexp (const std::string& pattern)
-  {
-    // FIXME: are we supposed to do both here?
-
-    clear_variable_regexp (pattern);
-
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.clear_function_regexp (pattern);
-  }
-
-  std::list<std::string> tree_evaluator::global_variable_names () const
-  {
-    return m_call_stack.global_variable_names ();
-  }
-
-  std::list<std::string> tree_evaluator::top_level_variable_names () const
-  {
-    return m_call_stack.top_level_variable_names ();
-  }
-
-  std::list<std::string> tree_evaluator::variable_names () const
-  {
-    return m_call_stack.variable_names ();
-  }
-
-  // Return a pointer to the user-defined function FNAME.  If FNAME is empty,
-  // search backward for the first user-defined function in the
-  // current call stack.
-
-  octave_user_code *
-  tree_evaluator::get_user_code (const std::string& fname,
-                                 const std::string& class_name)
-  {
-    octave_user_code *user_code = nullptr;
-
-    if (fname.empty ())
-      user_code = m_call_stack.debug_user_code ();
-    else
-      {
-        std::string name = fname;
-
-        if (sys::file_ops::dir_sep_char () != '/' && name[0] == '@')
-          {
-            auto beg = name.begin () + 2;  // never have @/method
-            auto end = name.end () - 1;    // never have trailing '/'
-            std::replace (beg, end, '/', sys::file_ops::dir_sep_char ());
-          }
-
-        std::size_t name_len = name.length ();
-
-        if (name_len > 2 && name.substr (name_len-2) == ".m")
-          name = name.substr (0, name_len-2);
-
-        if (name.empty ())
-          return nullptr;
-
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        octave_value fcn;
-        std::size_t p2 = std::string::npos;
-
-        if (name[0] == '@')
-          {
-            std::size_t p1 = name.find (sys::file_ops::dir_sep_char (), 1);
-
-            if (p1 == std::string::npos)
-              return nullptr;
-
-            std::string dispatch_type = name.substr (1, p1-1);
-
-            p2 = name.find ('>', p1);
-
-            std::string method = name.substr (p1+1, p2-1);
-
-            fcn = symtab.find_method (method, dispatch_type);
-          }
-        else if (! class_name.empty ())
-          {
-            cdef_manager& cdm = m_interpreter.get_cdef_manager ();
-
-            fcn = cdm.find_method (class_name, name);
-
-            // If there is no classdef method, then try legacy classes.
-            if (fcn.is_undefined ())
-              fcn = symtab.find_method (name, class_name);
-          }
-        else
+        std::string arg0 = argv[0];
+        std::string arg1 = argv[1];
+
+        if (arg1 == "on" || arg1 == "off")
+          std::swap (arg0, arg1);
+
+        if (arg0 == "on")
           {
-            p2 = name.find ('>');
-
-            std::string main_fcn = name.substr (0, p2);
-
-            fcn = symtab.find_function (main_fcn);
-          }
-
-        // List of function names sub1>sub2>...
-        std::string subfuns;
-
-        if (p2 != std::string::npos)
-          subfuns = name.substr (p2+1);
-
-        if (fcn.is_defined () && fcn.is_user_code ())
-          user_code = fcn.user_code_value ();
-
-        if (! user_code || subfuns.empty ())
-          return user_code;
-
-        fcn = user_code->find_subfunction (subfuns);
-
-        if (fcn.is_undefined ())
-          return nullptr;
-
-        user_code = fcn.user_code_value ();
-      }
-
-    return user_code;
-  }
-
-  std::string
-  tree_evaluator::current_function_name (bool skip_first) const
-  {
-    octave_function *curfcn = m_call_stack.current_function (skip_first);
-
-    if (curfcn)
-      return curfcn->name ();
-
-    return "";
-  }
-
-  bool
-  tree_evaluator::in_user_code () const
-  {
-    return m_call_stack.current_user_code () != nullptr;
-  }
-
-  void
-  tree_evaluator::visit_decl_command (tree_decl_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    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)
-      init_list->accept (*this);
-  }
-
-  void
-  tree_evaluator::visit_decl_elt (tree_decl_elt& elt)
-  {
-    tree_identifier *id = elt.ident ();
-
-    if (id)
-      {
-        if (elt.is_global ())
-          m_call_stack.make_global (id->symbol ());
-        else if (elt.is_persistent ())
-          m_call_stack.make_persistent (id->symbol ());
-        else
-          error ("declaration list element not global or persistent");
-
-        octave_lvalue ult = id->lvalue (*this);
-
-        if (ult.is_undefined ())
-          {
-            tree_expression *expr = elt.expression ();
-
-            octave_value init_val;
-
-            if (expr)
-              init_val = expr->evaluate (*this);
-            else
-              init_val = Matrix ();
-
-            ult.assign (octave_value::op_asn_eq, init_val);
-          }
-      }
-  }
-
-  template <typename T>
-  void
-  tree_evaluator::execute_range_loop (const range<T>& rng, int line,
-                                      octave_lvalue& ult,
-                                      tree_statement_list *loop_body)
-  {
-    octave_idx_type steps = rng.numel ();
-
-    if (math::isinf (rng.limit ()))
-      warning_with_id ("Octave:infinite-loop",
-                       "FOR loop limit is infinite, will stop after %"
-                       OCTAVE_IDX_TYPE_FORMAT " steps", steps);
-
-    for (octave_idx_type i = 0; i < steps; i++)
-      {
-        if (m_echo_state)
-          m_echo_file_pos = line;
-
-        octave_value val (rng.elem (i));
-
-        ult.assign (octave_value::op_asn_eq, val);
-
-        if (loop_body)
-          loop_body->accept (*this);
-
-        if (quit_loop_now ())
-          break;
-      }
-  }
-
-  void
-  tree_evaluator::visit_simple_for_command (tree_simple_for_command& cmd)
-  {
-    int line = cmd.line ();
-    if (line < 0)
-      line = 1;
-
-    if (m_echo_state)
-      {
-        echo_code (line);
-        line++;
-      }
-
-    if (m_debug_mode)
-      do_breakpoint (cmd.is_active_breakpoint (*this));
-
-    // FIXME: need to handle PARFOR loops here using cmd.in_parallel ()
-    // and cmd.maxproc_expr ();
-
-    unwind_protect_var<bool> upv (m_in_loop_command, true);
-
-    tree_expression *expr = cmd.control_expr ();
-
-    octave_value rhs = expr->evaluate (*this);
-
-    if (rhs.is_undefined ())
-      return;
-
-    tree_expression *lhs = cmd.left_hand_side ();
-
-    octave_lvalue ult = lhs->lvalue (*this);
-
-    tree_statement_list *loop_body = cmd.body ();
-
-    if (rhs.is_range ())
-      {
-        // FIXME: is there a better way to dispatch here?
-
-        if (rhs.is_double_type ())
-          {
-            execute_range_loop (rhs.range_value (), line, ult, loop_body);
-            return;
-          }
-
-        // For now, disable all but range<double>.
-
-#if 0
-        if (rhs.is_int64_type ())
-          {
-            execute_range_loop (rhs.int64_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_uint64_type ())
-          {
-            execute_range_loop (rhs.uint64_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_int32_type ())
-          {
-            execute_range_loop (rhs.int32_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_uint32_type ())
-          {
-            execute_range_loop (rhs.uint32_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_int16_type ())
-          {
-            execute_range_loop (rhs.int16_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_uint16_type ())
-          {
-            execute_range_loop (rhs.uint16_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_int8_type ())
-          {
-            execute_range_loop (rhs.int8_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_uint8_type ())
-          {
-            execute_range_loop (rhs.uint8_range_value (), line, ult, loop_body);
-            return;
-          }
-
-        if (rhs.is_single_type ())
-          {
-            execute_range_loop (rhs.float_range_value (), line, ult, loop_body);
-            return;
-          }
-#endif
-      }
-
-    if (rhs.is_scalar_type ())
-      {
-        if (m_echo_state)
-          m_echo_file_pos = line;
-
-        ult.assign (octave_value::op_asn_eq, rhs);
-
-        if (loop_body)
-          loop_body->accept (*this);
-
-        // Maybe decrement break and continue states.
-        quit_loop_now ();
-
-        return;
-      }
-
-    // Also handle any range types not explicitly handled above, though
-    // not as efficiently as the specialized code above.
-
-    if (rhs.is_range () || rhs.is_matrix_type () || rhs.iscell ()
-        || rhs.is_string () || rhs.isstruct ())
-      {
-        // A matrix or cell is reshaped to 2 dimensions and iterated by
-        // columns.
-
-        dim_vector dv = rhs.dims ().redim (2);
-
-        octave_idx_type nrows = dv(0);
-        octave_idx_type steps = dv(1);
-
-        octave_value arg = rhs;
-        if (rhs.ndims () > 2)
-          arg = arg.reshape (dv);
-
-        if (nrows > 0 && steps > 0)
-          {
-            octave_value_list idx;
-            octave_idx_type iidx;
-
-            // for row vectors, use single index to speed things up.
-            if (nrows == 1)
+            if (arg1 == "all")
               {
-                idx.resize (1);
-                iidx = 0;
+                m_echo = (ECHO_SCRIPTS | ECHO_FUNCTIONS | ECHO_ALL);
+                m_echo_files.clear ();
               }
             else
               {
-                idx.resize (2);
-                idx(0) = octave_value::magic_colon_t;
-                iidx = 1;
-              }
-
-            for (octave_idx_type i = 1; i <= steps; i++)
-              {
-                if (m_echo_state)
-                  m_echo_file_pos = line;
-
-                // index_op expects one-based indices.
-                idx(iidx) = i;
-                octave_value val = arg.index_op (idx);
-
-                ult.assign (octave_value::op_asn_eq, val);
-
-                if (loop_body)
-                  loop_body->accept (*this);
-
-                if (quit_loop_now ())
-                  break;
+                std::string file = fcn_file_in_path (arg1);
+                file = sys::env::make_absolute (file);
+
+                if (file.empty ())
+                  error ("echo: no such file %s", arg1.c_str ());
+
+                m_echo |= ECHO_FUNCTIONS;
+                m_echo_files[file] = true;
               }
           }
-        else
-          {
-            // Handle empty cases, while still assigning to loop var.
-            ult.assign (octave_value::op_asn_eq, arg);
-          }
-
-        return;
-      }
-
-    error ("invalid type in for loop expression near line %d, column %d",
-           cmd.line (), cmd.column ());
-  }
-
-  void
-  tree_evaluator::visit_complex_for_command (tree_complex_for_command& cmd)
-  {
-    int line = cmd.line ();
-    if (line < 0)
-      line = 1;
-
-    if (m_echo_state)
-      {
-        echo_code (line);
-        line++;
-      }
-
-    if (m_debug_mode)
-      do_breakpoint (cmd.is_active_breakpoint (*this));
-
-    unwind_protect_var<bool> upv (m_in_loop_command, true);
-
-    tree_expression *expr = cmd.control_expr ();
-
-    octave_value rhs = expr->evaluate (*this);
-
-    if (rhs.is_undefined ())
-      return;
-
-    if (! rhs.isstruct ())
-      error ("in statement 'for [X, Y] = VAL', VAL must be a structure");
-
-    // Cycle through structure elements.  First element of id_list
-    // is set to value and the second is set to the name of the
-    // structure element.
-
-    tree_argument_list *lhs = cmd.left_hand_side ();
-
-    auto p = lhs->begin ();
-
-    tree_expression *elt = *p++;
-
-    octave_lvalue val_ref = elt->lvalue (*this);
-
-    elt = *p;
-
-    octave_lvalue key_ref = elt->lvalue (*this);
-
-    const octave_map tmp_val = rhs.map_value ();
-
-    tree_statement_list *loop_body = cmd.body ();
-
-    string_vector keys = tmp_val.keys ();
-
-    octave_idx_type nel = keys.numel ();
-
-    for (octave_idx_type i = 0; i < nel; i++)
-      {
-        if (m_echo_state)
-          m_echo_file_pos = line;
-
-        std::string key = keys[i];
-
-        const Cell val_lst = tmp_val.contents (key);
-
-        octave_idx_type n = val_lst.numel ();
-
-        octave_value val = (n == 1) ? val_lst(0) : octave_value (val_lst);
-
-        val_ref.assign (octave_value::op_asn_eq, val);
-        key_ref.assign (octave_value::op_asn_eq, key);
-
-        if (loop_body)
-          loop_body->accept (*this);
-
-        if (quit_loop_now ())
-          break;
-      }
-  }
-
-  void tree_evaluator::visit_spmd_command (tree_spmd_command& cmd)
-  {
-    // For now, we just execute the commands serially.
-
-    tree_statement_list *body = cmd.body ();
-
-    if (body)
-      body->accept (*this);
-  }
-
-  octave_value
-  tree_evaluator::evaluate_anon_fcn_handle (tree_anon_fcn_handle& afh)
-  {
-    // FIXME: should CMD_LIST be limited to a single expression?
-    // I think that is what Matlab does.
-
-    symbol_scope new_scope;
-    symbol_scope scope = afh.scope ();
-    if (scope)
-      new_scope = scope.dup ();
-
-    tree_parameter_list *param_list = afh.parameter_list ();
-    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);
-
-    tree_expression *expr = afh.expression ();
-    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 (afh);
-
-    std::set<std::string> free_vars = anon_fcn_ctx.free_variables ();
-
-    stack_frame::local_vars_map local_vars;
-
-    std::shared_ptr<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_vars[name] = val;
-      }
-
-    octave_user_function *af
-      = new octave_user_function (new_scope, param_list_dup, ret_list,
-                                  stmt_list);
-
-    octave_function *curr_fcn = m_call_stack.current_function ();
-
-    bool is_nested = false;
-
-    if (curr_fcn)
-      {
-        // FIXME: maybe it would be better to just stash curr_fcn
-        // instead of individual bits of info about it?
-
-        // An anonymous function defined inside another nested function
-        // or parent of a nested function also behaves like a nested
-        // function.
-
-        if (curr_fcn->is_parent_function () || curr_fcn->is_nested_function ())
+        else if (arg0 == "off")
           {
-            is_nested = true;
-            af->mark_as_nested_function ();
-            new_scope.set_nesting_depth (parent_scope.nesting_depth () + 1);
-          }
-
-        af->stash_dir_name (curr_fcn->dir_name ());
-
-        new_scope.cache_fcn_file_name (curr_fcn->fcn_file_name ());
-        new_scope.cache_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);
-
-    return (is_nested
-            ? octave_value (new octave_fcn_handle (ov_fcn, local_vars, frame))
-            : octave_value (new octave_fcn_handle (ov_fcn, local_vars)));
-  }
-
-  octave_value_list
-  tree_evaluator::execute_builtin_function (octave_builtin& builtin_function,
-                                            int nargout,
-                                            const octave_value_list& args)
-  {
-    octave_value_list retval;
-
-    if (args.has_magic_colon ())
-      error ("invalid use of colon in function argument list");
-
-    profiler::enter<octave_builtin> block (m_profiler, builtin_function);
-
-    octave_builtin::fcn fcn = builtin_function.function ();
-
-    if (fcn)
-      retval = (*fcn) (args, nargout);
-    else
-      {
-        octave_builtin::meth meth = builtin_function.method ();
-
-        retval = (*meth) (m_interpreter, args, nargout);
-      }
-
-    // Do not allow null values to be returned from functions.
-    // FIXME: perhaps true builtins should be allowed?
-
-    retval.make_storable_values ();
-
-    // Fix the case of a single undefined value.
-    // This happens when a compiled function uses
-    //
-    //   octave_value retval;
-    //
-    // instead of
-    //
-    //   octave_value_list retval;
-    //
-    // the idiom is very common, so we solve that here.
-
-    if (retval.length () == 1 && retval.xelem (0).is_undefined ())
-      retval.clear ();
-
-    return retval;
-  }
-
-  octave_value_list
-  tree_evaluator::execute_mex_function (octave_mex_function& mex_function,
-                                        int nargout,
-                                        const octave_value_list& args)
-  {
-    octave_value_list retval;
-
-    if (args.has_magic_colon ())
-      error ("invalid use of colon in function argument list");
-
-    profiler::enter<octave_mex_function> block (m_profiler, mex_function);
-
-    retval = call_mex (mex_function, args, nargout);
-
-    return retval;
-  }
-
-  octave_value_list
-  tree_evaluator::execute_user_script (octave_user_script& user_script,
-                                       int nargout,
-                                       const octave_value_list& args)
-  {
-    octave_value_list retval;
-
-    std::string file_name = user_script.fcn_file_name ();
-
-    if (args.length () != 0 || nargout != 0)
-      error ("invalid call to script %s", file_name.c_str ());
-
-    tree_statement_list *cmd_list = user_script.body ();
-
-    if (! cmd_list)
-      return retval;
-
-    // FIXME: Maybe this check belongs in the places where we push a new
-    // stack frame?  Or in the call_stack push method itself?
-
-    if (m_call_stack.size () >= static_cast<std::size_t> (m_max_recursion_depth))
-      error ("max_recursion_depth exceeded");
-
-    unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_SCRIPT);
-
-    profiler::enter<octave_user_script> block (m_profiler, user_script);
-
-    if (echo ())
-      push_echo_state (tree_evaluator::ECHO_SCRIPTS, file_name);
-
-    // FIXME: Should we be using tree_evaluator::eval here?
-
-    cmd_list->accept (*this);
-
-    if (m_returning)
-      m_returning = 0;
-
-    if (m_breaking)
-      m_breaking--;
-
-    return retval;
-  }
-
-  void
-  tree_evaluator::visit_octave_user_script (octave_user_script&)
-  {
-    // ??
-    panic_impossible ();
-  }
-
-  octave_value_list
-  tree_evaluator::execute_user_function (octave_user_function& user_function,
-                                         int nargout,
-                                         const octave_value_list& xargs)
-  {
-    octave_value_list retval;
-
-    // If this function is a classdef constructor, extract the first input
-    // argument, which must be the partially constructed object instance.
-
-    octave_value_list args (xargs);
-    octave_value_list ret_args;
-
-    int nargin = args.length ();
-
-    if (user_function.is_classdef_constructor ())
-      {
-        if (nargin > 0)
-          {
-            ret_args = args.slice (0, 1, true);
-            --nargin;
-            args = args.slice (1, nargin, true);
-          }
-        else
-          panic_impossible ();
-      }
-
-    // FIXME: this probably shouldn't be a double-precision matrix.
-    Matrix ignored_outputs = ignored_fcn_outputs ();
-
-    tree_parameter_list *param_list = user_function.parameter_list ();
-
-    bool takes_varargs = false;
-    int max_inputs = 0;
-
-    if (param_list)
-      {
-        takes_varargs = param_list->takes_varargs ();
-        max_inputs = param_list->length ();
-      }
-
-    if (! takes_varargs && nargin > max_inputs)
-      {
-        std::string name = user_function.name ();
-
-        if (name.empty ())
-          name = "@<anonymous>";
-
-        error_with_id ("Octave:invalid-fun-call",
-                       "%s: function called with too many inputs",
-                       name.c_str ());
-      }
-
-    define_parameter_list_from_arg_vector (param_list, args);
-
-    tree_parameter_list *ret_list = user_function.return_list ();
-
-    if (ret_list && ! ret_list->takes_varargs ())
-      {
-        int max_outputs = ret_list->length ();
-
-        if (nargout > max_outputs)
-          {
-            std::string name = user_function.name ();
-
-            error_with_id ("Octave:invalid-fun-call",
-                           "%s: function called with too many outputs",
-                           name.c_str ());
-          }
-      }
-
-    bind_auto_fcn_vars (xargs.name_tags (), ignored_outputs, nargin,
-                        nargout, user_function.takes_varargs (),
-                        user_function.all_va_args (args));
-
-    // For classdef constructor, pre-populate the output arguments
-    // with the pre-initialized object instance, extracted above.
-
-    if (user_function.is_classdef_constructor ())
-      {
-        if (! ret_list)
-          error ("%s: invalid classdef constructor, no output argument defined",
-                 user_function.dispatch_class ().c_str ());
-
-        define_parameter_list_from_arg_vector (ret_list, ret_args);
-      }
-
-    // FIXME: Maybe this check belongs in the places where we push a
-    // new stack frame?  Or in the call_stack push method itself?
-
-    if (m_call_stack.size () >= static_cast<std::size_t> (m_max_recursion_depth))
-      error ("max_recursion_depth exceeded");
-
-    unwind_action act2 ([&user_function] () {
-                          user_function.restore_warning_states ();
-                        });
-
-    // Evaluate the commands that make up the function.
-
-    unwind_protect_var<stmt_list_type> upv (m_statement_context, SC_FUNCTION);
-
-    tree_statement_list *cmd_list = user_function.body ();
-
-    if (cmd_list)
-      {
-        profiler::enter<octave_user_function>
-          block (m_profiler, user_function);
-
-        if (echo ())
-          push_echo_state (tree_evaluator::ECHO_FUNCTIONS,
-                           user_function.fcn_file_name ());
-
-        if (user_function.is_special_expr ())
-          {
-            panic_if (cmd_list->length () != 1);
-
-            tree_statement *stmt = cmd_list->front ();
-
-            tree_expression *expr = stmt->expression ();
-
-            if (expr)
+            if (arg1 == "all")
+              {
+                m_echo = ECHO_OFF;
+                m_echo_files.clear ();
+              }
+            else
               {
-                m_call_stack.set_location (stmt->line (), stmt->column ());
-
-                retval = expr->evaluate_n (*this, nargout);
+                std::string file = fcn_file_in_path (arg1);
+                file = sys::env::make_absolute (file);
+
+                if (file.empty ())
+                  error ("echo: no such file %s", arg1.c_str ());
+
+                m_echo_files[file] = false;
               }
           }
         else
-          cmd_list->accept (*this);
-
-        if (m_returning)
-          m_returning = 0;
-
-        if (m_breaking)
-          m_breaking--;
-      }
-
-    // Copy return values out.
-
-    if (ret_list && ! user_function.is_special_expr ())
-      {
-        Cell varargout;
-
-        if (ret_list->takes_varargs ())
-          {
-            octave_value varargout_varval = varval ("varargout");
-
-            if (varargout_varval.is_defined ())
-              varargout = varargout_varval.xcell_value ("varargout must be a cell array object");
-          }
-
-        retval = convert_return_list_to_const_vector (ret_list, nargout,
-                                                      ignored_outputs,
-                                                      varargout);
-      }
-
-    return retval;
-  }
-
-  void
-  tree_evaluator::visit_octave_user_function (octave_user_function&)
-  {
-    // ??
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_octave_user_function_header (octave_user_function&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_octave_user_function_trailer (octave_user_function&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_function_def (tree_function_def& cmd)
-  {
-    octave_value fcn = cmd.function ();
-
-    octave_function *f = fcn.function_value ();
-
-    if (f)
-      {
-        std::string nm = f->name ();
-
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        symtab.install_cmdline_function (nm, fcn);
-
-        // Make sure that any variable with the same name as the new
-        // function is cleared.
-
-        assign (nm);
-      }
-  }
-
-  void
-  tree_evaluator::visit_identifier (tree_identifier&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_if_clause (tree_if_clause&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_if_command (tree_if_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        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)
-      lst->accept (*this);
-  }
-
-  void
-  tree_evaluator::visit_if_command_list (tree_if_command_list& lst)
-  {
-    for (tree_if_clause *tic : lst)
-      {
-        tree_expression *expr = tic->condition ();
-
-        if (! (in_debug_repl ()
-               && m_call_stack.current_frame () == m_debug_frame))
-          m_call_stack.set_location (tic->line (), tic->column ());
-
-        if (m_debug_mode && ! tic->is_else_clause ())
-          do_breakpoint (tic->is_active_breakpoint (*this));
-
-        if (tic->is_else_clause () || is_logically_true (expr, "if"))
-          {
-            tree_statement_list *stmt_lst = tic->commands ();
-
-            if (stmt_lst)
-              stmt_lst->accept (*this);
-
-            break;
-          }
-      }
-  }
-
-  void
-  tree_evaluator::visit_index_expression (tree_index_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_matrix (tree_matrix&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_cell (tree_cell&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_multi_assignment (tree_multi_assignment&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_no_op_command (tree_no_op_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    if (m_debug_mode && cmd.is_end_of_fcn_or_script ())
-      do_breakpoint (cmd.is_active_breakpoint (*this), true);
-  }
-
-  void
-  tree_evaluator::visit_constant (tree_constant&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_fcn_handle (tree_fcn_handle&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_parameter_list (tree_parameter_list&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_postfix_expression (tree_postfix_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_prefix_expression (tree_prefix_expression&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_return_command (tree_return_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    if (m_debug_mode)
-      do_breakpoint (cmd.is_active_breakpoint (*this));
-
-    // Act like dbcont.
-
-    if (in_debug_repl () && m_call_stack.current_frame () == m_debug_frame)
-      dbcont ();
-    else if (m_statement_context == SC_FUNCTION
-             || m_statement_context == SC_SCRIPT
-             || m_in_loop_command)
-      m_returning = 1;
-  }
-
-  void
-  tree_evaluator::visit_simple_assignment (tree_simple_assignment&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_statement (tree_statement& stmt)
-  {
-    tree_command *cmd = stmt.command ();
-    tree_expression *expr = stmt.expression ();
-
-    if (cmd || expr)
-      {
-        if (! (in_debug_repl ()
-               && m_call_stack.current_frame () == m_debug_frame))
-          m_call_stack.set_location (stmt.line (), stmt.column ());
-
-        try
-          {
-            if (cmd)
-              {
-                unwind_protect_var<const std::list<octave_lvalue> *>
-                  upv (m_lvalue_list, nullptr);
-
-                cmd->accept (*this);
-              }
-            else
-              {
-                if (m_echo_state)
-                  {
-                    int line = stmt.line ();
-                    if (line < 0)
-                      line = 1;
-                    echo_code (line);
-                    m_echo_file_pos = line + 1;
-                  }
-
-                if (m_debug_mode)
-                  do_breakpoint (expr->is_active_breakpoint (*this));
-
-                // FIXME: maybe all of this should be packaged in
-                // one virtual function that returns a flag saying whether
-                // or not the expression will take care of binding ans and
-                // printing the result.
-
-                // FIXME: it seems that we should just have to
-                // evaluate the expression and that should take care of
-                // everything, binding ans as necessary?
-
-                octave_value tmp_result = expr->evaluate (*this, 0);
-
-                if (tmp_result.is_defined ())
-                  {
-                    bool do_bind_ans = false;
-
-                    if (expr->is_identifier ())
-                      do_bind_ans = ! is_variable (expr);
-                    else
-                      do_bind_ans = ! expr->is_assignment_expression ();
-
-                    if (do_bind_ans)
-                      bind_ans (tmp_result, expr->print_result ()
-                                && statement_printing_enabled ());
-                  }
-              }
-          }
-        catch (const std::bad_alloc&)
-          {
-            // FIXME: We want to use error_with_id here so that give users
-            // control over this error message but error_with_id will
-            // require some memory allocations.  Is there anything we can
-            // do to make those more likely to succeed?
-
-            error_with_id ("Octave:bad-alloc",
-                           "out of memory or dimension too large for Octave's index type");
-          }
-        catch (const interrupt_exception&)
-          {
-            // If we are debugging, then continue with next statement.
-            // Otherwise, jump out of here.
-
-            if (m_debug_mode)
-              m_interpreter.recover_from_exception ();
-            else
-              throw;
-          }
-        catch (const execution_exception& ee)
-          {
-            error_system& es = m_interpreter.get_error_system ();
-
-            if ((m_interpreter.interactive ()
-                 || application::forced_interactive ())
-                && ((es.debug_on_error ()
-                     && m_bp_table.debug_on_err (es.last_error_id ()))
-                    || (es.debug_on_caught ()
-                        && m_bp_table.debug_on_caught (es.last_error_id ())))
-                && in_user_code ())
-              {
-                es.save_exception (ee);
-                es.display_exception (ee);
-
-                enter_debugger ();
-
-                // It doesn't make sense to continue execution after an
-                // error occurs so force the debugger to quit all debug
-                // levels and return the the top prompt.
-
-                throw quit_debug_exception (true);
-              }
-            else
-              throw;
-          }
-      }
-  }
-
-  void
-  tree_evaluator::visit_statement_list (tree_statement_list& lst)
-  {
-    // FIXME: commented out along with else clause below.
-    // static octave_value_list empty_list;
-
-    auto p = lst.begin ();
-
-    if (p != lst.end ())
-      {
-        while (true)
-          {
-            tree_statement *elt = *p++;
-
-            if (! elt)
-              error ("invalid statement found in statement list!");
-
-            octave_quit ();
-
-            elt->accept (*this);
-
-            if (m_breaking || m_continuing)
-              break;
-
-            if (m_returning)
-              break;
-
-            if (p == lst.end ())
-              break;
-            else
-              {
-                // Clear previous values before next statement is
-                // evaluated so that we aren't holding an extra
-                // reference to a value that may be used next.  For
-                // example, in code like this:
-                //
-                //   X = rand (N);  # refcount for X should be 1
-                //                  # after this statement
-                //
-                //   X(idx) = val;  # no extra copy of X should be
-                //                  # needed, but we will be faked
-                //                  # out if retval is not cleared
-                //                  # between statements here
-
-                //              result_values = empty_list;
-              }
-          }
-      }
-  }
-
-  void
-  tree_evaluator::visit_switch_case (tree_switch_case&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_switch_case_list (tree_switch_case_list&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_switch_command (tree_switch_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    if (m_debug_mode)
-      do_breakpoint (cmd.is_active_breakpoint (*this));
-
-    tree_expression *expr = cmd.switch_value ();
-
-    if (! expr)
-      error ("missing value in switch command near line %d, column %d",
-             cmd.line (), cmd.column ());
-
-    octave_value val = expr->evaluate (*this);
-
-    tree_switch_case_list *lst = cmd.case_list ();
-
-    if (lst)
-      {
-        for (tree_switch_case *t : *lst)
-          {
-            if (t->is_default_case () || switch_case_label_matches (t, val))
-              {
-                tree_statement_list *stmt_lst = t->commands ();
-
-                if (stmt_lst)
-                  stmt_lst->accept (*this);
-
-                break;
-              }
-          }
-      }
-  }
-
-  void
-  tree_evaluator::visit_try_catch_command (tree_try_catch_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
-      }
-
-    bool execution_error = false;
-    octave_scalar_map err_map;
-
-    tree_statement_list *try_code = cmd.body ();
-
-    if (try_code)
-      {
-        // unwind frame before catch block
-
-        unwind_protect frame;
-
-        interpreter_try (frame);
-
-        // The catch code is *not* added to unwind_protect stack; it
-        // doesn't need to be run on interrupts.
-
-        try
-          {
-            try_code->accept (*this);
-          }
-        catch (const execution_exception& ee)
-          {
-            execution_error = true;
-
-            error_system& es = m_interpreter.get_error_system ();
-
-            es.save_exception (ee);
-
-            err_map.assign ("message", es.last_error_message ());
-            err_map.assign ("identifier", es.last_error_id ());
-            err_map.assign ("stack", es.last_error_stack ());
-
-            m_interpreter.recover_from_exception ();
-          }
-
-        // Actions attached to unwind_protect frame will run here, prior
-        // to executing the catch block.
-      }
-
-    if (execution_error)
-      {
-        tree_statement_list *catch_code = cmd.cleanup ();
-
-        if (catch_code)
-          {
-            tree_identifier *expr_id = cmd.identifier ();
-
-            if (expr_id)
-              {
-                octave_lvalue ult = expr_id->lvalue (*this);
-
-                ult.assign (octave_value::op_asn_eq, err_map);
-              }
-
-            // perform actual "catch" block
-            catch_code->accept (*this);
-          }
-      }
-  }
-
-  void
-  tree_evaluator::do_unwind_protect_cleanup_code (tree_statement_list *list)
-  {
-    unwind_protect frame;
-
-    frame.protect_var (octave_interrupt_state);
-    octave_interrupt_state = 0;
-
-    // We want to preserve the last location info for possible
-    // backtracking.
-
-    frame.add (&call_stack::set_line, &m_call_stack,
-               m_call_stack.current_line ());
-
-    frame.add (&call_stack::set_column, &m_call_stack,
-               m_call_stack.current_column ());
-
-    // Similarly, if we have seen a return or break statement, allow all
-    // the cleanup code to run before returning or handling the break.
-    // We don't have to worry about continue statements because they can
-    // only occur in loops.
-
-    frame.protect_var (m_returning);
-    m_returning = 0;
-
-    frame.protect_var (m_breaking);
-    m_breaking = 0;
-
-    try
-      {
-        if (list)
-          list->accept (*this);
-      }
-    catch (const execution_exception& ee)
-      {
-        error_system& es = m_interpreter.get_error_system ();
-
-        es.save_exception (ee);
-        m_interpreter.recover_from_exception ();
-
-        if (m_breaking || m_returning)
-          frame.discard (2);
-        else
-          frame.run (2);
-
-        frame.discard (2);
-
-        throw;
-      }
-
-    // The unwind_protects are popped off the stack in the reverse of
-    // the order they are pushed on.
-
-    // FIXME: these statements say that if we see a break or
-    // return statement in the cleanup block, that we want to use the
-    // new value of the breaking or returning flag instead of restoring
-    // the previous value.  Is that the right thing to do?  I think so.
-    // Consider the case of
-    //
-    //   function foo ()
-    //     unwind_protect
-    //       fprintf (stderr, "1: this should always be executed\n");
-    //       break;
-    //       fprintf (stderr, "1: this should never be executed\n");
-    //     unwind_protect_cleanup
-    //       fprintf (stderr, "2: this should always be executed\n");
-    //       return;
-    //       fprintf (stderr, "2: this should never be executed\n");
-    //     end_unwind_protect
-    //   endfunction
-    //
-    // If we reset the value of the breaking flag, both the returning
-    // flag and the breaking flag will be set, and we shouldn't have
-    // both.  So, use the most recent one.  If there is no return or
-    // break in the cleanup block, the values should be reset to
-    // whatever they were when the cleanup block was entered.
-
-    if (m_breaking || m_returning)
-      frame.discard (2);
-    else
-      frame.run (2);
-  }
-
-  void
-  tree_evaluator::visit_unwind_protect_command (tree_unwind_protect_command& cmd)
-  {
-    if (m_echo_state)
-      {
-        int line = cmd.line ();
-        if (line < 0)
-          line = 1;
-        echo_code (line);
-        m_echo_file_pos = line + 1;
+          print_usage ();
       }
-
-    tree_statement_list *cleanup_code = cmd.cleanup ();
-
-    tree_statement_list *unwind_protect_code = cmd.body ();
-
-    if (unwind_protect_code)
-      {
-        try
-          {
-            unwind_protect_code->accept (*this);
-          }
-        catch (const execution_exception& ee)
-          {
-            error_system& es = m_interpreter.get_error_system ();
-
-            // FIXME: Maybe we should be able to temporarily set the
-            // interpreter's exception handling state to something "safe"
-            // while the cleanup block runs instead of just resetting it
-            // here?
-            es.save_exception (ee);
-            m_interpreter.recover_from_exception ();
-
-            // Run the cleanup code on exceptions, so that it is run even
-            // in case of interrupt or out-of-memory.
-            do_unwind_protect_cleanup_code (cleanup_code);
-
-            // If an error occurs inside the cleanup code, a new
-            // exception will be thrown instead of the original.
-            throw;
-          }
-        catch (const interrupt_exception&)
-          {
-            // The comments above apply here as well.
-            m_interpreter.recover_from_exception ();
-            do_unwind_protect_cleanup_code (cleanup_code);
-            throw;
-          }
-
-        // Also execute the unwind_protect_cleanump code if the
-        // unwind_protect block runs without error.
-        do_unwind_protect_cleanup_code (cleanup_code);
-      }
-  }
-
-  void
-  tree_evaluator::visit_while_command (tree_while_command& cmd)
-  {
-    int line = cmd.line ();
-    if (line < 0)
-      line = 1;
-
-    if (m_echo_state)
-      {
-        echo_code (line);
-        line++;
-      }
-
-    unwind_protect_var<bool> upv (m_in_loop_command, true);
-
-    tree_expression *expr = cmd.condition ();
-
-    if (! expr)
-      panic_impossible ();
-
-    for (;;)
-      {
-        if (m_echo_state)
-          m_echo_file_pos = line;
-
-        if (m_debug_mode)
-          do_breakpoint (cmd.is_active_breakpoint (*this));
-
-        if (is_logically_true (expr, "while"))
-          {
-            tree_statement_list *loop_body = cmd.body ();
-
-            if (loop_body)
-              loop_body->accept (*this);
-
-            if (quit_loop_now ())
-              break;
-          }
-        else
-          break;
-      }
-  }
-
-  void
-  tree_evaluator::visit_do_until_command (tree_do_until_command& cmd)
-  {
-    int line = cmd.line ();
-    if (line < 0)
-      line = 1;
-
-    if (m_echo_state)
-      {
-        echo_code (line);
-        line++;
-      }
-
-    unwind_protect_var<bool> upv (m_in_loop_command, true);
-
-    tree_expression *expr = cmd.condition ();
-
-    if (! expr)
-      panic_impossible ();
-
-    for (;;)
-      {
-        if (m_echo_state)
-          m_echo_file_pos = line;
-
-        tree_statement_list *loop_body = cmd.body ();
-
-        if (loop_body)
-          loop_body->accept (*this);
-
-        if (quit_loop_now ())
-          break;
-
-        if (m_debug_mode)
-          do_breakpoint (cmd.is_active_breakpoint (*this));
-
-        if (is_logically_true (expr, "do-until"))
-          break;
-      }
-  }
-
-  void
-  tree_evaluator::visit_superclass_ref (tree_superclass_ref&)
-  {
-    panic_impossible ();
-  }
-
-  void
-  tree_evaluator::visit_metaclass_query (tree_metaclass_query&)
-  {
-    panic_impossible ();
-  }
-
-  void tree_evaluator::bind_ans (const octave_value& val, bool print)
-  {
-    static std::string ans = "ans";
-
-    if (val.is_defined ())
-      {
-        if (val.is_cs_list ())
-          {
-            octave_value_list lst = val.list_value ();
-
-            for (octave_idx_type i = 0; i < lst.length (); i++)
-              bind_ans (lst(i), print);
-          }
-        else
-          {
-            // FIXME: Maybe assign could also return the assigned value,
-            // just for convenience?
-
-            assign (ans, val);
-
-            if (print)
-              {
-                // Use varval instead of displaying VAL directly so that
-                // we get the right type and value for things like
-                // magic_int values that may mutate when stored.
-
-                octave_value_list args = ovl (varval (ans));
-                args.stash_name_tags (string_vector (ans));
-                m_interpreter.feval ("display", args);
-              }
-          }
-      }
-  }
-
-  void
-  tree_evaluator::do_breakpoint (tree_statement& stmt)
-  {
-    do_breakpoint (stmt.is_active_breakpoint (*this),
-                   stmt.is_end_of_fcn_or_script ());
-  }
-
-  void
-  tree_evaluator::do_breakpoint (bool is_breakpoint,
-                                 bool is_end_of_fcn_or_script)
-  {
-    bool break_on_this_statement = false;
-
-    if (is_breakpoint)
-      break_on_this_statement = true;
-    else if (m_dbstep_flag > 0)
-      {
-        if (m_call_stack.current_frame () == m_debug_frame)
-          {
-            if (m_dbstep_flag == 1 || is_end_of_fcn_or_script)
-              {
-                // We get here if we are doing a "dbstep" or a "dbstep N" and
-                // the count has reached 1 so that we must stop and return to
-                // debug prompt.  Alternatively, "dbstep N" has been used but
-                // the end of the frame has been reached so we stop at the last
-                // line and return to prompt.
-
-                break_on_this_statement = true;
-              }
-            else
-              {
-                // Executing "dbstep N".  Decrease N by one and continue.
-
-                m_dbstep_flag--;
-              }
-
-          }
-        else if (m_dbstep_flag == 1
-                 && m_call_stack.current_frame () < m_debug_frame)
-          {
-            // We stepped out from the end of a function.
-
-            m_debug_frame = m_call_stack.current_frame ();
-
-            break_on_this_statement = true;
-          }
-      }
-    else if (m_dbstep_flag == -1)
-      {
-        // We get here if we are doing a "dbstep in".
-
-        break_on_this_statement = true;
-
-        m_debug_frame = m_call_stack.current_frame ();
-      }
-    else if (m_dbstep_flag == -2)
-      {
-        // We get here if we are doing a "dbstep out".  Check for end of
-        // function and whether the current frame is the same as the
-        // cached value because we want to step out from the frame where
-        // "dbstep out" was evaluated, not from any functions called from
-        // that frame.
-
-        if (is_end_of_fcn_or_script
-            && m_call_stack.current_frame () == m_debug_frame)
-          m_dbstep_flag = -1;
-      }
-
-    if (! break_on_this_statement)
-      break_on_this_statement = m_break_on_next_stmt;
-
-    m_break_on_next_stmt = false;
-
-    if (break_on_this_statement)
-      {
-        m_dbstep_flag = 0;
-
-        enter_debugger ();
-      }
-  }
-
-  bool
-  tree_evaluator::is_logically_true (tree_expression *expr,
-                                     const char *warn_for)
-  {
-    bool expr_value = false;
-
-    m_call_stack.set_location (expr->line (), expr->column ());
-
-    octave_value t1 = expr->evaluate (*this);
-
-    if (t1.is_defined ())
-      return t1.is_true ();
-    else
-      error ("%s: undefined value used in conditional expression", warn_for);
-
-    return expr_value;
-  }
-
-  octave_value
-  tree_evaluator::max_recursion_depth (const octave_value_list& args,
-                                       int nargout)
-  {
-    return set_internal_variable (m_max_recursion_depth, args, nargout,
-                                  "max_recursion_depth", 0);
-  }
-
-  symbol_info_list
-  tree_evaluator::glob_symbol_info (const std::string& pattern) const
-  {
-    return m_call_stack.glob_symbol_info (pattern);
-  }
-
-  symbol_info_list
-  tree_evaluator::regexp_symbol_info (const std::string& pattern) const
-  {
-    return m_call_stack.regexp_symbol_info (pattern);
-  }
-
-  symbol_info_list
-  tree_evaluator::get_symbol_info ()
-  {
-    return m_call_stack.get_symbol_info ();
-  }
-
-  symbol_info_list
-  tree_evaluator::top_scope_symbol_info () const
-  {
-    return m_call_stack.top_scope_symbol_info ();
-  }
-
-  octave_map tree_evaluator::get_autoload_map () const
-  {
-    Cell fcn_names (dim_vector (m_autoload_map.size (), 1));
-    Cell file_names (dim_vector (m_autoload_map.size (), 1));
-
-    octave_idx_type i = 0;
-    for (const auto& fcn_fname : m_autoload_map)
-      {
-        fcn_names(i) = fcn_fname.first;
-        file_names(i) = fcn_fname.second;
-
-        i++;
-      }
-
-    octave_map m;
-
-    m.assign ("function", fcn_names);
-    m.assign ("file", file_names);
-
-    return m;
-  }
-
-  std::string tree_evaluator::lookup_autoload (const std::string& nm) const
-  {
-    std::string retval;
-
-    auto p = m_autoload_map.find (nm);
-
-    if (p != m_autoload_map.end ())
-      {
-        load_path& lp = m_interpreter.get_load_path ();
-
-        retval = lp.find_file (p->second);
-      }
-
-    return retval;
-  }
-
-  std::list<std::string> tree_evaluator::autoloaded_functions () const
-  {
-    std::list<std::string> names;
-
-    for (const auto& fcn_fname : m_autoload_map)
-      names.push_back (fcn_fname.first);
-
-    return names;
-  }
-
-  std::list<std::string>
-  tree_evaluator::reverse_lookup_autoload (const std::string& nm) const
-  {
-    std::list<std::string> names;
-
-    for (const auto& fcn_fname : m_autoload_map)
-      if (nm == fcn_fname.second)
-        names.push_back (fcn_fname.first);
-
-    return names;
-  }
-
-  void tree_evaluator::add_autoload (const std::string& fcn,
-                                     const std::string& nm)
-  {
-    std::string file_name = check_autoload_file (nm);
-
-    m_autoload_map[fcn] = file_name;
-  }
-
-  void tree_evaluator::remove_autoload (const std::string& fcn,
-                                        const std::string& nm)
-  {
-    check_autoload_file (nm);
-
-    // Remove function from symbol table and autoload map.
-    symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-    symtab.clear_dld_function (fcn);
-
-    m_autoload_map.erase (fcn);
-  }
-
-  octave_value
-  tree_evaluator::whos_line_format (const octave_value_list& args, int nargout)
-  {
-    return set_internal_variable (m_whos_line_format, args, nargout,
-                                  "whos_line_format");
-  }
-
-  octave_value
-  tree_evaluator::silent_functions (const octave_value_list& args, int nargout)
-  {
-    return set_internal_variable (m_silent_functions, args, nargout,
-                                  "silent_functions");
-  }
-
-  octave_value
-  tree_evaluator::string_fill_char (const octave_value_list& args, int nargout)
-  {
-    return set_internal_variable (m_string_fill_char, args, nargout,
-                                  "string_fill_char");
-  }
-
-  // Final step of processing an indexing error.  Add the name of the
-  // variable being indexed, if any, then issue an error.  (Will this also
-  // be needed by pt-lvalue, which calls subsref?)
-
-  void tree_evaluator::final_index_error (index_exception& ie,
-                                          const tree_expression *expr)
-  {
-    std::string extra_message;
-
-    if (is_variable (expr))
-      {
-        std::string var = expr->name ();
-
-        ie.set_var (var);
-
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        octave_value fcn = symtab.find_function (var);
-
-        if (fcn.is_function ())
-          {
-            octave_function *fp = fcn.function_value ();
-
-            if (fp && fp->name () == var)
-              extra_message
-                = " (note: variable '" + var + "' shadows function)";
-          }
-      }
-
-    std::string msg = ie.message () + extra_message;
-
-    error_with_id (ie.err_id (), "%s", msg.c_str ());
-  }
-
-  octave_value
-  tree_evaluator::do_who (int argc, const string_vector& argv,
-                          bool return_list, bool verbose)
-  {
-    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)
-  {
-    octave_value_list retval;
-
-    if (args)
-      {
-        unwind_protect_var<const std::list<octave_lvalue> *>
-          upv (m_lvalue_list, nullptr);
-
-        int len = args->length ();
-
-        unwind_protect_var<int> upv2 (m_index_position);
-        unwind_protect_var<int> upv3 (m_num_indices);
-
-        m_num_indices = len;
-
-        std::list<octave_value> arg_vals;
-
-        int k = 0;
-
-        for (auto elt : *args)
-          {
-            // FIXME: is it possible for elt to be invalid?
-
-            if (! elt)
-              break;
-
-            m_index_position = k++;
-
-            octave_value tmp = elt->evaluate (*this);
-
-            if (tmp.is_cs_list ())
-              {
-                octave_value_list tmp_ovl = tmp.list_value ();
-
-                for (octave_idx_type i = 0; i < tmp_ovl.length (); i++)
-                  arg_vals.push_back (tmp_ovl(i));
-              }
-            else if (tmp.is_defined ())
-              arg_vals.push_back (tmp);
-          }
-
-        retval = octave_value_list (arg_vals);
-      }
-
-    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 (int type, const std::string& file_name,
-                                   int pos)
-  {
-    unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame ();
-
-    if (frame)
-      {
-        push_echo_state_cleanup (*frame);
-
-        set_echo_state (type, file_name, pos);
-      }
-  }
-
-  void
-  tree_evaluator::set_echo_state (int type, const std::string& file_name,
-                                  int pos)
-  {
-    m_echo_state = echo_this_file (file_name, type);
-    m_echo_file_name = file_name;
-    m_echo_file_pos = pos;
-  }
-
-  void
-  tree_evaluator::uwp_set_echo_state (bool state, const std::string& file_name,
-                                      int pos)
-  {
-    m_echo_state = state;
-    m_echo_file_name = file_name;
-    m_echo_file_pos = pos;
-  }
-
-  void
-  tree_evaluator::maybe_set_echo_state ()
-  {
-    octave_function *caller = caller_function ();
-
-    if (caller && caller->is_user_code ())
-      {
-        octave_user_code *fcn = dynamic_cast<octave_user_code *> (caller);
-
-        int type = fcn->is_user_function () ? ECHO_FUNCTIONS : ECHO_SCRIPTS;
-
-        std::string file_name = fcn->fcn_file_name ();
-
-        // We want the line where "echo" was called, not the line number
-        // stored in the stack frame that was created for the echo
-        // function (that will always be -1).
-
-        int pos = m_call_stack.current_user_code_line ();
-
-        if (pos < 0)
-          pos = 1;
-
-        set_echo_state (type, file_name, pos);
-      }
-  }
-
-  void
-  tree_evaluator::push_echo_state_cleanup (unwind_protect& frame)
-  {
-    frame.add (&tree_evaluator::uwp_set_echo_state, this,
-               m_echo_state, m_echo_file_name, m_echo_file_pos);
-  }
-
-  bool tree_evaluator::maybe_push_echo_state_cleanup ()
-  {
-    // This function is expected to be called from ECHO, which would be
-    // the top of the call stack.  If the caller of ECHO is a
-    // user-defined function or script, then set up unwind-protect
-    // elements to restore echo state.
-
-    unwind_protect *frame = m_call_stack.curr_fcn_unwind_protect_frame ();
-
-    if (frame)
-      {
-        push_echo_state_cleanup (*frame);
-        return true;
-      }
-
-    return false;
-  }
-
-
-  octave_value
-  tree_evaluator::echo (const octave_value_list& args, int)
-  {
-    bool cleanup_pushed = maybe_push_echo_state_cleanup ();
-
-    string_vector argv = args.make_argv ();
-
-    switch (args.length ())
-      {
-      case 0:
-        if ((m_echo & ECHO_SCRIPTS) || (m_echo & ECHO_FUNCTIONS))
-          {
-            m_echo = ECHO_OFF;
-            m_echo_files.clear ();
-          }
-        else
-          m_echo = ECHO_SCRIPTS;
-        break;
-
-      case 1:
+      break;
+
+    default:
+      print_usage ();
+      break;
+    }
+
+  if (cleanup_pushed)
+    maybe_set_echo_state ();
+
+  return octave_value ();
+}
+
+bool tree_evaluator::in_debug_repl () const
+{
+  return (m_debugger_stack.empty ()
+          ? false : m_debugger_stack.top()->in_debug_repl ());
+}
+
+void tree_evaluator::dbcont ()
+{
+  if (! m_debugger_stack.empty ())
+    m_debugger_stack.top()->dbcont ();
+}
+
+void tree_evaluator::dbquit (bool all)
+{
+  if (! m_debugger_stack.empty ())
+    m_debugger_stack.top()->dbquit (all);
+}
+
+static octave_value end_value (const octave_value& value,
+                               octave_idx_type index_position,
+                               octave_idx_type num_indices)
+{
+  dim_vector dv = value.dims ();
+  int ndims = dv.ndims ();
+
+  if (num_indices < ndims)
+    {
+      for (int i = num_indices; i < ndims; i++)
+        dv(num_indices-1) *= dv(i);
+
+      if (num_indices == 1)
+        {
+          ndims = 2;
+          dv.resize (ndims);
+          dv(1) = 1;
+        }
+      else
+        {
+          ndims = num_indices;
+          dv.resize (ndims);
+        }
+    }
+
+  return (index_position < ndims
+          ? octave_value (dv(index_position)) : octave_value (1.0));
+}
+
+octave_value_list
+tree_evaluator::evaluate_end_expression (const octave_value_list& args)
+{
+  int nargin = args.length ();
+
+  if (nargin != 0 && nargin != 3)
+    print_usage ();
+
+  if (nargin == 3)
+    {
+      octave_idx_type index_position
+        = args(1).xidx_type_value ("end: K must be integer value");
+
+      if (index_position < 1)
+        error ("end: K must be greater than zero");
+
+      octave_idx_type num_indices
+        = args(2).xidx_type_value ("end: N must be integer value");
+
+      if (num_indices < 1)
+        error ("end: N must be greater than zero");
+
+      return end_value (args(0), index_position-1, num_indices);
+    }
+
+  // If m_indexed_object is undefined, then this use of 'end' is
+  // either appearing in a function call argument list or in an
+  // attempt to index an undefined symbol.  There seems to be no
+  // reasonable way to provide a better error message.  So just fail
+  // with an invalid use message.  See bug #58830.
+
+  if (m_indexed_object.is_undefined ())
+    error ("invalid use of 'end': may only be used to index existing value");
+
+  octave_value expr_result;
+
+  if (m_index_list.empty ())
+    expr_result = m_indexed_object;
+  else
+    {
+      try
+        {
+          // When evaluating "end" with no arguments, we should have
+          // been called from the built-in Fend function that appears
+          // in the context of an argument list.  Fend will be
+          // evaluated in its own stack frame.  But we need to
+          // evaluate the partial expression that the special "end"
+          // token applies to in the calling stack frame.
+
+          unwind_action act ([=] (std::size_t frm)
+                             {
+                               m_call_stack.restore_frame (frm);
+                             }, m_call_stack.current_frame ());
+
+          std::size_t n = m_call_stack.find_current_user_frame ();
+          m_call_stack.goto_frame (n);
+
+          // End is only valid inside argument lists used for
+          // indexing.  The dispatch class is set by the function that
+          // evaluates the argument list.
+
+          // Silently ignore extra output values.
+
+          octave_value_list tmp
+            = m_indexed_object.subsref (m_index_type, m_index_list, 1);
+
+          expr_result = tmp.length () ? tmp(0) : octave_value ();
+
+          if (expr_result.is_cs_list ())
+            err_indexed_cs_list ();
+        }
+      catch (const index_exception&)
         {
-          std::string arg0 = argv[0];
-
-          if (arg0 == "on")
-            m_echo = ECHO_SCRIPTS;
-          else if (arg0 == "off")
-            m_echo = ECHO_OFF;
-          else
+          error ("error evaluating partial expression for END");
+        }
+    }
+
+  if (expr_result.isobject ())
+    {
+      // FIXME: is there a better way to lookup and execute a method
+      // that handles all the details like setting the dispatch class
+      // appropriately?
+
+      std::string dispatch_class = expr_result.class_name ();
+
+      symbol_table& symtab = m_interpreter.get_symbol_table ();
+
+      octave_value meth = symtab.find_method ("end", dispatch_class);
+
+      if (meth.is_defined ())
+        return m_interpreter.feval
+          (meth, ovl (expr_result, m_index_position+1, m_num_indices), 1);
+    }
+
+  return end_value (expr_result, m_index_position, m_num_indices);
+}
+
+octave_value
+tree_evaluator::PS4 (const octave_value_list& args, int nargout)
+{
+  return set_internal_variable (m_PS4, args, nargout, "PS4");
+}
+
+bool tree_evaluator::echo_this_file (const std::string& file, int type) const
+{
+  if ((type & m_echo) == ECHO_SCRIPTS)
+    {
+      // Asking about scripts and echo is enabled for them.
+      return true;
+    }
+
+  if ((type & m_echo) == ECHO_FUNCTIONS)
+    {
+      // Asking about functions and echo is enabled for functions.
+      // Now, which ones?
+
+      auto p = m_echo_files.find (file);
+
+      if (m_echo & ECHO_ALL)
+        {
+          // Return true ulness echo was turned off for a specific
+          // file.
+
+          return (p == m_echo_files.end () || p->second);
+        }
+      else
+        {
+          // Return true if echo is specifically enabled for this file.
+
+          return p != m_echo_files.end () && p->second;
+        }
+    }
+
+  return false;
+}
+
+void tree_evaluator::echo_code (int line)
+{
+  std::string prefix = command_editor::decode_prompt_string (m_PS4);
+
+  octave_function *curr_fcn = m_call_stack.current_function ();
+
+  if (curr_fcn && curr_fcn->is_user_code ())
+    {
+      octave_user_code *code = dynamic_cast<octave_user_code *> (curr_fcn);
+
+      int num_lines = line - m_echo_file_pos + 1;
+
+      std::deque<std::string> lines
+        = code->get_code_lines (m_echo_file_pos, num_lines);
+
+      for (auto& elt : lines)
+        octave_stdout << prefix << elt << std::endl;
+    }
+}
+
+// Decide if it's time to quit a for or while loop.
+bool tree_evaluator::quit_loop_now ()
+{
+  octave_quit ();
+
+  // Maybe handle 'continue N' someday...
+
+  if (m_continuing)
+    m_continuing--;
+
+  bool quit = (m_returning || m_breaking || m_continuing);
+
+  if (m_breaking)
+    m_breaking--;
+
+  return quit;
+}
+
+void tree_evaluator::bind_auto_fcn_vars (const string_vector& arg_names,
+                                         const Matrix& ignored_outputs,
+                                         int nargin, int nargout,
+                                         bool takes_varargs,
+                                         const octave_value_list& va_args)
+{
+  set_auto_fcn_var (stack_frame::ARG_NAMES, Cell (arg_names));
+  set_auto_fcn_var (stack_frame::IGNORED, ignored_outputs);
+  set_auto_fcn_var (stack_frame::NARGIN, nargin);
+  set_auto_fcn_var (stack_frame::NARGOUT, nargout);
+  set_auto_fcn_var (stack_frame::SAVED_WARNING_STATES, octave_value ());
+
+  if (takes_varargs)
+    assign ("varargin", va_args.cell_value ());
+}
+
+std::string
+tree_evaluator::check_autoload_file (const std::string& nm) const
+{
+  if (sys::env::absolute_pathname (nm))
+    return nm;
+
+  std::string full_name = nm;
+
+  octave_user_code *fcn = m_call_stack.current_user_code ();
+
+  bool found = false;
+
+  if (fcn)
+    {
+      std::string fname = fcn->fcn_file_name ();
+
+      if (! fname.empty ())
+        {
+          fname = sys::env::make_absolute (fname);
+          fname = fname.substr (0, fname.find_last_of (sys::file_ops::dir_sep_str ()) + 1);
+
+          sys::file_stat fs (fname + nm);
+
+          if (fs.exists ())
             {
-              std::string file = fcn_file_in_path (arg0);
-              file = sys::env::make_absolute (file);
-
-              if (file.empty ())
-                error ("echo: no such file %s", arg0.c_str ());
-
-              if (m_echo & ECHO_ALL)
-                {
-                  // Echo is enabled for all functions, so turn it off
-                  // for this one.
-
-                  m_echo_files[file] = false;
-                }
-              else
-                {
-                  // Echo may be enabled for specific functions.
-
-                  auto p = m_echo_files.find (file);
-
-                  if (p == m_echo_files.end ())
-                    {
-                      // Not this one, so enable it.
-
-                      m_echo |= ECHO_FUNCTIONS;
-                      m_echo_files[file] = true;
-                    }
-                  else
-                    {
-                      // This one is already in the list.  Flip the
-                      // status for it.
-
-                      p->second = ! p->second;
-                    }
-                }
+              full_name = fname + nm;
+              found = true;
             }
         }
-        break;
-
-      case 2:
-        {
-          std::string arg0 = argv[0];
-          std::string arg1 = argv[1];
-
-          if (arg1 == "on" || arg1 == "off")
-            std::swap (arg0, arg1);
-
-          if (arg0 == "on")
-            {
-              if (arg1 == "all")
-                {
-                  m_echo = (ECHO_SCRIPTS | ECHO_FUNCTIONS | ECHO_ALL);
-                  m_echo_files.clear ();
-                }
-              else
-                {
-                  std::string file = fcn_file_in_path (arg1);
-                  file = sys::env::make_absolute (file);
-
-                  if (file.empty ())
-                    error ("echo: no such file %s", arg1.c_str ());
-
-                  m_echo |= ECHO_FUNCTIONS;
-                  m_echo_files[file] = true;
-                }
-            }
-          else if (arg0 == "off")
-            {
-              if (arg1 == "all")
-                {
-                  m_echo = ECHO_OFF;
-                  m_echo_files.clear ();
-                }
-              else
-                {
-                  std::string file = fcn_file_in_path (arg1);
-                  file = sys::env::make_absolute (file);
-
-                  if (file.empty ())
-                    error ("echo: no such file %s", arg1.c_str ());
-
-                  m_echo_files[file] = false;
-                }
-            }
-          else
-            print_usage ();
-        }
-        break;
-
-      default:
-        print_usage ();
-        break;
-      }
-
-    if (cleanup_pushed)
-      maybe_set_echo_state ();
-
-    return octave_value ();
-  }
-
-  bool tree_evaluator::in_debug_repl () const
-  {
-    return (m_debugger_stack.empty ()
-            ? false : m_debugger_stack.top()->in_debug_repl ());
-  }
-
-  void tree_evaluator::dbcont ()
-  {
-    if (! m_debugger_stack.empty ())
-      m_debugger_stack.top()->dbcont ();
-  }
-
-  void tree_evaluator::dbquit (bool all)
-  {
-    if (! m_debugger_stack.empty ())
-      m_debugger_stack.top()->dbquit (all);
-  }
-
-  static octave_value end_value (const octave_value& value,
-                                 octave_idx_type index_position,
-                                 octave_idx_type num_indices)
-  {
-    dim_vector dv = value.dims ();
-    int ndims = dv.ndims ();
-
-    if (num_indices < ndims)
-      {
-        for (int i = num_indices; i < ndims; i++)
-          dv(num_indices-1) *= dv(i);
-
-        if (num_indices == 1)
-          {
-            ndims = 2;
-            dv.resize (ndims);
-            dv(1) = 1;
-          }
-        else
-          {
-            ndims = num_indices;
-            dv.resize (ndims);
-          }
-      }
-
-    return (index_position < ndims
-            ? octave_value (dv(index_position)) : octave_value (1.0));
-  }
-
-  octave_value_list
-  tree_evaluator::evaluate_end_expression (const octave_value_list& args)
-  {
-    int nargin = args.length ();
-
-    if (nargin != 0 && nargin != 3)
-      print_usage ();
-
-    if (nargin == 3)
-      {
-        octave_idx_type index_position
-          = args(1).xidx_type_value ("end: K must be integer value");
-
-        if (index_position < 1)
-          error ("end: K must be greater than zero");
-
-        octave_idx_type num_indices
-          = args(2).xidx_type_value ("end: N must be integer value");
-
-        if (num_indices < 1)
-          error ("end: N must be greater than zero");
-
-        return end_value (args(0), index_position-1, num_indices);
-      }
-
-    // If m_indexed_object is undefined, then this use of 'end' is
-    // either appearing in a function call argument list or in an
-    // attempt to index an undefined symbol.  There seems to be no
-    // reasonable way to provide a better error message.  So just fail
-    // with an invalid use message.  See bug #58830.
-
-    if (m_indexed_object.is_undefined ())
-      error ("invalid use of 'end': may only be used to index existing value");
-
-    octave_value expr_result;
-
-    if (m_index_list.empty ())
-      expr_result = m_indexed_object;
-    else
-      {
-        try
-          {
-            // When evaluating "end" with no arguments, we should have
-            // been called from the built-in Fend function that appears
-            // in the context of an argument list.  Fend will be
-            // evaluated in its own stack frame.  But we need to
-            // evaluate the partial expression that the special "end"
-            // token applies to in the calling stack frame.
-
-            unwind_action act ([=] (std::size_t frm)
-                               {
-                                 m_call_stack.restore_frame (frm);
-                               }, m_call_stack.current_frame ());
-
-            std::size_t n = m_call_stack.find_current_user_frame ();
-            m_call_stack.goto_frame (n);
-
-            // End is only valid inside argument lists used for
-            // indexing.  The dispatch class is set by the function that
-            // evaluates the argument list.
-
-            // Silently ignore extra output values.
-
-            octave_value_list tmp
-              = m_indexed_object.subsref (m_index_type, m_index_list, 1);
-
-            expr_result = tmp.length () ? tmp(0) : octave_value ();
-
-            if (expr_result.is_cs_list ())
-              err_indexed_cs_list ();
-          }
-        catch (const index_exception&)
-          {
-            error ("error evaluating partial expression for END");
-          }
-      }
-
-    if (expr_result.isobject ())
-      {
-        // FIXME: is there a better way to lookup and execute a method
-        // that handles all the details like setting the dispatch class
-        // appropriately?
-
-        std::string dispatch_class = expr_result.class_name ();
-
-        symbol_table& symtab = m_interpreter.get_symbol_table ();
-
-        octave_value meth = symtab.find_method ("end", dispatch_class);
-
-        if (meth.is_defined ())
-          return m_interpreter.feval
-            (meth, ovl (expr_result, m_index_position+1, m_num_indices), 1);
-      }
-
-    return end_value (expr_result, m_index_position, m_num_indices);
-  }
-
-  octave_value
-  tree_evaluator::PS4 (const octave_value_list& args, int nargout)
-  {
-    return set_internal_variable (m_PS4, args, nargout, "PS4");
-  }
-
-  bool tree_evaluator::echo_this_file (const std::string& file, int type) const
-  {
-    if ((type & m_echo) == ECHO_SCRIPTS)
-      {
-        // Asking about scripts and echo is enabled for them.
-        return true;
-      }
-
-    if ((type & m_echo) == ECHO_FUNCTIONS)
-      {
-        // Asking about functions and echo is enabled for functions.
-        // Now, which ones?
-
-        auto p = m_echo_files.find (file);
-
-        if (m_echo & ECHO_ALL)
-          {
-            // Return true ulness echo was turned off for a specific
-            // file.
-
-            return (p == m_echo_files.end () || p->second);
-          }
-        else
-          {
-            // Return true if echo is specifically enabled for this file.
-
-            return p != m_echo_files.end () && p->second;
-          }
-      }
-
-    return false;
-  }
-
-  void tree_evaluator::echo_code (int line)
-  {
-    std::string prefix = command_editor::decode_prompt_string (m_PS4);
-
-    octave_function *curr_fcn = m_call_stack.current_function ();
-
-    if (curr_fcn && curr_fcn->is_user_code ())
-      {
-        octave_user_code *code = dynamic_cast<octave_user_code *> (curr_fcn);
-
-        int num_lines = line - m_echo_file_pos + 1;
-
-        std::deque<std::string> lines
-          = code->get_code_lines (m_echo_file_pos, num_lines);
-
-        for (auto& elt : lines)
-          octave_stdout << prefix << elt << std::endl;
-      }
-  }
-
-  // Decide if it's time to quit a for or while loop.
-  bool tree_evaluator::quit_loop_now ()
-  {
-    octave_quit ();
-
-    // Maybe handle 'continue N' someday...
-
-    if (m_continuing)
-      m_continuing--;
-
-    bool quit = (m_returning || m_breaking || m_continuing);
-
-    if (m_breaking)
-      m_breaking--;
-
-    return quit;
-  }
-
-  void tree_evaluator::bind_auto_fcn_vars (const string_vector& arg_names,
-                                           const Matrix& ignored_outputs,
-                                           int nargin, int nargout,
-                                           bool takes_varargs,
-                                           const octave_value_list& va_args)
-  {
-    set_auto_fcn_var (stack_frame::ARG_NAMES, Cell (arg_names));
-    set_auto_fcn_var (stack_frame::IGNORED, ignored_outputs);
-    set_auto_fcn_var (stack_frame::NARGIN, nargin);
-    set_auto_fcn_var (stack_frame::NARGOUT, nargout);
-    set_auto_fcn_var (stack_frame::SAVED_WARNING_STATES, octave_value ());
-
-    if (takes_varargs)
-      assign ("varargin", va_args.cell_value ());
-  }
-
-  std::string
-  tree_evaluator::check_autoload_file (const std::string& nm) const
-  {
-    if (sys::env::absolute_pathname (nm))
-      return nm;
-
-    std::string full_name = nm;
-
-    octave_user_code *fcn = m_call_stack.current_user_code ();
-
-    bool found = false;
-
-    if (fcn)
-      {
-        std::string fname = fcn->fcn_file_name ();
-
-        if (! fname.empty ())
-          {
-            fname = sys::env::make_absolute (fname);
-            fname = fname.substr (0, fname.find_last_of (sys::file_ops::dir_sep_str ()) + 1);
-
-            sys::file_stat fs (fname + nm);
-
-            if (fs.exists ())
-              {
-                full_name = fname + nm;
-                found = true;
-              }
-          }
-      }
-
-    if (! found)
-      warning_with_id ("Octave:autoload-relative-file-name",
-                       "autoload: '%s' is not an absolute filename",
-                       nm.c_str ());
-
-    return full_name;
-  }
+    }
+
+  if (! found)
+    warning_with_id ("Octave:autoload-relative-file-name",
+                     "autoload: '%s' is not an absolute filename",
+                     nm.c_str ());
+
+  return full_name;
+}
 
 DEFMETHOD (max_recursion_depth, interp, args, nargout,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} max_recursion_depth ()
 @deftypefnx {} {@var{old_val} =} max_recursion_depth (@var{new_val})
 @deftypefnx {} {@var{old_val} =} max_recursion_depth (@var{new_val}, "local")
@@ -5272,9 +5272,9 @@
 @seealso{max_stack_depth}
 @end deftypefn */)
 {
-  tree_evaluator& tw = interp.get_evaluator ();
-
-  return tw.max_recursion_depth (args, nargout);
+tree_evaluator& tw = interp.get_evaluator ();
+
+return tw.max_recursion_depth (args, nargout);
 }
 
 /*
@@ -5290,7 +5290,7 @@
 */
 
 DEFMETHOD (whos_line_format, interp, args, nargout,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} whos_line_format ()
 @deftypefnx {} {@var{old_val} =} whos_line_format (@var{new_val})
 @deftypefnx {} {@var{old_val} =} whos_line_format (@var{new_val}, "local")
@@ -5365,13 +5365,13 @@
 @seealso{whos}
 @end deftypefn */)
 {
-  tree_evaluator& tw = interp.get_evaluator ();
-
-  return tw.whos_line_format (args, nargout);
+tree_evaluator& tw = interp.get_evaluator ();
+
+return tw.whos_line_format (args, nargout);
 }
 
 DEFMETHOD (silent_functions, interp, args, nargout,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} silent_functions ()
 @deftypefnx {} {@var{old_val} =} silent_functions (@var{new_val})
 @deftypefnx {} {@var{old_val} =} silent_functions (@var{new_val}, "local")
@@ -5387,9 +5387,9 @@
 The original variable value is restored when exiting the function.
 @end deftypefn */)
 {
-  tree_evaluator& tw = interp.get_evaluator ();
-
-  return tw.silent_functions (args, nargout);
+tree_evaluator& tw = interp.get_evaluator ();
+
+return tw.silent_functions (args, nargout);
 }
 
 /*
@@ -5405,7 +5405,7 @@
 */
 
 DEFMETHOD (string_fill_char, interp, args, nargout,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} string_fill_char ()
 @deftypefnx {} {@var{old_val} =} string_fill_char (@var{new_val})
 @deftypefnx {} {@var{old_val} =} string_fill_char (@var{new_val}, "local")
@@ -5419,9 +5419,9 @@
 @group
 string_fill_char ("X");
 [ "these"; "are"; "strings" ]
-      @result{}  "theseXX"
-          "areXXXX"
-          "strings"
+    @result{}  "theseXX"
+        "areXXXX"
+        "strings"
 @end group
 @end example
 
@@ -5430,9 +5430,9 @@
 The original variable value is restored when exiting the function.
 @end deftypefn */)
 {
-  tree_evaluator& tw = interp.get_evaluator ();
-
-  return tw.string_fill_char (args, nargout);
+tree_evaluator& tw = interp.get_evaluator ();
+
+return tw.string_fill_char (args, nargout);
 }
 
 /*
@@ -5454,7 +5454,7 @@
 */
 
 DEFMETHOD (PS4, interp, args, nargout,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn  {} {@var{val} =} PS4 ()
 @deftypefnx {} {@var{old_val} =} PS4 (@var{new_val})
 @deftypefnx {} {@var{old_val} =} PS4 (@var{new_val}, "local")
@@ -5470,11 +5470,11 @@
 @seealso{echo, PS1, PS2}
 @end deftypefn */)
 {
-  return interp.PS4 (args, nargout);
+return interp.PS4 (args, nargout);
 }
 
 DEFMETHOD (echo, interp, args, nargout,
-           doc: /* -*- texinfo -*-
+         doc: /* -*- texinfo -*-
 @deftypefn  {} {} echo
 @deftypefnx {} {} echo on
 @deftypefnx {} {} echo off
@@ -5514,9 +5514,9 @@
 @seealso{PS4}
 @end deftypefn */)
 {
-  tree_evaluator& tw = interp.get_evaluator ();
-
-  return tw.echo (args, nargout);
+tree_evaluator& tw = interp.get_evaluator ();
+
+return tw.echo (args, nargout);
 }
 
 /*