changeset 23343:49f051ef6f2f

local functions in classdef files (bug #41723) * symtab.h, symtab.cc (symbol_table::fcn_info::fcn_info_rep::xfind): Also handle file-local functions (symbol_table::fcn_info::fcn_info_rep::x_builtin_find): Likewise. (symbol_table::fcn_info::fcn_info_rep::dump): Display info about local functions. (symbol_table::fcn_info::fcn_info_rep::local_functions): New map. (symbol_table::fcn_info::fcn_info_rep::fcn_info_rep): Initialize it. (symbol_table::fcn_info::fcn_info_rep::install_local_funciton): New function. (symbol_table::fcn_info::fcn_info_rep::clear): Clear local_functions map. (symbol_table::fcn_info::install_local_function): New function. (symbol_table::install_local_function): New function. * parse.h, oct-parse.in.yy (fcn_list, fcn_list1, opt_fcn_list): New non-terminals. Set parsing_local_functions after classdef is recognized. (file): Accept opt_fcn_list list after classdef object. (base_parser::parsing_local_functions): New data member. (base_parser::base_parser): Initialize it. (base_parser::finish_function): Handle local functions.
author John W. Eaton <jwe@octave.org>
date Mon, 03 Apr 2017 16:21:19 -0400
parents c70c0397ca2f
children 7dc148266dca
files libinterp/corefcn/symtab.cc libinterp/corefcn/symtab.h libinterp/parse-tree/oct-parse.in.yy libinterp/parse-tree/parse.h
diffstat 4 files changed, 157 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/symtab.cc	Mon Apr 03 12:50:55 2017 -0400
+++ b/libinterp/corefcn/symtab.cc	Mon Apr 03 16:21:19 2017 -0400
@@ -706,18 +706,42 @@
 {
   if (local_funcs)
     {
+      octave_user_function *current_fcn = symbol_table::get_curr_fcn ();
+
+      // Local function.
+
+      if (current_fcn)
+        {
+          std::string fcn_file = current_fcn->fcn_file_name ();
+
+          if (! fcn_file.empty ())
+            {
+              str_val_iterator r = local_functions.find (fcn_file);
+
+              if (r != local_functions.end ())
+                {
+                  // We shouldn't need an out-of-date check here since
+                  // local functions may ultimately be called only from
+                  // a primary function or method defined in the same
+                  // file.
+
+                  return r->second;
+                }
+            }
+        }
+
       // Subfunction.  I think it only makes sense to check for
       // subfunctions if we are currently executing a function defined
       // from a .m file.
 
-      octave_user_function *current_fcn = symbol_table::get_curr_fcn ();
-
       for (scope_id scope = xcurrent_scope; scope >= 0;)
         {
           scope_val_iterator r = subfunctions.find (scope);
           if (r != subfunctions.end ())
             {
-              // FIXME: out-of-date check here.
+              // We shouldn't need an out-of-date check here since
+              // subfunctions may ultimately be called only from a
+              // primary function or method defined in the same file.
 
               return r->second;
             }
@@ -941,6 +965,27 @@
         }
     }
 
+  // Local function.
+
+  if (current_fcn)
+    {
+      std::string fcn_file = current_fcn->fcn_file_name ();
+
+      if (! fcn_file.empty ())
+        {
+          str_val_iterator r = local_functions.find (fcn_file);
+
+          if (r != local_functions.end ())
+            {
+              // We shouldn't need an out-of-date check here since local
+              // functions may ultimately be called only from a primary
+              // function or method defined in the same file.
+
+              return r->second;
+            }
+        }
+    }
+
   // Subfunction.  I think it only makes sense to check for
   // subfunctions if we are currently executing a function defined
   // from a .m file.
@@ -950,7 +995,9 @@
       scope_val_iterator r = subfunctions.find (scope);
       if (r != subfunctions.end ())
         {
-          // FIXME: out-of-date check here.
+          // We shouldn't need an out-of-date check here since
+          // subfunctions may ultimately be called only from a primary
+          // function or method defined in the same file.
 
           return r->second;
         }
@@ -1181,6 +1228,13 @@
            << " [" << scope_val.first << "]\n";
     }
 
+  if (! local_functions.empty ())
+    {
+      for (const auto& str_val : local_functions)
+        os << tprefix << "local: " << fcn_file_name (str_val.second)
+           << " [" << str_val.first << "]\n";
+    }
+
   if (! private_functions.empty ())
     {
       for (const auto& str_val : private_functions)
--- a/libinterp/corefcn/symtab.h	Mon Apr 03 12:50:55 2017 -0400
+++ b/libinterp/corefcn/symtab.h	Mon Apr 03 16:21:19 2017 -0400
@@ -747,10 +747,10 @@
     public:
 
       fcn_info_rep (const std::string& nm)
-        : name (nm), package_name (), subfunctions (), private_functions (),
-          class_constructors (), class_methods (), cmdline_function (),
-          autoload_function (), function_on_path (), built_in_function (),
-          count (1)
+        : name (nm), package_name (), subfunctions (), local_functions (),
+          private_functions (), class_constructors (), class_methods (),
+          cmdline_function (), autoload_function (), function_on_path (),
+          built_in_function (), count (1)
       {
         size_t pos = name.rfind ('.');
 
@@ -767,6 +767,8 @@
 
       fcn_info_rep& operator = (const fcn_info_rep&) = delete;
 
+      octave_value install_local_function (const std::string& file_name);
+
       octave_value load_private_function (const std::string& dir_name);
 
       octave_value load_class_constructor (void);
@@ -843,6 +845,12 @@
         subfunctions[scope] = f;
       }
 
+      void install_local_function (const octave_value& f,
+                                   const std::string& file_name)
+      {
+        local_functions[file_name] = f;
+      }
+
       void install_user_function (const octave_value& f)
       {
         function_on_path = f;
@@ -903,6 +911,7 @@
       void clear (bool force = false)
       {
         clear_map (subfunctions, force);
+        clear_map (local_functions, force);
         clear_map (private_functions, force);
         clear_map (class_constructors, force);
         clear_map (class_methods, force);
@@ -929,6 +938,9 @@
       // Scope id to function object.
       std::map<scope_id, octave_value> subfunctions;
 
+      // File name to function object.
+      std::map<std::string, octave_value> local_functions;
+
       // Directory name to function object.
       std::map<std::string, octave_value> private_functions;
 
@@ -1072,6 +1084,12 @@
       rep->install_subfunction (f, scope);
     }
 
+    void install_local_function (const octave_value& f,
+                                 const std::string& file_name)
+    {
+      rep->install_local_function (f, file_name);
+    }
+
     void install_user_function (const octave_value& f)
     {
       rep->install_user_function (f);
@@ -1575,6 +1593,31 @@
       inst->do_update_nest ();
   }
 
+  // Install local function FCN named NAME.  FILE_NAME is the name of
+  // the file containing the local function.
+
+  static void install_local_function (const std::string& name,
+                                      const octave_value& fcn,
+                                      const std::string& file_name)
+  {
+    fcn_table_iterator p = fcn_table.find (name);
+
+    if (p != fcn_table.end ())
+      {
+        fcn_info& finfo = p->second;
+
+        finfo.install_local_function (fcn, file_name);
+      }
+    else
+      {
+        fcn_info finfo (name);
+
+        finfo.install_local_function (fcn, file_name);
+
+        fcn_table[name] = finfo;
+      }
+  }
+
   static void install_user_function (const std::string& name,
                                      const octave_value& fcn)
   {
--- a/libinterp/parse-tree/oct-parse.in.yy	Mon Apr 03 12:50:55 2017 -0400
+++ b/libinterp/parse-tree/oct-parse.in.yy	Mon Apr 03 16:21:19 2017 -0400
@@ -231,6 +231,7 @@
 // Nonterminals we construct.
 %type <dummy_type> indirect_ref_op decl_param_init push_fcn_symtab
 %type <dummy_type> param_list_beg param_list_end stmt_begin parse_error
+%type <dummy_type> parsing_local_fcns
 %type <comment_type> stash_comment
 %type <tok_val> function_beg classdef_beg
 %type <punct_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep
@@ -269,6 +270,7 @@
 %type <tree_statement_type> statement function_end
 %type <tree_statement_list_type> simple_list simple_list1 list list1
 %type <tree_statement_list_type> opt_list
+%type <tree_statement_list_type> opt_fcn_list fcn_list fcn_list1
 %type <tree_classdef_attribute_type> attr
 %type <tree_classdef_attribute_list_type> attr_list opt_attr_list
 %type <tree_classdef_superclass_type> superclass
@@ -431,6 +433,32 @@
                   { $$ = parser.append_statement_list ($1, $2, $3, true); }
                 ;
 
+opt_fcn_list    : // empty
+                  { $$ = 0; }
+                | fcn_list
+                  { $$ = $1; }
+                ;
+                
+fcn_list        : fcn_list1 opt_sep
+                  {
+                    YYUSE ($2);
+
+                    $$ = $1;
+                  }
+                ;
+
+fcn_list1       : function
+                  {
+                    octave::tree_statement *stmt = parser.make_statement ($1);
+                    $$ = new octave::tree_statement_list (stmt);
+                  }
+                | fcn_list1 opt_sep function
+                  {
+                    octave::tree_statement *stmt = parser.make_statement ($3);
+                    $$ = parser.append_statement_list ($1, $2, stmt, false);
+                  }
+                ;
+
 statement       : expression
                   { $$ = parser.make_statement ($1); }
                 | command
@@ -1385,6 +1413,11 @@
 // Script or function file
 // =======================
 
+parsing_local_fcns
+                : // empty
+                  { parser.parsing_local_functions = true; }
+                ;
+
 file            : INPUT_FILE opt_nl opt_list END_OF_INPUT
                   {
                     YYUSE ($2);
@@ -1411,10 +1444,11 @@
 
                     $$ = 0;
                   }
-                | INPUT_FILE opt_nl classdef opt_sep END_OF_INPUT
+                | INPUT_FILE opt_nl classdef parsing_local_fcns opt_sep opt_fcn_list END_OF_INPUT
                   {
                     YYUSE ($2);
-                    YYUSE ($4);
+                    YYUSE ($5);
+                    YYUSE ($6);
 
                     if (lexer.reading_classdef_file)
                       parser.classdef_object = $3;
@@ -2108,7 +2142,8 @@
 
   base_parser::base_parser (base_lexer& lxr)
     : endfunction_found (false), autoloading (false),
-      fcn_file_from_relative_lookup (false), parsing_subfunctions (false),
+      fcn_file_from_relative_lookup (false),
+      parsing_subfunctions (false), parsing_local_functions (false),
       max_fcn_depth (0), curr_fcn_depth (0), primary_fcn_scope (-1),
       curr_class_name (), curr_package_name (), function_scopes (),
       primary_fcn_ptr (0), subfunction_names (), classdef_object (0),
@@ -2139,6 +2174,7 @@
     autoloading = false;
     fcn_file_from_relative_lookup = false;
     parsing_subfunctions = false;
+    parsing_local_functions = false;
     max_fcn_depth = 0;
     curr_fcn_depth = 0;
     primary_fcn_scope = -1;
@@ -3375,8 +3411,14 @@
                }
           }
 
-        if (curr_fcn_depth == 1 && fcn)
-          symbol_table::update_nest (fcn->scope ());
+        if (fcn)
+          {
+            if (parsing_local_functions )
+              symbol_table::install_local_function (nm, octave_value (fcn),
+                                                    file);
+            else if (curr_fcn_depth == 1)
+              symbol_table::update_nest (fcn->scope ());
+          }
 
         if (! lexer.reading_fcn_file && curr_fcn_depth == 1)
           {
--- a/libinterp/parse-tree/parse.h	Mon Apr 03 12:50:55 2017 -0400
+++ b/libinterp/parse-tree/parse.h	Mon Apr 03 16:21:19 2017 -0400
@@ -413,6 +413,11 @@
     // only be declared inside function files.
     bool parsing_subfunctions;
 
+    // TRUE if we are parsing local functions defined at after a
+    // classdef block.  Local functions can only be declared inside
+    // classdef files.
+    bool parsing_local_functions;
+
     // Maximum function depth detected.  Used to determine whether
     // we have nested functions or just implicitly ended subfunctions.
     int max_fcn_depth;