diff libinterp/parse-tree/pt-anon-scopes.cc @ 25824:91e1ca0e3a9d

Save all scopes of nested anonymous functions (bug #45969). * libinterp/parse-tree/pt-anon-scopes.[h,cc]: New files with new class tree_anon_scopes derived from class tree_walker. Collects variables in all scopes of nested anonymous function definitions. * libinterp/octave-value/ov-fcn-handle.cc (octave_fcn_handle::save_ascii, ::save_binary, ::save_hdf5): Use tree_anon_scopes object to save variables of anonymous functions. * test/bug-45969/bug-45969.tst, test/bug-45969/module.mk: New testfiles.
author Olaf Till <i7tiol@t-online.de>
date Sat, 18 Aug 2018 23:05:41 +0200
parents
children f2d795f07c84
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/pt-anon-scopes.cc	Sat Aug 18 23:05:41 2018 +0200
@@ -0,0 +1,433 @@
+/*
+
+Copyright (C) 1996-2018 John W. Eaton
+Copyright (C) 2015-2018 Olaf Till
+
+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
+<http://www.gnu.org/licenses/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "error.h"
+#include "pt-all.h"
+#include "pt-anon-scopes.h"
+
+// TODO: make sure that if(f->scope()) is checked if necessary
+
+namespace octave
+{
+  tree_anon_scopes::tree_anon_scopes (octave_user_function *f)
+    : scopes (), merged_tables ()
+  {
+    if (f)
+      {
+        if (! f->is_anonymous_function ())
+          panic_impossible ();
+
+        // Collect the scope of the outer anonymous function.
+
+        stash_scope_if_valid (f->scope ());
+
+        // Further walk the tree to find nested definitions of further
+        // anonymous functions.
+
+        tree_statement_list *cmd_list = f->body ();
+
+        if (cmd_list)
+          cmd_list->accept (*this);
+
+        // Collect symbol records of all collected scopes.
+
+        merge_tables ();
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_anon_fcn_handle (tree_anon_fcn_handle& afh)
+  {
+    // Collect the scope of this anonymous function.
+
+    stash_scope_if_valid (afh.scope ());
+
+    // Further walk the tree to find nested definitions of further
+    // anonymous functions.
+
+    tree_expression *e = afh.expression ();
+
+    if (e)
+      e->accept (*this);
+  }
+
+  // The rest of visit_... methods is only for walking the tree. Many of
+  // them, in particular all methods for commands, are not applicable to
+  // anonymous functions. Only parts of the tree are walked which could
+  // contain further (nested) anonymous function definitions (so
+  // e.g. identifiers and left hand sides of assignments are ignored).
+
+  void
+  tree_anon_scopes::visit_argument_list (tree_argument_list& lst)
+  {
+    tree_argument_list::iterator p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_expression *elt = *p++;
+
+        if (elt)
+          {
+            elt->accept (*this);
+          }
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_binary_expression (tree_binary_expression& expr)
+  {
+    tree_expression *op1 = expr.lhs ();
+
+    if (op1)
+      op1->accept (*this);
+
+    tree_expression *op2 = expr.rhs ();
+
+    if (op2)
+      op2->accept (*this);
+  }
+
+  void
+  tree_anon_scopes::visit_break_command (tree_break_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_colon_expression (tree_colon_expression& expr)
+  {
+    tree_expression *op1 = expr.base ();
+
+    if (op1)
+      op1->accept (*this);
+
+    tree_expression *op3 = expr.increment ();
+
+    if (op3)
+      op3->accept (*this);
+
+    tree_expression *op2 = expr.limit ();
+
+    if (op2)
+      op2->accept (*this);
+  }
+
+  void
+  tree_anon_scopes::visit_continue_command (tree_continue_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_decl_command (tree_decl_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_decl_elt (tree_decl_elt&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_decl_init_list (tree_decl_init_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_simple_for_command (tree_simple_for_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_complex_for_command (tree_complex_for_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_octave_user_script (octave_user_script&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_octave_user_function (octave_user_function&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_function_def (tree_function_def&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_identifier (tree_identifier& /* id */)
+  {
+  }
+
+  void
+  tree_anon_scopes::visit_if_clause (tree_if_clause&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_if_command (tree_if_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_if_command_list (tree_if_command_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_switch_case (tree_switch_case&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_switch_case_list (tree_switch_case_list&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_switch_command (tree_switch_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_index_expression (tree_index_expression& expr)
+  {
+    tree_expression *e = expr.expression ();
+
+    if (e)
+      e->accept (*this);
+
+    std::list<tree_argument_list *> lst = expr.arg_lists ();
+
+    std::list<tree_argument_list *>::iterator p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_argument_list *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_matrix (tree_matrix& lst)
+  {
+    tree_matrix::iterator p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_argument_list *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_cell (tree_cell& lst)
+  {
+    tree_matrix::iterator p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_argument_list *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_multi_assignment (tree_multi_assignment& expr)
+  {
+    tree_expression *rhs = expr.right_hand_side ();
+
+    if (rhs)
+      rhs->accept (*this);
+  }
+
+  void
+  tree_anon_scopes::visit_no_op_command (tree_no_op_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_constant (tree_constant& /* val */)
+  {
+  }
+
+  void
+  tree_anon_scopes::visit_fcn_handle (tree_fcn_handle& /* fh */)
+  {
+  }
+
+  void
+  tree_anon_scopes::visit_funcall (tree_funcall& /* fc */)
+  {
+  }
+
+  void
+  tree_anon_scopes::visit_parameter_list (tree_parameter_list&)
+  {
+    // In visit_anon_fcn_handle we only accept/visit the body of
+    // anonymous function definitions, not the parameter list.
+
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_postfix_expression (tree_postfix_expression& expr)
+  {
+    tree_expression *e = expr.operand ();
+
+    if (e)
+      e->accept (*this);
+  }
+
+  void
+  tree_anon_scopes::visit_prefix_expression (tree_prefix_expression& expr)
+  {
+    tree_expression *e = expr.operand ();
+
+    if (e)
+      e->accept (*this);
+  }
+
+  void
+  tree_anon_scopes::visit_return_command (tree_return_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_return_list (tree_return_list& lst)
+  {
+    tree_return_list::iterator p = lst.begin ();
+
+    while (p != lst.end ())
+      {
+        tree_index_expression *elt = *p++;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_simple_assignment (tree_simple_assignment& expr)
+  {
+    tree_expression *rhs = expr.right_hand_side ();
+
+    if (rhs)
+      rhs->accept (*this);
+  }
+
+  void
+  tree_anon_scopes::visit_statement (tree_statement& stmt)
+  {
+    tree_command *cmd = stmt.command ();
+
+    if (cmd)
+      panic_impossible ();
+    else
+      {
+        tree_expression *expr = stmt.expression ();
+
+        if (expr)
+          expr->accept (*this);
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_statement_list (tree_statement_list& lst)
+  {
+    for (auto& p : lst)
+      {
+        tree_statement *elt = p;
+
+        if (elt)
+          elt->accept (*this);
+      }
+  }
+
+  void
+  tree_anon_scopes::visit_try_catch_command (tree_try_catch_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_unwind_protect_command (tree_unwind_protect_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_while_command (tree_while_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::visit_do_until_command (tree_do_until_command&)
+  {
+    panic_impossible ();
+  }
+
+  void
+  tree_anon_scopes::merge_tables (void)
+  {
+    for (const auto& sc : scopes)
+      {
+        symrec_list vars = sc.all_variables ();
+
+        for (const auto& symrec : vars)
+          merged_tables[symrec.name ()] = symrec;
+      }
+  }
+}
+