# HG changeset patch # User Max Brister # Date 1333144185 14400 # Node ID 66dd2cfa80207882f784a90784d89ff50bfbc2df # Parent e0d66b8b0c6312d6b592254565ae2ebc14fb00ad bug 35772: Nested function support diff --git a/src/oct-parse.yy b/src/oct-parse.yy --- a/src/oct-parse.yy +++ b/src/oct-parse.yy @@ -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 (); @@ -2861,7 +2868,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) @@ -2937,10 +2948,24 @@ { fcn->mark_as_subfunction (); - symbol_table::install_subfunction (nm, octave_value (fcn), - primary_fcn_scope); + 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 && current_function_depth == 1) { // 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); @@ -4306,6 +4330,7 @@ parser_end_of_input = false; line_editing = false; current_function_depth = 0; + function_scopes.clear (); max_function_depth = 0; parsing_subfunctions = false; endfunction_found = false; diff --git a/src/ov-fcn-handle.cc b/src/ov-fcn-handle.cc --- a/src/ov-fcn-handle.cc +++ b/src/ov-fcn-handle.cc @@ -80,6 +80,9 @@ if (uf && nm != anonymous) symbol_table::cache_name (uf->scope (), nm); + + if (uf && uf->is_nested_function ()) + ::error ("handles to nested functions are not yet supported"); } octave_value_list diff --git a/src/ov-usr-fcn.cc b/src/ov-usr-fcn.cc --- a/src/ov-usr-fcn.cc +++ b/src/ov-usr-fcn.cc @@ -188,8 +188,9 @@ system_fcn_file (false), call_depth (-1), num_named_args (param_list ? param_list->length () : 0), subfunction (false), inline_function (false), - anonymous_function (false), class_constructor (false), - class_method (false), parent_scope (-1), local_scope (sid), + anonymous_function (false), nested_function(false), + class_constructor (false), class_method (false), + parent_scope (-1), local_scope (sid), curr_unwind_protect_frame (0) { if (cmd_list) @@ -387,7 +388,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 --git a/src/ov-usr-fcn.h b/src/ov-usr-fcn.h --- a/src/ov-usr-fcn.h +++ b/src/ov-usr-fcn.h @@ -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; } @@ -277,6 +283,10 @@ : false; } + bool is_nested_function (void) const { return nested_function; } + + void mark_as_nested_function (void) { nested_function = true; } + void mark_as_class_constructor (void) { class_constructor = true; } bool is_class_constructor (const std::string& cname = std::string ()) const @@ -400,6 +410,9 @@ // TRUE means this is an anonymous function. bool anonymous_function; + // TRUE means this is a nested function. (either a child or parent) + bool nested_function; + // TRUE means this function is the constructor for class object. bool class_constructor; diff --git a/src/pt-id.h b/src/pt-id.h --- a/src/pt-id.h +++ b/src/pt-id.h @@ -128,10 +128,11 @@ { symbol_table::scope_id curr_scope = symbol_table::current_scope (); - if (scope != curr_scope) + if (scope != curr_scope || ! sym.is_valid ()) { scope = curr_scope; sym = symbol_table::insert (sym.name ()); + assert (sym.is_valid ()); } return sym; diff --git a/src/symtab.cc b/src/symtab.cc --- a/src/symtab.cc +++ b/src/symtab.cc @@ -78,11 +78,25 @@ singleton_cleanup_list::add (cleanup_instance); } +symbol_table::context_id +symbol_table::symbol_record::symbol_record_rep::active_context (void) const +{ + octave_user_function *fcn = curr_fcn; + + // FIXME -- If active_context () == -1, then it does not make much + // sense to use this symbol_record. This means an attempt at accessing + // a variable from a function that has not been called yet is + // happening. This should be cleared up when an implementing closures. + + return fcn && fcn->active_context () != static_cast (-1) + ? fcn->active_context () : xcurrent_context; +} + void symbol_table::symbol_record::symbol_record_rep::dump (std::ostream& os, const std::string& prefix) const { - octave_value val = varval (xcurrent_context); + octave_value val = varval (); os << prefix << name; @@ -625,40 +639,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 *scope_curr_fcn = get_curr_fcn (scope); + if (scope_curr_fcn) + scope = scope_curr_fcn->parent_fcn_scope (); + else + scope = -1; } // Private function. @@ -896,29 +893,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 *scope_curr_fcn = get_curr_fcn (scope); + if (scope_curr_fcn) + scope = scope_curr_fcn->parent_fcn_scope (); + else + scope = -1; } return octave_value (); @@ -1144,6 +1133,24 @@ } } +void +symbol_table::install_nestfunction (const std::string& name, + const octave_value& fcn, + scope_id parent_scope) +{ + install_subfunction (name, fcn, parent_scope); + + // stash the nest_parent for resolving variables after parsing is done + 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); + + parent_table->add_nest_child (*fcn_table); +} + octave_value symbol_table::find (const std::string& name, const octave_value_list& args, @@ -1456,6 +1463,46 @@ } } +void +symbol_table::do_update_nest (void) +{ + assert (curr_fcn); + + if (nest_parent || nest_children.size ()) + curr_fcn->mark_as_nested_function (); + + if (nest_parent) + { + // fix bad symbol_records + for (table_iterator ti = table.begin (); ti != table.end (); ++ti) + { + symbol_record &ours = ti->second; + symbol_record parents; + if (! ours.is_formal () + && nest_parent->look_nonlocal (ti->first, parents)) + { + if (ours.is_global () || ours.is_persistent ()) + ::error ("global and persistent may only be used in the topmost level in which a nested variable is used"); + + if (! ours.is_formal ()) + { + ours.invalidate (); + ti->second = parents; + } + } + else + ours.set_curr_fcn (curr_fcn); + } + } + else if (nest_children.size()) + for (table_iterator ti = table.begin (); ti != table.end (); ++ti) + ti->second.set_curr_fcn (curr_fcn); + + for (std::vector::iterator iter = nest_children.begin (); + iter != nest_children.end (); ++iter) + (*iter)->do_update_nest (); +} + DEFUN (ignore_function_time_stamp, args, nargout, "-*- texinfo -*-\n\ @deftypefn {Built-in Function} {@var{val} =} ignore_function_time_stamp ()\n\ diff --git a/src/symtab.h b/src/symtab.h --- a/src/symtab.h +++ b/src/symtab.h @@ -207,21 +207,25 @@ public: symbol_record_rep (const std::string& nm, const octave_value& v, - unsigned int sc) - : name (nm), value_stack (), storage_class (sc), finfo (), count (1) + unsigned int sc, scope_id s) + : decl_scope (s), curr_fcn (0), name (nm), value_stack (), + storage_class (sc), finfo (), valid (true), count (1) { value_stack.push_back (v); } - void force_variable (context_id context) + void force_variable (context_id context = xdefault_context) { + if (context == xdefault_context) + context = active_context (); + octave_value& val = varref (context); if (! val.is_defined ()) mark_forced (); } - octave_value& varref (context_id context) + octave_value& varref (context_id context = xdefault_context) { if (is_global ()) return symbol_table::global_varref (name); @@ -229,6 +233,9 @@ return symbol_table::persistent_varref (name); else { + if (context == xdefault_context) + context = active_context (); + context_id n = value_stack.size (); while (n++ <= context) value_stack.push_back (octave_value ()); @@ -237,7 +244,7 @@ } } - octave_value varval (context_id context) const + octave_value varval (context_id context = xdefault_context) const { if (is_global ()) return symbol_table::global_varval (name); @@ -245,6 +252,9 @@ return symbol_table::persistent_varval (name); else { + if (context == xdefault_context) + context = active_context (); + if (context < value_stack.size ()) return value_stack[context]; else @@ -252,9 +262,10 @@ } } - void push_context (void) + void push_context (scope_id s) { - if (! (is_persistent () || is_global ())) + if (! (is_persistent () || is_global ()) + && s == scope ()) value_stack.push_back (octave_value ()); } @@ -272,11 +283,12 @@ // // Here, X should only exist in the final stack frame. - size_t pop_context (void) + size_t pop_context (scope_id s) { size_t retval = 1; - if (! (is_persistent () || is_global ())) + if (! (is_persistent () || is_global ()) + && s == scope ()) { value_stack.pop_back (); retval = value_stack.size (); @@ -285,9 +297,12 @@ return retval; } - void clear (void) + void clear (void) { clear (scope ()); } + + void clear (scope_id s) { - if (! (is_hidden () || is_inherited ())) + if (! (is_hidden () || is_inherited ()) + && s == scope ()) { if (is_global ()) unmark_global (); @@ -295,22 +310,33 @@ if (is_persistent ()) { symbol_table::persistent_varref (name) - = varval (xcurrent_context); + = varval (); unmark_persistent (); } - varref (xcurrent_context) = octave_value (); + varref () = octave_value (); } } - bool is_defined (context_id context) const + bool is_defined (context_id context = xdefault_context) const { + if (context == xdefault_context) + context = active_context (); + return varval (context).is_defined (); } + bool is_valid (void) const + { + return valid; + } + bool is_variable (context_id context) const { + if (context == xdefault_context) + context = active_context (); + return (! is_local () || is_defined (context) || is_forced ()); } @@ -355,31 +381,49 @@ void init_persistent (void) { - if (! is_defined (xcurrent_context)) + if (! is_defined ()) { mark_persistent (); - varref (xcurrent_context) = symbol_table::persistent_varval (name); + varref () = symbol_table::persistent_varval (name); } // FIXME -- this causes trouble with recursive calls. // else // error ("unable to declare existing variable persistent"); } + void invalidate (void) + { + valid = false; + } + void erase_persistent (void) { unmark_persistent (); symbol_table::erase_persistent (name); } - symbol_record_rep *dup (void) const + context_id active_context (void) const; + + scope_id scope (void) const { return decl_scope; } + + void set_curr_fcn (octave_user_function *fcn) { - return new symbol_record_rep (name, varval (xcurrent_context), - storage_class); + curr_fcn = fcn; } + symbol_record_rep *dup (scope_id new_scope) const + { + return new symbol_record_rep (name, varval (), storage_class, + new_scope); + } + void dump (std::ostream& os, const std::string& prefix) const; + scope_id decl_scope; + + octave_user_function* curr_fcn; + std::string name; std::deque value_stack; @@ -388,6 +432,8 @@ fcn_info *finfo; + bool valid; + octave_refcount count; private: @@ -403,8 +449,8 @@ symbol_record (const std::string& nm = std::string (), const octave_value& v = octave_value (), - unsigned int sc = local) - : rep (new symbol_record_rep (nm, v, sc)) { } + unsigned int sc = local, scope_id s = xcurrent_scope) + : rep (new symbol_record_rep (nm, v, sc, s)) { } symbol_record (const symbol_record& sr) : rep (sr.rep) @@ -432,39 +478,50 @@ delete rep; } - symbol_record dup (void) const { return symbol_record (rep->dup ()); } + symbol_record dup (scope_id new_scope) const + { + return symbol_record (rep->dup (new_scope)); + } std::string name (void) const { return rep->name; } - octave_value find (const octave_value_list& args = octave_value_list ()) const; - - void force_variable (context_id context = xcurrent_context) + octave_value + find (const octave_value_list& args = octave_value_list ()) const; + + void force_variable (context_id context = xdefault_context) { rep->force_variable (context); } - octave_value& varref (context_id context = xcurrent_context) + octave_value& varref (context_id context = xdefault_context) { return rep->varref (context); } - octave_value varval (context_id context = xcurrent_context) const + octave_value varval (context_id context = xdefault_context) const { return rep->varval (context); } - void push_context (void) { rep->push_context (); } - - size_t pop_context (void) { return rep->pop_context (); } + void push_context (scope_id s) { rep->push_context (s); } + + size_t pop_context (scope_id s) { return rep->pop_context (s); } void clear (void) { rep->clear (); } - bool is_defined (context_id context = xcurrent_context) const + void clear (scope_id s) { rep->clear (s); } + + bool is_defined (context_id context = xdefault_context) const { return rep->is_defined (context); } - bool is_variable (context_id context = xcurrent_context) const + bool is_valid (void) const + { + return rep->is_valid (); + } + + bool is_variable (context_id context = xdefault_context) const { return rep->is_variable (context); } @@ -500,8 +557,16 @@ void erase_persistent (void) { rep->erase_persistent (); } + void invalidate (void) { rep->invalidate (); } + + context_id active_context (void) const { return rep->active_context (); } + + scope_id scope (void) const { return rep->scope (); } + unsigned int xstorage_class (void) const { return rep->storage_class; } + void set_curr_fcn (octave_user_function *fcn) { rep->set_curr_fcn (fcn); } + void dump (std::ostream& os, const std::string& prefix = std::string ()) const { @@ -928,7 +993,7 @@ if (p == all_instances.end ()) { - symbol_table *inst = new symbol_table (); + symbol_table *inst = new symbol_table (scope); if (inst) all_instances[scope] = instance = inst; @@ -1009,7 +1074,7 @@ { scope_id new_scope = alloc_scope (); - symbol_table *new_symbol_table = new symbol_table (); + symbol_table *new_symbol_table = new symbol_table (scope); if (new_symbol_table) { @@ -1034,7 +1099,8 @@ { symbol_table *inst = get_instance (scope); - return inst ? inst->do_find_symbol (name) : symbol_record (); + return inst ? inst->do_find_symbol (name) : + symbol_record ("", octave_value (), symbol_record::local, scope); } static void @@ -1074,7 +1140,7 @@ static void force_variable (const std::string& name, scope_id scope = xcurrent_scope, - context_id context = xcurrent_context) + context_id context = xdefault_context) { symbol_table *inst = get_instance (scope); @@ -1084,7 +1150,7 @@ static octave_value& varref (const std::string& name, scope_id scope = xcurrent_scope, - context_id context = xcurrent_context) + context_id context = xdefault_context) { static octave_value foobar; @@ -1095,7 +1161,7 @@ static octave_value varval (const std::string& name, scope_id scope = xcurrent_scope, - context_id context = xcurrent_context) + context_id context = xdefault_context) { symbol_table *inst = get_instance (scope); @@ -1263,6 +1329,17 @@ } } + 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) { @@ -1620,7 +1697,7 @@ static std::list all_variables (scope_id scope = xcurrent_scope, - context_id context = xcurrent_context, + context_id context = xdefault_context, bool defined_only = true) { symbol_table *inst = get_instance (scope); @@ -1673,7 +1750,8 @@ if (pat.match (p->first)) retval.push_back (symbol_record (p->first, p->second, - symbol_record::global)); + symbol_record::global, + xglobal_scope)); } return retval; @@ -1695,7 +1773,8 @@ if (pat.is_match (p->first)) retval.push_back (symbol_record (p->first, p->second, - symbol_record::global)); + symbol_record::global, + xglobal_scope)); } return retval; @@ -1922,11 +2001,8 @@ { assert (scope != xtop_scope && scope != xglobal_scope); symbol_table *inst = get_instance (scope); - // FIXME: normally, functions should not usurp each other's scope. - // If for any incredible reason this is needed, call - // set_user_function (0, scope) first. - assert (inst->curr_fcn == 0 || curr_fcn == 0); - inst->curr_fcn = curr_fcn; + if (inst) + inst->do_set_curr_fcn (curr_fcn); } static void cleanup (void); @@ -1953,6 +2029,9 @@ typedef std::map::const_iterator fcn_table_const_iterator; typedef std::map::iterator fcn_table_iterator; + + // The scope of this symbol table. + scope_id my_scope; // Name for this table (usually the file name of the function // corresponding to the scope); @@ -1961,6 +2040,12 @@ // Map from symbol names to symbol info. std::map table; + // Child nested functions. + std::vector nest_children; + + // Parent nested function (may be null). + symbol_table *nest_parent; + // The associated user code (may be null). octave_user_function *curr_fcn; @@ -2000,10 +2085,17 @@ static context_id xcurrent_context; - symbol_table (void) - : table_name (), table (), curr_fcn (0), persistent_table () { } - - ~symbol_table (void) { } + static const context_id xdefault_context = static_cast (-1); + + symbol_table (scope_id scope) + : my_scope (scope), table_name (), table (), nest_children (), nest_parent (0), + curr_fcn (0), persistent_table () { } + + ~symbol_table (void) + { + // prevent using a symbol_record with a bad curr_fcn + // do_set_curr_fcn (0); + } static symbol_table *get_instance (scope_id scope, bool create = true) { @@ -2017,7 +2109,7 @@ { if (! instance && create) { - symbol_table *inst = new symbol_table (); + symbol_table *inst = new symbol_table (scope); if (inst) { @@ -2041,7 +2133,7 @@ { if (create) { - retval = new symbol_table (); + retval = new symbol_table (scope); if (retval) all_instances[scope] = retval; @@ -2063,6 +2155,13 @@ return retval; } + void add_nest_child (symbol_table& st) + { + assert (!st.nest_parent); + nest_children.push_back (&st); + st.nest_parent = this; + } + void insert_symbol_record (const symbol_record& sr) { table[sr.name ()] = sr; @@ -2072,7 +2171,7 @@ 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.insert_symbol_record (p->second.dup (new_symbol_table.my_scope)); } symbol_record do_find_symbol (const std::string& name) @@ -2126,8 +2225,18 @@ { table_iterator p = table.find (name); - return p == table.end () - ? (table[name] = symbol_record (name)) : p->second; + if (p == table.end ()) + { + symbol_record parent_symbol; + + if (nest_parent && nest_parent->look_nonlocal (name, parent_symbol)) + return table[name] = parent_symbol; + else + return table[name] = symbol_record (name, octave_value (), + symbol_record::local, my_scope); + } + else + return p->second; } void do_force_variable (const std::string& name, context_id context) @@ -2156,13 +2265,17 @@ } else return p->second.varref (context); + } octave_value do_varval (const std::string& name, context_id context) const { table_const_iterator p = table.find (name); - return (p != table.end ()) ? p->second.varval (context) : octave_value (); + if (p != table.end ()) + return p->second.varval (context); + + return octave_value (); } octave_value& do_persistent_varref (const std::string& name) @@ -2207,15 +2320,15 @@ void do_push_context (void) { for (table_iterator p = table.begin (); p != table.end (); p++) - p->second.push_context (); + p->second.push_context (my_scope); } void do_pop_context (void) { for (table_iterator p = table.begin (); p != table.end (); ) { - if (p->second.pop_context () == 0) - table.erase (p++); + if (p->second.pop_context (my_scope) == 0) + table.erase (p++); else p++; } @@ -2224,7 +2337,7 @@ void do_clear_variables (void) { for (table_iterator p = table.begin (); p != table.end (); p++) - p->second.clear (); + p->second.clear (my_scope); } void do_clear_objects (void) @@ -2234,7 +2347,7 @@ symbol_record& sr = p->second; octave_value& val = sr.varref (); if (val.is_object()) - p->second.clear (); + p->second.clear (my_scope); } } @@ -2268,7 +2381,7 @@ table_iterator p = table.find (name); if (p != table.end ()) - p->second.clear (); + p->second.clear (my_scope); } void do_clear_global_pattern (const std::string& pat) @@ -2308,7 +2421,7 @@ if (sr.is_defined () || sr.is_global ()) { if (pattern.match (sr.name ())) - sr.clear (); + sr.clear (my_scope); } } } @@ -2324,7 +2437,7 @@ if (sr.is_defined () || sr.is_global ()) { if (pattern.is_match (sr.name ())) - sr.clear (); + sr.clear (my_scope); } } } @@ -2352,7 +2465,7 @@ for (table_const_iterator p = table.begin (); p != table.end (); p++) { const symbol_record& sr = p->second; - + if (defined_only && ! sr.is_defined (context)) continue; @@ -2462,6 +2575,42 @@ void do_dump (std::ostream& os); void do_cache_name (const std::string& name) { table_name = name; } + + void do_update_nest (void); + + void do_set_curr_fcn (octave_user_function *fcn) + { + // FIXME: normally, functions should not usurp each other's scope. + // If for any incredible reason this is needed, call + // set_user_function (0, scope) first. + assert (curr_fcn == 0 || fcn == 0); + curr_fcn = fcn; + + // incase of an usurp we need to update curr_fcn of symbol_records + // for (table_iterator ti = table.begin (); ti != table.end (); ++ti) + // { + // symbol_record &r = ti->second; + // if (r.scope () == my_scope) + // r.set_curr_fcn (curr_fcn); + // } + } + + bool look_nonlocal (const std::string& name, symbol_record& result) + { + table_iterator p = table.find (name); + if (p == table.end ()) + { + if (nest_parent) + return nest_parent->look_nonlocal (name, result); + } + else if (! p->second.is_automatic ()) + { + result = p->second; + return true; + } + + return false; + } }; extern bool out_of_date_check (octave_value& function, diff --git a/test/nest/arg_nest.m b/test/nest/arg_nest.m new file mode 100644 --- /dev/null +++ b/test/nest/arg_nest.m @@ -0,0 +1,8 @@ +# arg_nest.m +function x = arg_nest + x = 1; + A (x); + function A (x) + x = 2; + endfunction +endfunction diff --git a/test/nest/arg_ret.m b/test/nest/arg_ret.m new file mode 100644 --- /dev/null +++ b/test/nest/arg_ret.m @@ -0,0 +1,7 @@ +function a = arg_ret + a = 10; + f; + function a = f + a = 5; + endfunction +endfunction diff --git a/test/nest/module.mk b/test/nest/module.mk new file mode 100644 --- /dev/null +++ b/test/nest/module.mk @@ -0,0 +1,17 @@ +nest_FCN_FILES = \ + nest/arg_nest.m \ + nest/arg_ret.m \ + nest/no_closure.m \ + nest/recursive_nest.m \ + nest/recursive_nest2.m \ + nest/recursive_nest3.m \ + nest/scope0.m \ + nest/scope1.m \ + nest/scope2.m \ + nest/script_nest.m \ + nest/script_nest_script.m \ + nest/test_nest.m \ + nest/varg_nest.m \ + nest/varg_nest2.m + +FCN_FILES += $(nest_FCN_FILES) diff --git a/test/nest/no_closure.m b/test/nest/no_closure.m new file mode 100644 --- /dev/null +++ b/test/nest/no_closure.m @@ -0,0 +1,11 @@ +# no_closure.m +function no_closure (n) + if n == 0 + x = @no_closure; + else + f = @no_closure; + endif + + function f + endfunction +endfunction diff --git a/test/nest/persistent_nest.m b/test/nest/persistent_nest.m new file mode 100644 --- /dev/null +++ b/test/nest/persistent_nest.m @@ -0,0 +1,10 @@ +# persistent_nest +function y = persistent_nest () + persistent x = 0; + g; + y = x; + + function g + x = x + 1; + end +end diff --git a/test/nest/recursive_nest.m b/test/nest/recursive_nest.m new file mode 100644 --- /dev/null +++ b/test/nest/recursive_nest.m @@ -0,0 +1,13 @@ +# recursive_nest.m +function x = recursive_nest () + global recursive_nest_inc = 1 + x = 5; + f (20); + + function f (n) + if n > 0 + x = x + recursive_nest_inc; + f (n - 1); + end + endfunction +endfunction diff --git a/test/nest/recursive_nest2.m b/test/nest/recursive_nest2.m new file mode 100644 --- /dev/null +++ b/test/nest/recursive_nest2.m @@ -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 --git a/test/nest/recursive_nest3.m b/test/nest/recursive_nest3.m new file mode 100644 --- /dev/null +++ b/test/nest/recursive_nest3.m @@ -0,0 +1,13 @@ +function x = recursive_nest3 () + y = 5; + f (y); + x = y; + g (x); + function f (y) + y = 10; + endfunction + + function g (x) + x = 10; + endfunction +endfunction diff --git a/test/nest/scope0.m b/test/nest/scope0.m new file mode 100644 --- /dev/null +++ b/test/nest/scope0.m @@ -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 diff --git a/test/nest/scope1.m b/test/nest/scope1.m new file mode 100644 --- /dev/null +++ b/test/nest/scope1.m @@ -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 diff --git a/test/nest/scope2.m b/test/nest/scope2.m new file mode 100644 --- /dev/null +++ b/test/nest/scope2.m @@ -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 --git a/test/nest/scope3.m b/test/nest/scope3.m new file mode 100644 --- /dev/null +++ b/test/nest/scope3.m @@ -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 diff --git a/test/nest/script_nest.m b/test/nest/script_nest.m new file mode 100644 --- /dev/null +++ b/test/nest/script_nest.m @@ -0,0 +1,11 @@ +# script_nest.m +function x = script_nest + A (5) + function A (n) + if n <= 0 + script_nest_script; + else + A (n - 1); + endif + endfunction +endfunction diff --git a/test/nest/script_nest_script.m b/test/nest/script_nest_script.m new file mode 100644 --- /dev/null +++ b/test/nest/script_nest_script.m @@ -0,0 +1,2 @@ +# script_nest_script.m +x = 5; diff --git a/test/nest/test_nest.m b/test/nest/test_nest.m new file mode 100644 --- /dev/null +++ b/test/nest/test_nest.m @@ -0,0 +1,46 @@ +## 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 +## . + +%!assert (recursive_nest (), 25); + +%!assert (recursive_nest2 (), 20); + +%!assert (recursive_nest3 (), 5); + +%!assert (script_nest (), 5); + +%!assert (arg_ret (), 10); + +%!assert (varg_nest (-1), 6); + +%!assert (varg_nest2, 5); + +%!test +%! scope0; + +%!test +%! scope1 (1); + +%!test +%! scope3; + +%!assert (arg_nest, 1); + +%!error scope2; +%!error no_closure (0); +%!error no_closure (1); diff --git a/test/nest/varg_nest.m b/test/nest/varg_nest.m new file mode 100644 --- /dev/null +++ b/test/nest/varg_nest.m @@ -0,0 +1,11 @@ +function x = varg_nest (varargin) + x = abs (f (-5)) + g; + + function x = f (varargin) + x = abs (varargin{1}); + endfunction + + function x = g + x = abs (varargin{1}); + endfunction +endfunction diff --git a/test/nest/varg_nest2.m b/test/nest/varg_nest2.m new file mode 100644 --- /dev/null +++ b/test/nest/varg_nest2.m @@ -0,0 +1,14 @@ +function x = varg_nest2 + [a, b] = f; + x = a; + + if nargout == 1 + x = a; + endif + + function a, b = f + if nargout == 2 + a = b = 5; + endif + endfunction +endfunction