view libinterp/parse-tree/pt-idx.cc @ 31607:aac27ad79be6 stable

maint: Re-indent code after switch to using namespace macros. * build-env.h, build-env.in.cc, Cell.h, __betainc__.cc, __eigs__.cc, __ftp__.cc, __ichol__.cc, __ilu__.cc, __isprimelarge__.cc, __magick_read__.cc, __pchip_deriv__.cc, amd.cc, base-text-renderer.cc, base-text-renderer.h, besselj.cc, bitfcns.cc, bsxfun.cc, c-file-ptr-stream.h, call-stack.cc, call-stack.h, ccolamd.cc, cellfun.cc, chol.cc, colamd.cc, dasrt.cc, data.cc, debug.cc, defaults.cc, defaults.h, det.cc, display.cc, display.h, dlmread.cc, dynamic-ld.cc, dynamic-ld.h, ellipj.cc, environment.cc, environment.h, error.cc, error.h, errwarn.h, event-manager.cc, event-manager.h, event-queue.cc, event-queue.h, fcn-info.cc, fcn-info.h, fft.cc, fft2.cc, file-io.cc, filter.cc, find.cc, ft-text-renderer.cc, ft-text-renderer.h, gcd.cc, gl-render.cc, gl-render.h, gl2ps-print.cc, gl2ps-print.h, graphics-toolkit.cc, graphics-toolkit.h, graphics.cc, gsvd.cc, gtk-manager.cc, gtk-manager.h, help.cc, help.h, hook-fcn.cc, hook-fcn.h, input.cc, input.h, interpreter-private.cc, interpreter-private.h, interpreter.cc, interpreter.h, inv.cc, jsondecode.cc, jsonencode.cc, latex-text-renderer.cc, latex-text-renderer.h, load-path.cc, load-path.h, load-save.cc, load-save.h, lookup.cc, ls-hdf5.cc, ls-mat4.cc, ls-mat5.cc, lsode.cc, lu.cc, mappers.cc, matrix_type.cc, max.cc, mex.cc, mexproto.h, mxarray.h, mxtypes.in.h, oct-errno.in.cc, oct-hdf5-types.cc, oct-hist.cc, oct-hist.h, oct-map.cc, oct-map.h, oct-opengl.h, oct-prcstrm.h, oct-process.cc, oct-process.h, oct-stdstrm.h, oct-stream.cc, oct-stream.h, oct-strstrm.h, octave-default-image.h, ordqz.cc, ordschur.cc, pager.cc, pager.h, pinv.cc, pow2.cc, pr-output.cc, psi.cc, qr.cc, quadcc.cc, rand.cc, regexp.cc, settings.cc, settings.h, sighandlers.cc, sighandlers.h, sparse-xpow.cc, sqrtm.cc, stack-frame.cc, stack-frame.h, stream-euler.cc, strfns.cc, svd.cc, syminfo.cc, syminfo.h, symrcm.cc, symrec.cc, symrec.h, symscope.cc, symscope.h, symtab.cc, symtab.h, sysdep.cc, sysdep.h, text-engine.cc, text-engine.h, text-renderer.cc, text-renderer.h, time.cc, toplev.cc, typecast.cc, url-handle-manager.cc, url-handle-manager.h, urlwrite.cc, utils.cc, utils.h, variables.cc, variables.h, xdiv.cc, __delaunayn__.cc, __init_fltk__.cc, __init_gnuplot__.cc, __ode15__.cc, __voronoi__.cc, audioread.cc, convhulln.cc, gzip.cc, cdef-class.cc, cdef-class.h, cdef-fwd.h, cdef-manager.cc, cdef-manager.h, cdef-method.cc, cdef-method.h, cdef-object.cc, cdef-object.h, cdef-package.cc, cdef-package.h, cdef-property.cc, cdef-property.h, cdef-utils.cc, cdef-utils.h, ov-base-diag.cc, ov-base-int.cc, ov-base-mat.cc, ov-base-mat.h, ov-base-scalar.cc, ov-base.cc, ov-base.h, ov-bool-mat.cc, ov-bool-mat.h, ov-bool-sparse.cc, ov-bool.cc, ov-builtin.h, ov-cell.cc, ov-ch-mat.cc, ov-class.cc, ov-class.h, ov-classdef.cc, ov-classdef.h, ov-complex.cc, ov-cx-diag.cc, ov-cx-mat.cc, ov-cx-sparse.cc, ov-dld-fcn.cc, ov-dld-fcn.h, ov-fcn-handle.cc, ov-fcn-handle.h, ov-fcn.h, ov-float.cc, ov-flt-complex.cc, ov-flt-cx-diag.cc, ov-flt-cx-mat.cc, ov-flt-re-diag.cc, ov-flt-re-mat.cc, ov-flt-re-mat.h, ov-intx.h, ov-java.cc, ov-lazy-idx.cc, ov-legacy-range.cc, ov-magic-int.cc, ov-mex-fcn.cc, ov-mex-fcn.h, ov-null-mat.cc, ov-perm.cc, ov-range.cc, ov-re-diag.cc, ov-re-mat.cc, ov-re-mat.h, ov-re-sparse.cc, ov-scalar.cc, ov-str-mat.cc, ov-struct.cc, ov-typeinfo.cc, ov-typeinfo.h, ov-usr-fcn.cc, ov-usr-fcn.h, ov.cc, ov.h, ovl.h, octave.cc, octave.h, op-b-sbm.cc, op-bm-sbm.cc, op-cs-scm.cc, op-fm-fcm.cc, op-fs-fcm.cc, op-s-scm.cc, op-scm-cs.cc, op-scm-s.cc, op-sm-cs.cc, ops.h, anon-fcn-validator.cc, anon-fcn-validator.h, bp-table.cc, bp-table.h, comment-list.cc, comment-list.h, filepos.h, lex.h, oct-lvalue.cc, oct-lvalue.h, parse.h, profiler.cc, profiler.h, pt-anon-scopes.cc, pt-anon-scopes.h, pt-arg-list.cc, pt-arg-list.h, pt-args-block.cc, pt-args-block.h, pt-array-list.cc, pt-array-list.h, pt-assign.cc, pt-assign.h, pt-binop.cc, pt-binop.h, pt-bp.cc, pt-bp.h, pt-cbinop.cc, pt-cbinop.h, pt-cell.cc, pt-cell.h, pt-check.cc, pt-check.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-const.cc, pt-const.h, pt-decl.cc, pt-decl.h, pt-eval.cc, pt-eval.h, pt-except.cc, pt-except.h, pt-exp.cc, pt-exp.h, pt-fcn-handle.cc, pt-fcn-handle.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-jump.h, pt-loop.cc, pt-loop.h, pt-mat.cc, pt-mat.h, pt-misc.cc, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-tm-const.cc, pt-tm-const.h, pt-unop.cc, pt-unop.h, pt-walk.cc, pt-walk.h, pt.cc, pt.h, token.cc, token.h, Range.cc, Range.h, idx-vector.cc, idx-vector.h, range-fwd.h, CollocWt.cc, CollocWt.h, aepbalance.cc, aepbalance.h, chol.cc, chol.h, gepbalance.cc, gepbalance.h, gsvd.cc, gsvd.h, hess.cc, hess.h, lo-mappers.cc, lo-mappers.h, lo-specfun.cc, lo-specfun.h, lu.cc, lu.h, oct-convn.cc, oct-convn.h, oct-fftw.cc, oct-fftw.h, oct-norm.cc, oct-norm.h, oct-rand.cc, oct-rand.h, oct-spparms.cc, oct-spparms.h, qr.cc, qr.h, qrp.cc, qrp.h, randgamma.cc, randgamma.h, randmtzig.cc, randmtzig.h, randpoisson.cc, randpoisson.h, schur.cc, schur.h, sparse-chol.cc, sparse-chol.h, sparse-lu.cc, sparse-lu.h, sparse-qr.cc, sparse-qr.h, svd.cc, svd.h, child-list.cc, child-list.h, dir-ops.cc, dir-ops.h, file-ops.cc, file-ops.h, file-stat.cc, file-stat.h, lo-sysdep.cc, lo-sysdep.h, lo-sysinfo.cc, lo-sysinfo.h, mach-info.cc, mach-info.h, oct-env.cc, oct-env.h, oct-group.cc, oct-group.h, oct-password.cc, oct-password.h, oct-syscalls.cc, oct-syscalls.h, oct-time.cc, oct-time.h, oct-uname.cc, oct-uname.h, action-container.cc, action-container.h, base-list.h, cmd-edit.cc, cmd-edit.h, cmd-hist.cc, cmd-hist.h, f77-fcn.h, file-info.cc, file-info.h, lo-array-errwarn.cc, lo-array-errwarn.h, lo-hash.cc, lo-hash.h, lo-ieee.h, lo-regexp.cc, lo-regexp.h, lo-utils.cc, lo-utils.h, oct-base64.cc, oct-base64.h, oct-glob.cc, oct-glob.h, oct-inttypes.h, oct-mutex.cc, oct-mutex.h, oct-refcount.h, oct-shlib.cc, oct-shlib.h, oct-sparse.cc, oct-sparse.h, oct-string.h, octave-preserve-stream-state.h, pathsearch.cc, pathsearch.h, quit.cc, quit.h, unwind-prot.cc, unwind-prot.h, url-transfer.cc, url-transfer.h: Re-indent code after switch to using namespace macros.
author Rik <rik@octave.org>
date Thu, 01 Dec 2022 18:02:15 -0800
parents e88a07dec498
children 597f3ee61a48
line wrap: on
line source

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1996-2022 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Octave is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include "Cell.h"
#include "error.h"
#include "interpreter-private.h"
#include "oct-map.h"
#include "ovl.h"
#include "oct-lvalue.h"
#include "ov.h"
#include "pt-arg-list.h"
#include "pt-eval.h"
#include "pt-id.h"
#include "pt-idx.h"
#include "utils.h"
#include "variables.h"
#include "errwarn.h"

OCTAVE_BEGIN_NAMESPACE(octave)

// Index expressions.

tree_index_expression::tree_index_expression (int l, int c)
  : tree_expression (l, c), m_expr (nullptr), m_args (0), m_type (),
    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false) { }

tree_index_expression::tree_index_expression (tree_expression *e,
    tree_argument_list *lst,
    int l, int c, char t)
  : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
{
  append (lst, t);
}

tree_index_expression::tree_index_expression (tree_expression *e,
    const std::string& n,
    int l, int c)
  : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
{
  append (n);
}

tree_index_expression::tree_index_expression (tree_expression *e,
    tree_expression *df,
    int l, int c)
  : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
{
  append (df);
}

tree_index_expression *
tree_index_expression::append (tree_argument_list *lst, char t)
{
  m_args.push_back (lst);
  m_type.append (1, t);
  m_arg_nm.push_back (lst ? lst->get_arg_names () : string_vector ());
  m_dyn_field.push_back (static_cast<tree_expression *> (nullptr));

  if (lst && lst->has_magic_tilde ())
    error ("invalid use of empty argument (~) in index expression");

  return this;
}

tree_index_expression *
tree_index_expression::append (const std::string& n)
{
  m_args.push_back (static_cast<tree_argument_list *> (nullptr));
  m_type += '.';
  m_arg_nm.push_back (n);
  m_dyn_field.push_back (static_cast<tree_expression *> (nullptr));

  return this;
}

tree_index_expression *
tree_index_expression::append (tree_expression *df)
{
  m_args.push_back (static_cast<tree_argument_list *> (nullptr));
  m_type += '.';
  m_arg_nm.push_back ("");
  m_dyn_field.push_back (df);

  return this;
}

tree_index_expression::~tree_index_expression (void)
{
  delete m_expr;

  while (! m_args.empty ())
    {
      auto p = m_args.begin ();
      delete *p;
      m_args.erase (p);
    }

  while (! m_dyn_field.empty ())
    {
      auto p = m_dyn_field.begin ();
      delete *p;
      m_dyn_field.erase (p);
    }
}

// This is useful for printing the name of the variable in an indexed
// assignment.

std::string
tree_index_expression::name (void) const
{
  return m_expr->name ();
}

std::string
tree_index_expression::get_struct_index
(tree_evaluator& tw,
 std::list<string_vector>::const_iterator p_arg_nm,
 std::list<tree_expression *>::const_iterator p_dyn_field) const
{
  std::string fn = (*p_arg_nm)(0);

  if (fn.empty ())
    {
      tree_expression *df = *p_dyn_field;

      if (df)
        {
          octave_value t = df->evaluate (tw);

          fn = t.xstring_value ("dynamic structure field names must be strings");
        }
      else
        panic_impossible ();
    }

  return fn;
}

octave_lvalue
tree_index_expression::lvalue (tree_evaluator& tw)
{
  std::list<octave_value_list> idx;

  int n = m_args.size ();

  auto p_args = m_args.begin ();
  auto p_arg_nm = m_arg_nm.begin ();
  auto p_dyn_field = m_dyn_field.begin ();

  octave_lvalue retval = m_expr->lvalue (tw);

  unwind_action
  act ([&tw] (const octave_value& val,
              const std::string& index_type,
              const std::list<octave_value_list>& index_list)
  {
    tw.set_indexed_object (val);
    tw.set_index_list (index_type, index_list);
  }, tw.indexed_object (), tw.index_type (), tw.index_list ());

  tw.set_indexed_object (retval.value ());
  tw.clear_index_list ();

  for (int i = 0; i < n; i++)
    {
      switch (m_type[i])
        {
        case '(':
          {
            octave_value_list tidx = tw.make_value_list (*p_args, *p_arg_nm);

            tw.append_index_list ('(', tidx);
            idx.push_back (tidx);
          }
          break;

        case '{':
          {
            octave_value_list tidx = tw.make_value_list (*p_args, *p_arg_nm);

            tw.append_index_list ('{', tidx);
            idx.push_back (tidx);
          }
          break;

        case '.':
          {
            octave_value tidx = get_struct_index (tw, p_arg_nm, p_dyn_field);

            tw.append_index_list ('.', tidx);
            idx.push_back (tidx);
          }
          break;

        default:
          panic_impossible ();
        }

      if (idx.back ().empty ())
        error ("invalid empty index list");

      p_args++;
      p_arg_nm++;
      p_dyn_field++;
    }

  retval.set_index (m_type, idx);

  return retval;
}

tree_index_expression *
tree_index_expression::dup (symbol_scope& scope) const
{
  tree_index_expression *new_idx_expr
    = new tree_index_expression (line (), column ());

  new_idx_expr->m_expr = (m_expr ? m_expr->dup (scope) : nullptr);

  std::list<tree_argument_list *> new_args;

  for (const tree_argument_list *elt : m_args)
    new_args.push_back (elt ? elt->dup (scope) : nullptr);

  new_idx_expr->m_args = new_args;

  new_idx_expr->m_type = m_type;

  new_idx_expr->m_arg_nm = m_arg_nm;

  std::list<tree_expression *> new_dyn_field;

  for (const tree_expression *elt : m_dyn_field)
    new_dyn_field.push_back (elt ? elt->dup (scope) : nullptr);

  new_idx_expr->m_dyn_field = new_dyn_field;

  new_idx_expr->copy_base (*this);

  return new_idx_expr;
}

// Unlike Matlab, which does not allow the result of a function call
// or array indexing expression to be further indexed, Octave attempts
// to handle arbitrary index expressions.  For example, Octave allows
// expressions like
//
//   svd (rand (10))(1:5)
//
// Although octave_value objects may contain function objects, no
// indexing operation or function call is supposed to return them
// directly.  Instead, the language is supposed to only allow function
// objects to be stored as function handles (named or anonymous) or as
// inline functions.  The only place a function object should appear
// directly is if the symbol stored in a tree_identifier object
// resolves to a function.  This means that the only place we need to
// look for functions is in the first element of the index
// expression.
//
// Steps:
//
//  * Obtain the initial value from the expression component of the
//    tree_index_expression object.  If it is a tree_identifier object
//    indexed by '(args)' and the identifier is not a variable, then
//    perform a function call.  Use the (optional) arguments to perform
//    the function lookup so we choose the correct function or class
//    method to call.  Otherwise, evaluate the first expression
//    without any additional arguments.
//
//  * Iterate over the remaining elements of the index expression and
//    call the octave_value::subsref method.  If indexing a class or
//    classdef object, build up a list of indices for a call to the
//    subsref method for the object.  Otherwise, use the result of
//    each temporary evaluation for the next index element.
//
//  * If not indexing a class or classdef object and any partial
//    expression evaluation produces a class or classdef object, then
//    build up a complete argument list from that point on for a final
//    subsref call for that object.
//
//    Multiple partial evaluations may be required.  For example,
//    given a class or classdef object X, then for the expression
//
//      x.a{end}(2:end).b
//
//    we must evaluate x.a to obtain the size for the first {end}
//    expression, then we must evaluate x.a{end} to obtain the size
//    for the second (2:end) expression.  Finally, the complete
//    expression may be evaluated.
//
//    If X is a cell array in the above expression, and none of the
//    intermediate evaluations produces a class or classdef object,
//    then the evaluation is performed as the following series of
//    steps
//
//      tmp = x.a
//      tmp = tmp{end}
//      tmp = tmp(2:end)
//      result = tmp.b
//
//    If any of the partial evaluations produces a class or classdef
//    object, then the subsref method for that object is called as
//    described above.  For example, suppose x.a produces a classdef
//    object.  Then the evaluation is performed as the following
//    series of steps
//
//      base_expr = tmp = x.a
//      tmp = base_expr{end}
//      base_expr{end}(2:end).b
//
//    In the last two steps, the partial value computed in the
//    previous step is used to determine the value of END.

octave_value_list
tree_index_expression::evaluate_n (tree_evaluator& tw, int nargout)
{
  octave_value_list retval;

  panic_if (m_args.empty ());

  auto p_args = m_args.begin ();
  auto p_arg_nm = m_arg_nm.begin ();
  auto p_dyn_field = m_dyn_field.begin ();

  int n = m_args.size ();
  int beg = 0;

  octave_value base_expr_val;

  if (m_expr->is_identifier () && m_type[beg] == '(')
    {
      tree_identifier *id = dynamic_cast<tree_identifier *> (m_expr);

      bool is_var = tw.is_variable (m_expr);

      std::string nm =  id->name ();

      if (is_var && is_word_list_cmd ())
        {
          bool maybe_binary_op = false;
          if ((*p_args) && (*p_args)->length () > 0)
            {
              // check if first character of first argument might be (the
              // start of) a binary operator
              std::string ops = "+-*/\\.^|&";
              string_vector arg_list = (*p_args)->get_arg_names ();
              if (! arg_list.isempty ()
                  && (ops.find (arg_list(0)[1]) != std::string::npos))
                maybe_binary_op = true;
            }

          std::string advice;
          if (maybe_binary_op)
            advice = "\nCheck whitespace around potential binary operator.";

          error ("variable \"%s\" used as function in command style expression%s",
                 nm.c_str (), advice.c_str ());
        }

      if (! is_var)
        {
          octave_value_list first_args;

          tree_argument_list *al = *p_args;

          if (al && al->length () > 0)
            {
              unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
              {
                tw.set_lvalue_list (lvl);
              }, tw.lvalue_list ());

              tw.set_lvalue_list (nullptr);

              string_vector anm = *p_arg_nm;

              first_args = tw.convert_to_const_vector (al);

              first_args.stash_name_tags (anm);
            }

          interpreter& interp = tw.get_interpreter ();

          symbol_table& symtab = interp.get_symbol_table ();

          octave_value val = symtab.find_function (nm, first_args);

          octave_function *fcn = nullptr;

          if (val.is_function ())
            fcn = val.function_value (true);

          if (fcn)
            {
              try
                {
                  retval = fcn->call (tw, nargout, first_args);
                }
              catch (index_exception& ie)
                {
                  tw.final_index_error (ie, m_expr);
                }

              beg++;
              p_args++;
              p_arg_nm++;
              p_dyn_field++;

              if (n > beg)
                {
                  // More indices to follow.  Silently ignore
                  // extra output values.

                  if (retval.length () == 0)
                    error ("indexing undefined value");
                  else
                    base_expr_val = retval(0);
                }
              else
                {
                  // No more indices, so we are done.

                  // See note at end of function about deleting
                  // temporaries prior to pushing result.

                  base_expr_val = octave_value ();
                  first_args = octave_value_list ();

                  return retval;
                }
            }
        }
    }

  if (base_expr_val.is_undefined ())
    base_expr_val = m_expr->evaluate (tw);

  // If we are indexing an object or looking at something like
  //
  //   classname.static_function (args, ...);
  //
  // then we'll just build a complete index list for one big subsref
  // call.  If the expression we are indexing is a classname then
  // base_expr_val will be an octave_classdef_meta object.  If we have
  // files in a +packagename folder, they will also be an
  // octave_classdef_meta object, but we don't want to index them.

  std::list<octave_value_list> idx_list;

  {
    // Note: need new scope so that the following unwind action will
    // happen before we perform the final indexing for objects (for
    // example).

    unwind_action
    act ([&tw] (const octave_value& val,
                const std::string& index_type,
                const std::list<octave_value_list>& index_list)
    {
      tw.set_indexed_object (val);
      tw.set_index_list (index_type, index_list);
    },
    tw.indexed_object (),
    tw.index_type (), tw.index_list ());

    tw.set_indexed_object ();
    tw.clear_index_list ();

    bool indexing_object = (base_expr_val.isobject ()
                            || base_expr_val.isjava ()
                            || (base_expr_val.is_classdef_meta ()
                                && ! base_expr_val.is_package ()));

    octave_value partial_expr_val = base_expr_val;

    for (int i = beg; i < n; i++)
      {
        if (i > beg)
          {
            if (! indexing_object)
              {
                // Evaluate what we have so far.

                try
                  {
                    // Silently ignore extra output values.

                    octave_value_list tmp_list
                      = base_expr_val.subsref (m_type.substr (beg, i-beg),
                                               idx_list, nargout);

                    partial_expr_val
                      = tmp_list.length () ? tmp_list(0) : octave_value ();

                    base_expr_val = partial_expr_val;

                    if (partial_expr_val.is_cs_list ())
                      err_indexed_cs_list ();

                    retval = partial_expr_val;

                    beg = i;
                    idx_list.clear ();
                    tw.clear_index_list ();

                    if (partial_expr_val.isobject ()
                        || partial_expr_val.isjava ()
                        || (partial_expr_val.is_classdef_meta ()
                            && ! partial_expr_val.is_package ()))
                      {
                        // Found an object, so now we'll build up
                        // complete index list for one big subsref
                        // call from this point on.

                        // FIXME: is is also possible to have a
                        // static method call buried somewhere in
                        // the depths of a complex indexing
                        // expression so that we would also need to
                        // check for an octave_classdef_meta object
                        // here?

                        indexing_object = true;
                      }
                  }
                catch (index_exception& ie)
                  {
                    tw.final_index_error (ie, m_expr);
                  }
              }
          }

        tw.set_indexed_object (partial_expr_val);

        switch (m_type[i])
          {
          case '(':
            {
              octave_value_list tmp = tw.make_value_list (*p_args, *p_arg_nm);
              tw.append_index_list ('(', tmp);
              idx_list.push_back (tmp);
            }
            break;

          case '{':
            {
              octave_value_list tmp = tw.make_value_list (*p_args, *p_arg_nm);
              tw.append_index_list ('{', tmp);
              idx_list.push_back (tmp);
            }
            break;

          case '.':
            {
              octave_value tmp = get_struct_index (tw, p_arg_nm, p_dyn_field);
              tw.append_index_list ('.', tmp);
              idx_list.push_back (tmp);
            }
            break;

          default:
            panic_impossible ();
          }

        p_args++;
        p_arg_nm++;
        p_dyn_field++;
      }
  }

  // If ! idx_list.empty () that means we still have stuff to index
  // otherwise they would have been dealt with and idx_list would have
  // been emptied.
  if (! idx_list.empty ())
    {
      // This is for +package and other classdef_meta objects
      if (! base_expr_val.is_function ()
          || base_expr_val.is_classdef_meta ())
        {
          try
            {
              retval = base_expr_val.subsref (m_type.substr (beg, n-beg),
                                              idx_list, nargout);
              beg = n;
              idx_list.clear ();
            }
          catch (index_exception& ie)
            {
              tw.final_index_error (ie, m_expr);
            }
        }
      else
        {
          // FIXME: we want this to only be a superclass constructor
          // call Should we actually make a check for this or are all
          // other types of calls already dealt with?

          octave_function *fcn = base_expr_val.function_value ();

          if (fcn)
            {
              try
                {
                  // FIXME: is it possible for the IDX_LIST to have
                  // more than one element here?  Do we need to check?

                  octave_value_list final_args;

                  if (idx_list.size () != 1)
                    error ("unexpected extra index at end of expression");

                  if (m_type[beg] != '(')
                    error ("invalid index type '%c' for function call",
                           m_type[beg]);

                  final_args = idx_list.front ();

                  // FIXME: Do we ever need the names of the arguments
                  // passed to FCN here?

                  retval = fcn->call (tw, nargout, final_args);
                }
              catch (index_exception& ie)
                {
                  tw.final_index_error (ie, m_expr);
                }
            }
        }
    }

  // FIXME: when can the following happen?  In what case does indexing
  // result in a value that is a function?  Classdef method calls?
  // Something else?

  octave_value val = (retval.length () ? retval(0) : octave_value ());

  if (val.is_function ())
    {
      octave_function *fcn = val.function_value (true);

      if (fcn)
        {
          octave_value_list final_args;

          if (! idx_list.empty ())
            {
              if (n - beg != 1)
                error ("unexpected extra index at end of expression");

              if (m_type[beg] != '(')
                error ("invalid index type '%c' for function call",
                       m_type[beg]);

              final_args = idx_list.front ();
            }

          retval = fcn->call (tw, nargout, final_args);
        }
    }

  // Delete any temporary values prior to returning so that
  // destructors for any temporary classdef handle objects will be
  // called before we return.

  idx_list.clear ();
  base_expr_val = octave_value ();
  val = octave_value ();

  return retval;
}

OCTAVE_END_NAMESPACE(octave)

/*
%!test
%! clear x;
%! clear y;
%! y = 3;
%! x(y(end)) = 1;
%! assert (x, [0, 0, 1]);
%! clear x;
%! clear y;
%! y = {3};
%! x(y{end}) = 1;
%! assert (x, [0, 0, 1]);

%!test
%! x = {1, 2, 3};
%! [x{:}] = deal (4, 5, 6);
%! assert (x, {4, 5, 6});

%!test
%! [x.a, x.b.c] = deal (1, 2);
%! assert (x.a == 1 && x.b.c == 2);

%!test
%! [x.a, x(2).b] = deal (1, 2);
%! assert (x(1).a == 1 && isempty (x(2).a) && isempty (x(1).b) && x(2).b == 2);

%!test
%! x = struct (zeros (0, 1), {"a", "b"});
%! x(2).b = 1;
%! assert (x(2).b == 1);

%!test
%! x = struct (zeros (0, 1), {"a", "b"});
%! x(2).b = 1;
%! assert (x(2).b == 1);
*/