GNU Octave 7.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
pt-idx.cc
Go to the documentation of this file.
1////////////////////////////////////////////////////////////////////////
2//
3// Copyright (C) 1996-2022 The Octave Project Developers
4//
5// See the file COPYRIGHT.md in the top-level directory of this
6// distribution or <https://octave.org/copyright/>.
7//
8// This file is part of Octave.
9//
10// Octave is free software: you can redistribute it and/or modify it
11// under the terms of the GNU General Public License as published by
12// the Free Software Foundation, either version 3 of the License, or
13// (at your option) any later version.
14//
15// Octave is distributed in the hope that it will be useful, but
16// WITHOUT ANY WARRANTY; without even the implied warranty of
17// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18// GNU General Public License for more details.
19//
20// You should have received a copy of the GNU General Public License
21// along with Octave; see the file COPYING. If not, see
22// <https://www.gnu.org/licenses/>.
23//
24////////////////////////////////////////////////////////////////////////
25
26#if defined (HAVE_CONFIG_H)
27# include "config.h"
28#endif
29
30#include "Cell.h"
31#include "error.h"
32#include "interpreter-private.h"
33#include "oct-map.h"
34#include "ovl.h"
35#include "oct-lvalue.h"
36#include "ov.h"
37#include "pt-arg-list.h"
38#include "pt-eval.h"
39#include "pt-id.h"
40#include "pt-idx.h"
41#include "utils.h"
42#include "variables.h"
43#include "errwarn.h"
44
45namespace octave
46{
47 // Index expressions.
48
50 : tree_expression (l, c), m_expr (nullptr), m_args (0), m_type (),
51 m_arg_nm (), m_dyn_field (), m_word_list_cmd (false) { }
52
55 int l, int c, char t)
56 : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
57 m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
58 {
59 append (lst, t);
60 }
61
63 const std::string& n,
64 int l, int c)
65 : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
66 m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
67 {
68 append (n);
69 }
70
73 int l, int c)
74 : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
75 m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
76 {
77 append (df);
78 }
79
82 {
83 m_args.push_back (lst);
84 m_type.append (1, t);
85 m_arg_nm.push_back (lst ? lst->get_arg_names () : string_vector ());
86 m_dyn_field.push_back (static_cast<tree_expression *> (nullptr));
87
88 if (lst && lst->has_magic_tilde ())
89 error ("invalid use of empty argument (~) in index expression");
90
91 return this;
92 }
93
95 tree_index_expression::append (const std::string& n)
96 {
97 m_args.push_back (static_cast<tree_argument_list *> (nullptr));
98 m_type += '.';
99 m_arg_nm.push_back (n);
100 m_dyn_field.push_back (static_cast<tree_expression *> (nullptr));
101
102 return this;
103 }
104
107 {
108 m_args.push_back (static_cast<tree_argument_list *> (nullptr));
109 m_type += '.';
110 m_arg_nm.push_back ("");
111 m_dyn_field.push_back (df);
112
113 return this;
114 }
115
117 {
118 delete m_expr;
119
120 while (! m_args.empty ())
121 {
122 auto p = m_args.begin ();
123 delete *p;
124 m_args.erase (p);
125 }
126
127 while (! m_dyn_field.empty ())
128 {
129 auto p = m_dyn_field.begin ();
130 delete *p;
131 m_dyn_field.erase (p);
132 }
133 }
134
135 // This is useful for printing the name of the variable in an indexed
136 // assignment.
137
138 std::string
140 {
141 return m_expr->name ();
142 }
143
144 std::string
146 (tree_evaluator& tw,
147 std::list<string_vector>::const_iterator p_arg_nm,
148 std::list<tree_expression *>::const_iterator p_dyn_field) const
149 {
150 std::string fn = (*p_arg_nm)(0);
151
152 if (fn.empty ())
153 {
154 tree_expression *df = *p_dyn_field;
155
156 if (df)
157 {
158 octave_value t = df->evaluate (tw);
159
160 fn = t.xstring_value ("dynamic structure field names must be strings");
161 }
162 else
164 }
165
166 return fn;
167 }
168
171 {
172 std::list<octave_value_list> idx;
173
174 int n = m_args.size ();
175
176 auto p_args = m_args.begin ();
177 auto p_arg_nm = m_arg_nm.begin ();
178 auto p_dyn_field = m_dyn_field.begin ();
179
180 octave_lvalue retval = m_expr->lvalue (tw);
181
183 act ([&tw] (const octave_value& val,
184 const std::string& index_type,
185 const std::list<octave_value_list>& index_list)
186 {
187 tw.set_indexed_object (val);
188 tw.set_index_list (index_type, index_list);
189 }, tw.indexed_object (), tw.index_type (), tw.index_list ());
190
191 tw.set_indexed_object (retval.value ());
192 tw.clear_index_list ();
193
194 for (int i = 0; i < n; i++)
195 {
196 switch (m_type[i])
197 {
198 case '(':
199 {
200 octave_value_list tidx = tw.make_value_list (*p_args, *p_arg_nm);
201
202 tw.append_index_list ('(', tidx);
203 idx.push_back (tidx);
204 }
205 break;
206
207 case '{':
208 {
209 octave_value_list tidx = tw.make_value_list (*p_args, *p_arg_nm);
210
211 tw.append_index_list ('{', tidx);
212 idx.push_back (tidx);
213 }
214 break;
215
216 case '.':
217 {
218 octave_value tidx = get_struct_index (tw, p_arg_nm, p_dyn_field);
219
220 tw.append_index_list ('.', tidx);
221 idx.push_back (tidx);
222 }
223 break;
224
225 default:
227 }
228
229 if (idx.back ().empty ())
230 error ("invalid empty index list");
231
232 p_args++;
233 p_arg_nm++;
234 p_dyn_field++;
235 }
236
237 retval.set_index (m_type, idx);
238
239 return retval;
240 }
241
244 {
245 tree_index_expression *new_idx_expr
246 = new tree_index_expression (line (), column ());
247
248 new_idx_expr->m_expr = (m_expr ? m_expr->dup (scope) : nullptr);
249
250 std::list<tree_argument_list *> new_args;
251
252 for (const tree_argument_list *elt : m_args)
253 new_args.push_back (elt ? elt->dup (scope) : nullptr);
254
255 new_idx_expr->m_args = new_args;
256
257 new_idx_expr->m_type = m_type;
258
259 new_idx_expr->m_arg_nm = m_arg_nm;
260
261 std::list<tree_expression *> new_dyn_field;
262
263 for (const tree_expression *elt : m_dyn_field)
264 new_dyn_field.push_back (elt ? elt->dup (scope) : nullptr);
265
266 new_idx_expr->m_dyn_field = new_dyn_field;
267
268 new_idx_expr->copy_base (*this);
269
270 return new_idx_expr;
271 }
272
273 // Unlike Matlab, which does not allow the result of a function call
274 // or array indexing expression to be further indexed, Octave attempts
275 // to handle arbitrary index expressions. For example, Octave allows
276 // expressions like
277 //
278 // svd (rand (10))(1:5)
279 //
280 // Although octave_value objects may contain function objects, no
281 // indexing operation or function call is supposed to return them
282 // directly. Instead, the language is supposed to only allow function
283 // objects to be stored as function handles (named or anonymous) or as
284 // inline functions. The only place a function object should appear
285 // directly is if the symbol stored in a tree_identifier object
286 // resolves to a function. This means that the only place we need to
287 // look for functions is in the first element of the index
288 // expression.
289 //
290 // Steps:
291 //
292 // * Obtain the initial value from the expression component of the
293 // tree_index_expression object. If it is a tree_identifier object
294 // indexed by '(args)' and the identifier is not a variable, then
295 // perform a function call. Use the (optional) arguments to perform
296 // the function lookup so we choose the correct function or class
297 // method to call. Otherwise, evaluate the first expression
298 // without any additional arguments.
299 //
300 // * Iterate over the remaining elements of the index expression and
301 // call the octave_value::subsref method. If indexing a class or
302 // classdef object, build up a list of indices for a call to the
303 // subsref method for the object. Otherwise, use the result of
304 // each temporary evaluation for the next index element.
305 //
306 // * If not indexing a class or classdef object and any partial
307 // expression evaluation produces a class or classdef object, then
308 // build up a complete argument list from that point on for a final
309 // subsref call for that object.
310 //
311 // Multiple partial evaluations may be required. For example,
312 // given a class or classdef object X, then for the expression
313 //
314 // x.a{end}(2:end).b
315 //
316 // we must evaluate x.a to obtain the size for the first {end}
317 // expression, then we must evaluate x.a{end} to obtain the size
318 // for the second (2:end) expression. Finally, the complete
319 // expression may be evaluated.
320 //
321 // If X is a cell array in the above expression, and none of the
322 // intermediate evaluations produces a class or classdef object,
323 // then the evaluation is performed as the following series of
324 // steps
325 //
326 // tmp = x.a
327 // tmp = tmp{end}
328 // tmp = tmp(2:end)
329 // result = tmp.b
330 //
331 // If any of the partial evaluations produces a class or classdef
332 // object, then the subsref method for that object is called as
333 // described above. For example, suppose x.a produces a classdef
334 // object. Then the evaluation is performed as the following
335 // series of steps
336 //
337 // base_expr = tmp = x.a
338 // tmp = base_expr{end}
339 // base_expr{end}(2:end).b
340 //
341 // In the last two steps, the partial value computed in the
342 // previous step is used to determine the value of END.
343
346 {
347 octave_value_list retval;
348
349 assert (! m_args.empty ());
350
351 auto p_args = m_args.begin ();
352 auto p_arg_nm = m_arg_nm.begin ();
353 auto p_dyn_field = m_dyn_field.begin ();
354
355 int n = m_args.size ();
356 int beg = 0;
357
358 octave_value base_expr_val;
359
360 if (m_expr->is_identifier () && m_type[beg] == '(')
361 {
362 tree_identifier *id = dynamic_cast<tree_identifier *> (m_expr);
363
364 bool is_var = tw.is_variable (m_expr);
365
366 std::string nm = id->name ();
367
368 if (is_var && is_word_list_cmd ())
369 error ("%s used as variable and later as function", nm.c_str ());
370
371 if (! is_var)
372 {
373 octave_value_list first_args;
374
375 tree_argument_list *al = *p_args;
376
377 if (al && al->length () > 0)
378 {
379 unwind_action act ([&tw] (const std::list<octave_lvalue> *lvl)
380 {
381 tw.set_lvalue_list (lvl);
382 }, tw.lvalue_list ());
383
384 tw.set_lvalue_list (nullptr);
385
386 string_vector anm = *p_arg_nm;
387
388 first_args = tw.convert_to_const_vector (al);
389
390 first_args.stash_name_tags (anm);
391 }
392
393 interpreter& interp = tw.get_interpreter ();
394
395 symbol_table& symtab = interp.get_symbol_table ();
396
397 octave_value val = symtab.find_function (nm, first_args);
398
399 octave_function *fcn = nullptr;
400
401 if (val.is_function ())
402 fcn = val.function_value (true);
403
404 if (fcn)
405 {
406 try
407 {
408 retval = fcn->call (tw, nargout, first_args);
409 }
410 catch (index_exception& ie)
411 {
412 tw.final_index_error (ie, m_expr);
413 }
414
415 beg++;
416 p_args++;
417 p_arg_nm++;
418 p_dyn_field++;
419
420 if (n > beg)
421 {
422 // More indices to follow. Silently ignore
423 // extra output values.
424
425 if (retval.length () == 0)
426 error ("indexing undefined value");
427 else
428 base_expr_val = retval(0);
429 }
430 else
431 {
432 // No more indices, so we are done.
433
434 // See note at end of function about deleting
435 // temporaries prior to pushing result.
436
437 base_expr_val = octave_value ();
438 first_args = octave_value_list ();
439
440 return retval;
441 }
442 }
443 }
444 }
445
446 if (base_expr_val.is_undefined ())
447 base_expr_val = m_expr->evaluate (tw);
448
449 // If we are indexing an object or looking at something like
450 //
451 // classname.static_function (args, ...);
452 //
453 // then we'll just build a complete index list for one big subsref
454 // call. If the expression we are indexing is a classname then
455 // base_expr_val will be an octave_classdef_meta object. If we have
456 // files in a +packagename folder, they will also be an
457 // octave_classdef_meta object, but we don't want to index them.
458
459 std::list<octave_value_list> idx_list;
460
461 {
462 // Note: need new scope so that the following unwind action will
463 // happen before we perform the final indexing for objects (for
464 // example).
465
467 act ([&tw] (const octave_value& val,
468 const std::string& index_type,
469 const std::list<octave_value_list>& index_list)
470 {
471 tw.set_indexed_object (val);
472 tw.set_index_list (index_type, index_list);
473 },
474 tw.indexed_object (),
475 tw.index_type (), tw.index_list ());
476
477 tw.set_indexed_object ();
478 tw.clear_index_list ();
479
480 bool indexing_object = (base_expr_val.isobject ()
481 || base_expr_val.isjava ()
482 || (base_expr_val.is_classdef_meta ()
483 && ! base_expr_val.is_package ()));
484
485 octave_value partial_expr_val = base_expr_val;
486
487 for (int i = beg; i < n; i++)
488 {
489 if (i > beg)
490 {
491 if (! indexing_object)
492 {
493 // Evaluate what we have so far.
494
495 try
496 {
497 // Silently ignore extra output values.
498
499 octave_value_list tmp_list
500 = base_expr_val.subsref (m_type.substr (beg, i-beg),
501 idx_list, nargout);
502
503 partial_expr_val
504 = tmp_list.length () ? tmp_list(0) : octave_value ();
505
506 base_expr_val = partial_expr_val;
507
508 if (partial_expr_val.is_cs_list ())
510
511 retval = partial_expr_val;
512
513 beg = i;
514 idx_list.clear ();
515 tw.clear_index_list ();
516
517 if (partial_expr_val.isobject ()
518 || partial_expr_val.isjava ()
519 || (partial_expr_val.is_classdef_meta ()
520 && ! partial_expr_val.is_package ()))
521 {
522 // Found an object, so now we'll build up
523 // complete index list for one big subsref
524 // call from this point on.
525
526 // FIXME: is is also possible to have a
527 // static method call buried somewhere in
528 // the depths of a complex indexing
529 // expression so that we would also need to
530 // check for an octave_classdef_meta object
531 // here?
532
533 indexing_object = true;
534 }
535 }
536 catch (index_exception& ie)
537 {
538 tw.final_index_error (ie, m_expr);
539 }
540 }
541 }
542
543 tw.set_indexed_object (partial_expr_val);
544
545 switch (m_type[i])
546 {
547 case '(':
548 {
549 octave_value_list tmp = tw.make_value_list (*p_args, *p_arg_nm);
550 tw.append_index_list ('(', tmp);
551 idx_list.push_back (tmp);
552 }
553 break;
554
555 case '{':
556 {
557 octave_value_list tmp = tw.make_value_list (*p_args, *p_arg_nm);
558 tw.append_index_list ('{', tmp);
559 idx_list.push_back (tmp);
560 }
561 break;
562
563 case '.':
564 {
565 octave_value tmp = get_struct_index (tw, p_arg_nm, p_dyn_field);
566 tw.append_index_list ('.', tmp);
567 idx_list.push_back (tmp);
568 }
569 break;
570
571 default:
573 }
574
575 p_args++;
576 p_arg_nm++;
577 p_dyn_field++;
578 }
579 }
580
581 // If ! idx_list.empty () that means we still have stuff to index
582 // otherwise they would have been dealt with and idx_list would have
583 // been emptied.
584 if (! idx_list.empty ())
585 {
586 // This is for +package and other classdef_meta objects
587 if (! base_expr_val.is_function ()
588 || base_expr_val.is_classdef_meta ())
589 {
590 try
591 {
592 retval = base_expr_val.subsref (m_type.substr (beg, n-beg),
593 idx_list, nargout);
594 beg = n;
595 idx_list.clear ();
596 }
597 catch (index_exception& ie)
598 {
599 tw.final_index_error (ie, m_expr);
600 }
601 }
602 else
603 {
604 // FIXME: we want this to only be a superclass constructor
605 // call Should we actually make a check for this or are all
606 // other types of calls already dealt with?
607
608 octave_function *fcn = base_expr_val.function_value ();
609
610 if (fcn)
611 {
612 try
613 {
614 // FIXME: is it possible for the IDX_LIST to have
615 // more than one element here? Do we need to check?
616
617 octave_value_list final_args;
618
619 if (idx_list.size () != 1)
620 error ("unexpected extra index at end of expression");
621
622 if (m_type[beg] != '(')
623 error ("invalid index type '%c' for function call",
624 m_type[beg]);
625
626 final_args = idx_list.front ();
627
628 // FIXME: Do we ever need the names of the arguments
629 // passed to FCN here?
630
631 retval = fcn->call (tw, nargout, final_args);
632 }
633 catch (index_exception& ie)
634 {
635 tw.final_index_error (ie, m_expr);
636 }
637 }
638 }
639 }
640
641 // FIXME: when can the following happen? In what case does indexing
642 // result in a value that is a function? Classdef method calls?
643 // Something else?
644
645 octave_value val = (retval.length () ? retval(0) : octave_value ());
646
647 if (val.is_function ())
648 {
649 octave_function *fcn = val.function_value (true);
650
651 if (fcn)
652 {
653 octave_value_list final_args;
654
655 if (! idx_list.empty ())
656 {
657 if (n - beg != 1)
658 error ("unexpected extra index at end of expression");
659
660 if (m_type[beg] != '(')
661 error ("invalid index type '%c' for function call",
662 m_type[beg]);
663
664 final_args = idx_list.front ();
665 }
666
667 retval = fcn->call (tw, nargout, final_args);
668 }
669 }
670
671 // Delete any temporary values prior to returning so that
672 // destructors for any temporary classdef handle objects will be
673 // called before we return.
674
675 idx_list.clear ();
676 base_expr_val = octave_value ();
677 val = octave_value ();
678
679 return retval;
680 }
681}
682
683/*
684%!test
685%! clear x;
686%! clear y;
687%! y = 3;
688%! x(y(end)) = 1;
689%! assert (x, [0, 0, 1]);
690%! clear x;
691%! clear y;
692%! y = {3};
693%! x(y{end}) = 1;
694%! assert (x, [0, 0, 1]);
695
696%!test
697%! x = {1, 2, 3};
698%! [x{:}] = deal (4, 5, 6);
699%! assert (x, {4, 5, 6});
700
701%!test
702%! [x.a, x.b.c] = deal (1, 2);
703%! assert (x.a == 1 && x.b.c == 2);
704
705%!test
706%! [x.a, x(2).b] = deal (1, 2);
707%! assert (x(1).a == 1 && isempty (x(2).a) && isempty (x(1).b) && x(2).b == 2);
708
709%!test
710%! x = struct (zeros (0, 1), {"a", "b"});
711%! x(2).b = 1;
712%! assert (x(2).b == 1);
713
714%!test
715%! x = struct (zeros (0, 1), {"a", "b"});
716%! x(2).b = 1;
717%! assert (x(2).b == 1);
718*/
symbol_table & get_symbol_table(void)
Definition: interpreter.h:296
std::size_t length(void) const
Definition: base-list.h:53
void set_index(const std::string &t, const std::list< octave_value_list > &i)
Definition: oct-lvalue.cc:171
octave_value value(void) const
Definition: oct-lvalue.cc:215
string_vector get_arg_names(void) const
Definition: pt-arg-list.cc:101
bool has_magic_tilde(void) const
Definition: pt-arg-list.h:72
virtual bool is_identifier(void) const
Definition: pt-exp.h:70
virtual void copy_base(const tree_expression &e)
Definition: pt-exp.h:133
virtual octave_lvalue lvalue(tree_evaluator &)
Definition: pt-exp.cc:43
virtual std::string name(void) const
Definition: pt-exp.h:105
virtual tree_expression * dup(symbol_scope &scope) const =0
virtual octave_value evaluate(tree_evaluator &tw, int nargout=1)=0
bool is_word_list_cmd(void) const
Definition: pt-idx.h:96
tree_index_expression * dup(symbol_scope &scope) const
Definition: pt-idx.cc:243
std::string 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
Definition: pt-idx.cc:146
tree_index_expression(tree_expression *e=nullptr, tree_argument_list *lst=nullptr, int l=-1, int c=-1, char t='(')
Definition: pt-idx.cc:53
octave_lvalue lvalue(tree_evaluator &tw)
Definition: pt-idx.cc:170
tree_index_expression * append(tree_argument_list *lst=nullptr, char t='(')
Definition: pt-idx.cc:81
std::list< tree_expression * > m_dyn_field
Definition: pt-idx.h:141
tree_expression * m_expr
Definition: pt-idx.h:128
std::string name(void) const
Definition: pt-idx.cc:139
octave_value_list evaluate_n(tree_evaluator &tw, int nargout=1)
Definition: pt-idx.cc:345
std::list< tree_argument_list * > m_args
Definition: pt-idx.h:131
std::list< string_vector > m_arg_nm
Definition: pt-idx.h:138
virtual int column(void) const
Definition: pt.h:62
virtual int line(void) const
Definition: pt.h:60
virtual octave_value_list call(octave::tree_evaluator &tw, int nargout=0, const octave_value_list &args=octave_value_list())
Definition: ov-fcn.cc:50
void stash_name_tags(const string_vector &nm)
Definition: ovl.h:165
octave_idx_type length(void) const
Definition: ovl.h:113
void clear(void)
Definition: ovl.h:173
bool is_function(void) const
Definition: ov.h:822
bool is_package(void) const
Definition: ov.h:706
octave_value subsref(const std::string &type, const std::list< octave_value_list > &idx)
Definition: ov.h:525
OCTINTERP_API std::string xstring_value(const char *fmt,...) const
OCTINTERP_API octave_function * function_value(bool silent=false) const
bool is_cs_list(void) const
Definition: ov.h:715
bool is_classdef_meta(void) const
Definition: ov.h:697
bool isjava(void) const
Definition: ov.h:712
bool isobject(void) const
Definition: ov.h:709
bool is_undefined(void) const
Definition: ov.h:640
octave_value find_function(const std::string &name, const symbol_scope &search_scope=symbol_scope())
Definition: symtab.cc:249
void set_indexed_object(const octave_value &obj=octave_value())
Definition: pt-eval.h:709
interpreter & get_interpreter(void)
Definition: pt-eval.h:414
void final_index_error(index_exception &ie, const tree_expression *expr)
Definition: pt-eval.cc:4528
octave_value indexed_object(void) const
Definition: pt-eval.h:704
octave_value_list convert_to_const_vector(tree_argument_list *arg_list)
Definition: pt-eval.cc:2295
const std::string & index_type(void) const
Definition: pt-eval.h:738
octave_value_list make_value_list(tree_argument_list *args, const string_vector &arg_nm)
Definition: pt-eval.cc:4566
bool is_variable(const std::string &name) const
Definition: pt-eval.cc:1865
void clear_index_list(void)
Definition: pt-eval.h:726
const std::list< octave_lvalue > * lvalue_list(void) const
Definition: pt-eval.h:749
void set_lvalue_list(const std::list< octave_lvalue > *lst)
Definition: pt-eval.h:754
void append_index_list(char type, const octave_value_list &idx)
Definition: pt-eval.h:732
const std::list< octave_value_list > & index_list(void) const
Definition: pt-eval.h:714
void set_index_list(const std::string &index_type, const std::list< octave_value_list > &index_list)
Definition: pt-eval.h:719
void error(const char *fmt,...)
Definition: error.cc:980
#define panic_impossible()
Definition: error.h:411
void err_indexed_cs_list(void)
Definition: errwarn.cc:65
return octave_value(v1.char_array_value() . concat(v2.char_array_value(), ra_idx),((a1.is_sq_string()||a2.is_sq_string()) ? '\'' :'"'))