# HG changeset patch
# User Max Brister
# Date 1331260537 25200
# Node ID fbe54c9e8eb7f94d68603e549211a13988c4cb44
# Parent c1f9b54350f93099170787589f3ea6b0b178f703
Nested function implementation/tests
diff -r c1f9b54350f9 -r fbe54c9e8eb7 src/oct-parse.yy
--- a/src/oct-parse.yy Fri Feb 24 20:42:29 2012 -0500
+++ b/src/oct-parse.yy Thu Mar 08 19:35:37 2012 -0700
@@ -115,6 +115,11 @@
// nested function.
static int current_function_depth = 0;
+// A stack holding the nested function scopes being parsed.
+// We don't use std::stack, because we want the clear method. Also, we
+// must access one from the top
+static std::vector function_scopes;
+
// Maximum function depth detected. Just here to determine whether
// we have nested functions or just implicitly ended subfunctions.
static int max_function_depth = 0;
@@ -1226,6 +1231,8 @@
symbol_table::set_scope (symbol_table::alloc_scope ());
+ function_scopes.push_back (symbol_table::current_scope ());
+
if (! reading_script_file && current_function_depth == 1
&& ! parsing_subfunctions)
primary_fcn_scope = symbol_table::current_scope ();
@@ -2862,7 +2869,11 @@
if (current_function_depth > 1 || parsing_subfunctions)
{
fcn->stash_parent_fcn_name (curr_fcn_file_name);
- fcn->stash_parent_fcn_scope (primary_fcn_scope);
+
+ if (current_function_depth > 1)
+ fcn->stash_parent_fcn_scope (function_scopes[function_scopes.size() - 2]);
+ else
+ fcn->stash_parent_fcn_scope (primary_fcn_scope);
}
if (lexer_flags.parsing_class_method)
@@ -2934,14 +2945,28 @@
fcn->define_ret_list (ret_list);
+ assert (current_function_depth == function_scopes.size());
if (current_function_depth > 1 || parsing_subfunctions)
{
- fcn->mark_as_subfunction ();
-
- symbol_table::install_subfunction (nm, octave_value (fcn),
- primary_fcn_scope);
+ fcn->mark_as_subfunction ();
+ if (endfunction_found && function_scopes.size () > 1)
+ {
+ symbol_table::scope_id pscope =
+ function_scopes[function_scopes.size() - 2];
+ symbol_table::install_nestfunction (nm, octave_value (fcn), pscope);
+ }
+ else
+ {
+ symbol_table::install_subfunction (nm, octave_value (fcn),
+ primary_fcn_scope);
+ }
}
+ if (current_function_depth == 1 && fcn)
+ {
+ symbol_table::update_nest (fcn->scope ());
+ }
+
if (! reading_fcn_file)
{
// We are either reading a script file or defining a function
@@ -2979,6 +3004,7 @@
parsing_subfunctions = true;
current_function_depth--;
+ function_scopes.pop_back ();
lexer_flags.defining_func--;
lexer_flags.parsed_function_name.pop ();
@@ -3448,6 +3474,7 @@
frame.protect_var (line_editing);
frame.protect_var (current_class_name);
frame.protect_var (current_function_depth);
+ frame.protect_var (function_scopes);
frame.protect_var (max_function_depth);
frame.protect_var (parsing_subfunctions);
frame.protect_var (endfunction_found);
@@ -3458,6 +3485,7 @@
line_editing = false;
current_class_name = dispatch_type;
current_function_depth = 0;
+ function_scopes.clear ();
max_function_depth = 0;
parsing_subfunctions = false;
endfunction_found = false;
@@ -3576,11 +3604,6 @@
if (status != 0)
error ("parse error while reading %s file %s",
file_type.c_str(), ff.c_str ());
- else if (reading_fcn_file && endfunction_found
- && max_function_depth > 1)
- warning_with_id ("Octave:nested-functions-coerced",
- "nested functions are coerced into subfunctions "
- "in file %s", ff.c_str ());
}
else
{
@@ -4292,6 +4315,7 @@
frame.protect_var (line_editing);
frame.protect_var (current_eval_string);
frame.protect_var (current_function_depth);
+ frame.protect_var (function_scopes);
frame.protect_var (max_function_depth);
frame.protect_var (parsing_subfunctions);
frame.protect_var (endfunction_found);
@@ -4307,6 +4331,7 @@
line_editing = false;
current_function_depth = 0;
max_function_depth = 0;
+ function_scopes.clear ();
parsing_subfunctions = false;
endfunction_found = false;
reading_fcn_file = false;
diff -r c1f9b54350f9 -r fbe54c9e8eb7 src/ov-usr-fcn.cc
--- a/src/ov-usr-fcn.cc Fri Feb 24 20:42:29 2012 -0500
+++ b/src/ov-usr-fcn.cc Thu Mar 08 19:35:37 2012 -0700
@@ -387,7 +387,7 @@
// Save old and set current symbol table context, for
// eval_undefined_error().
- int context = is_anonymous_function () ? 0 : call_depth;
+ int context = active_context ();
octave_call_stack::push (this, local_scope, context);
frame.add_fcn (octave_call_stack::pop);
diff -r c1f9b54350f9 -r fbe54c9e8eb7 src/ov-usr-fcn.h
--- a/src/ov-usr-fcn.h Fri Feb 24 20:42:29 2012 -0500
+++ b/src/ov-usr-fcn.h Thu Mar 08 19:35:37 2012 -0700
@@ -177,6 +177,12 @@
~octave_user_function (void);
+ symbol_table::context_id active_context () const
+ {
+ return is_anonymous_function () ? 0
+ : static_cast(call_depth);
+ }
+
octave_function *function_value (bool = false) { return this; }
octave_user_function *user_function_value (bool = false) { return this; }
diff -r c1f9b54350f9 -r fbe54c9e8eb7 src/symtab.cc
--- a/src/symtab.cc Fri Feb 24 20:42:29 2012 -0500
+++ b/src/symtab.cc Thu Mar 08 19:35:37 2012 -0700
@@ -625,40 +625,23 @@
// subfunctions if we are currently executing a function defined
// from a .m file.
- scope_val_iterator r = subfunctions.find (xcurrent_scope);
-
octave_user_function *curr_fcn = symbol_table::get_curr_fcn ();
- if (r != subfunctions.end ())
+ for (scope_id scope = xcurrent_scope; scope >= 0;)
{
- // FIXME -- out-of-date check here.
+ scope_val_iterator r = subfunctions.find (scope);
+ if (r != subfunctions.end ())
+ {
+ // FIXME -- out-of-date check here.
- return r->second;
- }
- else
- {
- if (curr_fcn)
- {
- // FIXME -- maybe it would be better if we could just get
- // a pointer to the parent function so we would have
- // access to all info about it instead of only being able
- // to query the current function for specific bits of info
- // about its parent function?
+ return r->second;
+ }
- scope_id pscope = curr_fcn->parent_fcn_scope ();
-
- if (pscope > 0)
- {
- r = subfunctions.find (pscope);
-
- if (r != subfunctions.end ())
- {
- // FIXME -- out-of-date check here.
-
- return r->second;
- }
- }
- }
+ octave_user_function *temp = get_curr_fcn (scope);
+ if (temp)
+ scope = temp->parent_fcn_scope ();
+ else
+ scope = -1;
}
// Private function.
@@ -896,29 +879,21 @@
// subfunctions if we are currently executing a function defined
// from a .m file.
- scope_val_iterator r = subfunctions.find (xcurrent_scope);
+ 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.
- if (r != subfunctions.end ())
- {
- // FIXME -- out-of-date check here.
+ return r->second;
+ }
- return r->second;
- }
- else if (curr_fcn)
- {
- scope_id pscope = curr_fcn->parent_fcn_scope ();
-
- if (pscope > 0)
- {
- r = subfunctions.find (pscope);
-
- if (r != subfunctions.end ())
- {
- // FIXME -- out-of-date check here.
-
- return r->second;
- }
- }
+ octave_user_function *temp = get_curr_fcn (scope);
+ if (temp)
+ scope = temp->parent_fcn_scope ();
+ else
+ scope = -1;
}
return octave_value ();
@@ -1144,6 +1119,24 @@
}
}
+void symbol_table::install_nestfunction (const std::string& name,
+ const octave_value& fcn,
+ scope_id parent_scope)
+{
+ // for now, just treat it as a subfunction of the parent_scope
+ install_subfunction (name, fcn, parent_scope);
+
+ // but we also need to share variables
+ octave_function *fv = fcn.function_value();
+ assert(fv);
+ symbol_table *fcn_table = get_instance (fv->scope());
+ symbol_table *parent_table = get_instance (parent_scope);
+ assert (fcn_table);
+ assert (parent_table);
+
+ fcn_table->stash_nest_parent(*parent_table);
+}
+
octave_value
symbol_table::find (const std::string& name,
const octave_value_list& args,
@@ -1390,6 +1383,51 @@
return retval;
}
+symbol_table*
+symbol_table::find_nonlocal (const std::string& name, symbol_record& record)
+{
+ for (symbol_table *tbl = nest_parent; tbl; tbl = tbl->nest_parent)
+ {
+ table_iterator iter = tbl->table.find (name);
+
+ assert (tbl->curr_fcn); // can only have nested functions for user functions
+ if (! (iter == tbl->table.end () || iter->second.is_nonlocal ()))
+ {
+ record = iter->second;
+ if (tbl->curr_fcn->active_context () != static_cast(-1))
+ return tbl;
+
+ // returning handles to nested functions requires closures
+ ::error ("Closures are not yet supported.");
+ return 0;
+ }
+ }
+
+ ::error ("unable to resolve nonlocal!");
+ return 0;
+}
+
+octave_value&
+symbol_table::do_nonlocal_varref (const std::string& name)
+{
+ static octave_value foobar;
+ symbol_record record;
+ symbol_table* tbl = find_nonlocal (name, record);
+ if (tbl)
+ return record.varref (tbl->curr_fcn->active_context ());
+ return foobar;
+}
+
+octave_value
+symbol_table::do_nonlocal_varval (const std::string& name)
+{
+ symbol_record record;
+ symbol_table* tbl = find_nonlocal (name, record);
+ if (tbl)
+ return record.varval (tbl->curr_fcn->active_context ());
+ return octave_value ();
+}
+
void
symbol_table::do_dump (std::ostream& os)
{
diff -r c1f9b54350f9 -r fbe54c9e8eb7 src/symtab.h
--- a/src/symtab.h Fri Feb 24 20:42:29 2012 -0500
+++ b/src/symtab.h Thu Mar 08 19:35:37 2012 -0700
@@ -199,6 +199,9 @@
// temporary variables forced into symbol table for parsing
static const unsigned int forced = 128;
+ // variables that are in a higher up nested scope, but not global
+ static const unsigned int nonlocal = 256;
+
private:
class
@@ -227,6 +230,8 @@
return symbol_table::global_varref (name);
else if (is_persistent ())
return symbol_table::persistent_varref (name);
+ else if (is_nonlocal ())
+ return symbol_table::nonlocal_varref (name);
else
{
context_id n = value_stack.size ();
@@ -243,6 +248,8 @@
return symbol_table::global_varval (name);
else if (is_persistent ())
return symbol_table::persistent_varval (name);
+ else if (is_nonlocal ())
+ return symbol_table::nonlocal_varval (name);
else
{
if (context < value_stack.size ())
@@ -254,7 +261,7 @@
void push_context (void)
{
- if (! (is_persistent () || is_global ()))
+ if (! (is_persistent () || is_global () || is_nonlocal ()))
value_stack.push_back (octave_value ());
}
@@ -276,7 +283,7 @@
{
size_t retval = 1;
- if (! (is_persistent () || is_global ()))
+ if (! (is_persistent () || is_global () || is_nonlocal ()))
{
value_stack.pop_back ();
retval = value_stack.size ();
@@ -287,7 +294,7 @@
void clear (void)
{
- if (! (is_hidden () || is_inherited ()))
+ if (! (is_hidden () || is_inherited () || is_nonlocal ()))
{
if (is_global ())
unmark_global ();
@@ -322,6 +329,7 @@
bool is_global (void) const { return storage_class & global; }
bool is_persistent (void) const { return storage_class & persistent; }
bool is_forced (void) const { return storage_class & forced; }
+ bool is_nonlocal (void) const { return storage_class & nonlocal; }
void mark_local (void) { storage_class |= local; }
void mark_automatic (void) { storage_class |= automatic; }
@@ -343,6 +351,11 @@
storage_class |= persistent;
}
void mark_forced (void) { storage_class |= forced; }
+ void mark_nonlocal (void)
+ {
+ // FIXME -- check to ensure this is legal
+ storage_class |= nonlocal;
+ }
void unmark_local (void) { storage_class &= ~local; }
void unmark_automatic (void) { storage_class &= ~automatic; }
@@ -352,6 +365,7 @@
void unmark_global (void) { storage_class &= ~global; }
void unmark_persistent (void) { storage_class &= ~persistent; }
void unmark_forced (void) { storage_class &= ~forced; }
+ void unmark_nonlocal (void) { storage_class &= ~nonlocal; }
void init_persistent (void)
{
@@ -375,7 +389,7 @@
symbol_record_rep *dup (void) const
{
return new symbol_record_rep (name, varval (xcurrent_context),
- storage_class);
+ storage_class);
}
void dump (std::ostream& os, const std::string& prefix) const;
@@ -477,6 +491,7 @@
bool is_inherited (void) const { return rep->is_inherited (); }
bool is_persistent (void) const { return rep->is_persistent (); }
bool is_forced (void) const { return rep->is_forced (); }
+ bool is_nonlocal (void) const { return rep->is_nonlocal (); }
void mark_local (void) { rep->mark_local (); }
void mark_automatic (void) { rep->mark_automatic (); }
@@ -486,6 +501,7 @@
void mark_global (void) { rep->mark_global (); }
void mark_persistent (void) { rep->mark_persistent (); }
void mark_forced (void) { rep->mark_forced (); }
+ void mark_nonlocal (void) { rep->mark_nonlocal (); }
void unmark_local (void) { rep->unmark_local (); }
void unmark_automatic (void) { rep->unmark_automatic (); }
@@ -495,6 +511,7 @@
void unmark_global (void) { rep->unmark_global (); }
void unmark_persistent (void) { rep->unmark_persistent (); }
void unmark_forced (void) { rep->unmark_forced (); }
+ void unmark_nonlocal (void) { rep->unmark_nonlocal (); }
void init_persistent (void) { rep->init_persistent (); }
@@ -1154,6 +1171,22 @@
inst->do_erase_persistent (name);
}
+ static octave_value& nonlocal_varref (const std::string& name)
+ {
+ symbol_table *inst = get_instance (xcurrent_scope);
+
+ assert (inst);
+ return inst->do_nonlocal_varref (name);
+ }
+
+ static octave_value nonlocal_varval (const std::string& name)
+ {
+ symbol_table *inst = get_instance (xcurrent_scope);
+
+ assert (inst);
+ return inst->do_nonlocal_varval (name);
+ }
+
static bool is_variable (const std::string& name)
{
symbol_table *inst = get_instance (xcurrent_scope);
@@ -1263,6 +1296,16 @@
}
}
+ static void install_nestfunction (const std::string& name, const octave_value& fcn,
+ scope_id parent_scope);
+
+ static void update_nest (scope_id scope)
+ {
+ symbol_table *inst = get_instance (scope);
+ if (inst)
+ inst->do_update_nest ();
+ }
+
static void install_user_function (const std::string& name,
const octave_value& fcn)
{
@@ -1961,6 +2004,12 @@
// Map from symbol names to symbol info.
std::map table;
+ // If we are nested, this is our parent function
+ symbol_table *nest_parent;
+
+ // And these are all of our nested child functions
+ std::vector nest_children;
+
// The associated user code (may be null).
octave_user_function *curr_fcn;
@@ -2001,7 +2050,7 @@
static context_id xcurrent_context;
symbol_table (void)
- : table_name (), table (), curr_fcn (0), persistent_table () { }
+ : table_name (), table (), nest_parent (0), curr_fcn (0), persistent_table () { }
~symbol_table (void) { }
@@ -2063,16 +2112,11 @@
return retval;
}
- void insert_symbol_record (const symbol_record& sr)
- {
- table[sr.name ()] = sr;
- }
-
void
do_dup_scope (symbol_table& new_symbol_table) const
{
for (table_const_iterator p = table.begin (); p != table.end (); p++)
- new_symbol_table.insert_symbol_record (p->second.dup ());
+ new_symbol_table.table[p->first] = p->second.dup ();
}
symbol_record do_find_symbol (const std::string& name)
@@ -2188,6 +2232,12 @@
persistent_table.erase (p);
}
+ symbol_table* find_nonlocal (const std::string& name, symbol_record &record);
+
+ octave_value& do_nonlocal_varref (const std::string& name);
+
+ octave_value do_nonlocal_varval (const std::string& name);
+
bool do_is_variable (const std::string& name) const
{
bool retval = false;
@@ -2462,6 +2512,41 @@
void do_dump (std::ostream& os);
void do_cache_name (const std::string& name) { table_name = name; }
+
+ void stash_nest_parent (symbol_table& parent)
+ {
+ assert (!nest_parent);
+ nest_parent = &parent;
+ parent.nest_children.push_back (this);
+ }
+
+ void do_update_nest (const std::set& check = std::set ())
+ {
+ if (check.size ())
+ {
+ for (table_iterator iter = table.begin (); iter != table.end (); ++iter)
+ {
+ if (check.count (iter->first))
+ iter->second.mark_nonlocal ();
+ else
+ iter->second.unmark_nonlocal ();
+ }
+ }
+
+ if (nest_children.size ())
+ {
+ std::set next = check;
+ for (table_iterator iter = table.begin (); iter != table.end (); ++iter)
+ {
+ if (iter->second.is_local ())
+ next.insert (iter->first);
+ }
+
+ for (std::vector::iterator iter = nest_children.begin ();
+ iter != nest_children.end (); ++iter)
+ (*iter)->do_update_nest (next);
+ }
+ }
};
extern bool out_of_date_check (octave_value& function,
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/module.mk
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/module.mk Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,8 @@
+nest_FCN_FILES = \
+ nest/recursive_nest.m \
+ nest/scope0.m \
+ nest/scope1.m \
+ nest/scope2.m \
+ nest/test_nest.m
+
+FCN_FILES += $(nest_FCN_FILES)
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/recursive_nest.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/recursive_nest.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,12 @@
+# recursive_nest.m
+function x = recursive_nest()
+ X = 5;
+ B(20)
+ x = X;
+ function B(n)
+ if n > 0
+ X = X + 1;
+ B(n - 1);
+ end
+ endfunction
+endfunction
\ No newline at end of file
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/recursive_nest2.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/recursive_nest2.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,21 @@
+# recursive_nest2.m
+function x = recursive_nest2()
+ x = B(20)
+ function v = B(n)
+ Y = 0;
+ BB(n);
+ C;
+ v = Y;
+ function BB(m)
+ if m > 0
+ Y = Y + 1;
+ BB(m - 1);
+ C;
+ end
+ endfunction
+ endfunction
+
+ function C
+ Y = 0;
+ endfunction
+endfunction
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/scope0.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/scope0.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,16 @@
+# scope0.m
+function scope0
+ C
+ function A
+ B
+ function B
+ endfunction
+ endfunction
+
+ function C
+ D
+ function D
+ A
+ endfunction
+ endfunction
+endfunction
\ No newline at end of file
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/scope1.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/scope1.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,20 @@
+# scope1.m
+function scope1(n)
+ value = n;
+ if value
+ C
+ end
+ function A
+ B
+ function B
+ scope1(0)
+ endfunction
+ endfunction
+
+ function C
+ D
+ function D
+ A
+ endfunction
+ endfunction
+endfunction
\ No newline at end of file
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/scope2.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/scope2.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,17 @@
+# scope2.m
+function scope2
+ C
+ function A
+ B
+ function B
+ D
+ endfunction
+ endfunction
+
+ function C
+ D
+ function D
+ A
+ endfunction
+ endfunction
+endfunction
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/scope3.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/scope3.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,19 @@
+# scope3.m
+function scope3
+ C
+ function A
+ B
+ function B
+ E
+ endfunction
+ function E
+ endfunction
+ endfunction
+
+ function C
+ D
+ function D
+ A
+ endfunction
+ endfunction
+endfunction
\ No newline at end of file
diff -r c1f9b54350f9 -r fbe54c9e8eb7 test/nest/test_nest.m
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nest/test_nest.m Thu Mar 08 19:35:37 2012 -0700
@@ -0,0 +1,37 @@
+## Copyright (C) 2006-2012 John W. Eaton
+##
+## 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
+## .
+
+%!test
+%! a = recursive_nest;
+%! assert (a, 25);
+
+%!test
+%! a = recursive_nest2;
+%! assert (a, 20);
+
+%!test
+%! scope0;
+
+%!test
+%! scope1(1);
+
+%!test
+% fail ("scope2");
+
+%!test
+%! scope3;