Mercurial > hg > octave-terminal
changeset 14544:be18c9e359bf
Nested function support (bug #35772)
* src/oct-parse.yy (push_fcn_symtab, recover_from_parsing_function)
(eval_string): Keep track of a stack of nested functions.
(parse_fcn_file): Keep track of a stack of nested functions and remove
warning that nested functions are not supported.
(frob_function): Call symbol_table::install_nestfunction for nested functions.
(finish_function): Call symbol_table::update_nest on top level functions.
* src/ov-fcn-handle.cc (octave_fcn_handle::octave_fcn_handle):
Error when creating a handle to a nested function.
* src/ov-usr-fcn.cc (octave_user_function::octave_user_function):
Initialize new members.
(octave_user_function::do_multi_index_op): Use active_context to
determine execution context.
* src/ov-usr-fcn.h (octave_user_function::active_context,
octave_user_function::is_nested_function,
octave_user_function::mark_as_nested_function): New functions.
* src/pt-id.h (symbol_table::xsym): Check symbol validity.
* src/symtab.cc (symbol_table::symbol_record::symbol_record_rep::active_context,
symbol_table::install_nestfunction, symbol_table::do_update_nest):
New functions.
(symbol_table::symbol_record::symbol_record_rep::dump): Use varval ()
instead of varval (current_context).
(symbol_table::fcn_info::fcn_info_rep::xfind,
symbol_table::fcn_info::fcn_info_rep::x_builtin_find): Allow for
parents of parents in subfunction search.
* src/symtab.h (symbol_table::symbol_record::symbol_record_rep::symbol_record_rep,
symbol_table::symbol_record::symbol_record): New parameter,
decl_scope.
(symbol_table::symbol_record::symbol_record_rep::force_variable,
symbol_table::symbol_record::force_variable,
symbol_table::symbol_record::symbol_record_rep::varref,
symbol_table::symbol_record::varref,
symbol_table::symbol_record::symbol_record_rep::varval,
symbol_table::symbol_record::varval,
symbol_table::symbol_record::symbol_record_rep::is_defined,
symbol_table::symbol_record::is_defined,
symbol_table::symbol_record::symbol_record_rep::is_variable,
symbol_table::symbol_record::is_variable,
symbol_table::symbol_record::varval,
symbol_table::symbol_record::symbol_record_rep::varval): Use xdefault_context.
symbol_table::symbol_record::symbol_record_rep::push_context,
symbol_table::symbol_record::push_context,
symbol_table::symbol_record::symbol_record_rep::pop_context,
symbol_table::symbol_record::pop_context,
symbol_table::symbol_record::symbol_record_rep::clear,
symbol_table::symbol_record::clear): Only work when the decl_scope of
the symbol is the active scope.
(symbol_table::symbol_record::symbol_record_rep::is_valid,
symbol_table::symbol_record::is_valid,
symbol_table::symbol_record::symbol_record_rep::invalidate,
symbol_table::symbol_record::invalidate,
symbol_table::symbol_record::symbol_record_rep::set_curr_fcn,
symbol_table::symbol_record::set_curr_fcn,
symbol_table::symbol_record::symbol_record_rep::scope,
symbol_table::symbol_record::scope,
symbol_table::symbol_record::active_context,
symbol_table::update_nest, symbol_table::symbol_table,
symbol_table::add_nest_child, symbol_table::look_nonlocal): New functions.
(symbol_table::symbol_record::symbol_record_rep::init_persistent):
Init to xdefault_context instead of xcurrent_context.
(symbol_table::symbol_record::symbol_record_rep::dup,
symbol_table::symbol_record::dup): New parameter, scope.
(symbol_table::set_scope, symbol_table::dup_scope,
symbol_table:;get_instance): Pass scope_id to symbol_table constructor.
(symbol_table::find_symbol, symbol_table::glob_global_variables,
symbol_table::regexp_global_variables): Specify scope when creating
symbol_record.
(symbol_table::force_variable, symbol_table::varref,
symbol_table::varval, symbol_table::all_variables): Default to
xdefault_context instead of xcurrent_context.
(symbol_table::do_dup_scope): Pass scope of new symbol table to symbol
dup.
(symbol_table::do_insert): Check nest_parent for nonlocals.
(symbol_table::do_push_context, symbol_table::do_pop_context,
symbol_table::do_clear_variables, symbol_table::do_clear_objects,
symbol_table::do_clear_variable, symbol_table::do_clear_variable_pattern,
symbol_table::do_clear_variable_regexp): Pass my_scope to symbol.
* test/Makefile.am: Include nest/module.mk.
* test/nest/arg_nest.m: New file.
* test/nest/arg_ret.m: New file.
* test/nest/module.mk: New file.
* test/nest/no_closure.m: New file.
* test/nest/persistent_nest.m: New file.
* test/nest/recursive_nest.m: New file.
* test/nest/recursive_nest2.m: New file.
* test/nest/recursive_nest3.m: New file.
* test/nest/scope0.m: New file.
* test/nest/scope1.m: New file.
* test/nest/scope2.m: New file.
* test/nest/scope3.m: New file.
* test/nest/script_nest.m: New file.
* test/nest/script_nest_script.m: New file.
* test/nest/test_nest.m: New file.
* test/nest/varg_nest.m: New file.
* test/nest/varg_nest2.m: New file.
author | Max Brister <max@2bass.com> |
---|---|
date | Tue, 10 Apr 2012 20:42:22 -0400 |
parents | e47d929fde8f |
children | 1f922eedf9ce |
files | NEWS src/oct-parse.yy src/ov-fcn-handle.cc src/ov-usr-fcn.cc src/ov-usr-fcn.h src/pt-id.h src/symtab.cc src/symtab.h test/Makefile.am test/nest/arg_nest.m test/nest/arg_ret.m test/nest/module.mk test/nest/no_closure.m test/nest/persistent_nest.m test/nest/recursive_nest.m test/nest/recursive_nest2.m test/nest/recursive_nest3.m test/nest/scope0.m test/nest/scope1.m test/nest/scope2.m test/nest/scope3.m test/nest/script_nest.m test/nest/script_nest_script.m test/nest/test_nest.m test/nest/varg_nest.m test/nest/varg_nest2.m |
diffstat | 26 files changed, 590 insertions(+), 118 deletions(-) [+] |
line wrap: on
line diff
--- a/NEWS +++ b/NEWS @@ -18,6 +18,9 @@ strings. The regexprep function now processes backslash escapes in single-quoted replacement strings. + ** Octave now supports nested functions with scoping rules that are + compatible with Matlab. + ** Redundant terminal comma accepted by parser A redundant terminal comma is now accepted in matrix
--- 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<symbol_table::scope_id> 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 (); @@ -2867,7 +2874,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) @@ -2943,10 +2954,22 @@ { 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 @@ -2985,6 +3008,7 @@ parsing_subfunctions = true; current_function_depth--; + function_scopes.pop_back (); lexer_flags.defining_func--; lexer_flags.parsed_function_name.pop (); @@ -3454,6 +3478,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); @@ -3464,6 +3489,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; @@ -3582,11 +3608,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 { @@ -4298,6 +4319,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); @@ -4312,6 +4334,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;
--- 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
--- 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);
--- 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<symbol_table::context_id>(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;
--- a/src/pt-id.h +++ b/src/pt-id.h @@ -128,7 +128,7 @@ { 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 ());
--- 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<context_id> (-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. - - return r->second; - } - else - { - if (curr_fcn) + scope_val_iterator r = subfunctions.find (scope); + if (r != subfunctions.end ()) { - // 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? + // FIXME -- out-of-date check here. - scope_id pscope = curr_fcn->parent_fcn_scope (); + return r->second; + } - 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); - - if (r != subfunctions.end ()) + for (scope_id scope = xcurrent_scope; scope >= 0;) { - // FIXME -- out-of-date check here. - - return r->second; - } - else if (curr_fcn) - { - scope_id pscope = curr_fcn->parent_fcn_scope (); + scope_val_iterator r = subfunctions.find (scope); + if (r != subfunctions.end ()) + { + // FIXME -- out-of-date check here. - if (pscope > 0) - { - r = subfunctions.find (pscope); + return r->second; + } - 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,23 @@ } } +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(); + + symbol_table *fcn_table = get_instance (fv->scope()); + + symbol_table *parent_table = get_instance (parent_scope); + + parent_table->add_nest_child (*fcn_table); +} + octave_value symbol_table::find (const std::string& name, const octave_value_list& args, @@ -1456,6 +1462,44 @@ } } +void +symbol_table::do_update_nest (void) +{ + 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<symbol_table*>::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\
--- a/src/symtab.h +++ b/src/symtab.h @@ -206,22 +206,26 @@ { 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) + symbol_record_rep (scope_id s, const std::string& nm, + const octave_value& v, unsigned int sc) + : 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), + curr_fcn = fcn; + } + + symbol_record_rep *dup (scope_id new_scope) const + { + return new symbol_record_rep (new_scope, name, varval (), storage_class); } void dump (std::ostream& os, const std::string& prefix) const; + scope_id decl_scope; + + octave_user_function* curr_fcn; + std::string name; std::deque<octave_value> value_stack; @@ -388,6 +432,8 @@ fcn_info *finfo; + bool valid; + octave_refcount<size_t> count; private: @@ -401,10 +447,11 @@ public: - symbol_record (const std::string& nm = std::string (), + symbol_record (scope_id s = xcurrent_scope, + const std::string& nm = std::string (), const octave_value& v = octave_value (), unsigned int sc = local) - : rep (new symbol_record_rep (nm, v, sc)) { } + : rep (new symbol_record_rep (s, nm, v, sc)) { } symbol_record (const symbol_record& sr) : rep (sr.rep) @@ -432,39 +479,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 +558,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 +994,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 +1075,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 +1100,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 (scope); } static void @@ -1074,7 +1141,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 +1151,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 +1162,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 +1330,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 +1698,7 @@ static std::list<symbol_record> 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); @@ -1672,7 +1750,8 @@ // may be handled the same way. if (pat.match (p->first)) - retval.push_back (symbol_record (p->first, p->second, + retval.push_back (symbol_record (xglobal_scope, + p->first, p->second, symbol_record::global)); } @@ -1694,7 +1773,8 @@ // may be handled the same way. if (pat.is_match (p->first)) - retval.push_back (symbol_record (p->first, p->second, + retval.push_back (symbol_record (xglobal_scope, + p->first, p->second, symbol_record::global)); } @@ -1924,7 +2004,8 @@ 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. + // set_user_function (0, scope) first. This may cause problems with + // nested functions, as the curr_fcn of symbol_records must be updated. assert (inst->curr_fcn == 0 || curr_fcn == 0); inst->curr_fcn = curr_fcn; } @@ -1953,6 +2034,9 @@ typedef std::map<std::string, fcn_info>::const_iterator fcn_table_const_iterator; typedef std::map<std::string, fcn_info>::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 +2045,12 @@ // Map from symbol names to symbol info. std::map<std::string, symbol_record> table; + // Child nested functions. + std::vector<symbol_table*> 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,8 +2090,11 @@ static context_id xcurrent_context; - symbol_table (void) - : table_name (), table (), curr_fcn (0), persistent_table () { } + static const context_id xdefault_context = static_cast<context_id> (-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) { } @@ -2017,7 +2110,7 @@ { if (! instance && create) { - symbol_table *inst = new symbol_table (); + symbol_table *inst = new symbol_table (scope); if (inst) { @@ -2041,7 +2134,7 @@ { if (create) { - retval = new symbol_table (); + retval = new symbol_table (scope); if (retval) all_instances[scope] = retval; @@ -2063,6 +2156,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 +2172,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 +2226,17 @@ { 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 (my_scope, name, octave_value ()); + } + else + return p->second; } void do_force_variable (const std::string& name, context_id context) @@ -2207,15 +2316,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 +2333,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 +2343,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 +2377,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 +2417,7 @@ if (sr.is_defined () || sr.is_global ()) { if (pattern.match (sr.name ())) - sr.clear (); + sr.clear (my_scope); } } } @@ -2324,7 +2433,7 @@ if (sr.is_defined () || sr.is_global ()) { if (pattern.is_match (sr.name ())) - sr.clear (); + sr.clear (my_scope); } } } @@ -2462,6 +2571,25 @@ void do_dump (std::ostream& os); void do_cache_name (const std::string& name) { table_name = name; } + + void do_update_nest (void); + + 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,
--- a/test/Makefile.am +++ b/test/Makefile.am @@ -56,6 +56,7 @@ include class-concat/module.mk include ctor-vs-method/module.mk include fcn-handle-derived-resolution/module.mk +include nest/module.mk check: test_sparse.m test_bc_overloads.m $(top_builddir)/run-octave --norc --silent --no-history $(srcdir)/fntests.m $(srcdir)
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
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
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)
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
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
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
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
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
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
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
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
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
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
new file mode 100644 --- /dev/null +++ b/test/nest/script_nest_script.m @@ -0,0 +1,2 @@ +# script_nest_script.m +x = 5;
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 +## <http://www.gnu.org/licenses/>. + +%!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 <D' undefined near line 7> scope2; +%!error <handles to nested functions are not yet supported> no_closure (0); +%!error <handles to nested functions are not yet supported> no_closure (1);