Mercurial > octave
diff libinterp/interpfcn/symtab.cc @ 15195:2fc554ffbc28
split libinterp from src
* libinterp: New directory. Move all files from src directory here
except Makefile.am, main.cc, main-cli.cc, mkoctfile.in.cc,
mkoctfilr.in.sh, octave-config.in.cc, octave-config.in.sh.
* libinterp/Makefile.am: New file, extracted from src/Makefile.am.
* src/Makefile.am: Delete everything except targets and definitions
needed to build and link main and utility programs.
* Makefile.am (SUBDIRS): Include libinterp in the list.
* autogen.sh: Run config-module.sh in libinterp/dldfcn directory, not
src/dldfcn directory.
* configure.ac (AC_CONFIG_SRCDIR): Use libinterp/octave.cc, not
src/octave.cc.
(DL_LDFLAGS, LIBOCTINTERP): Use libinterp, not src.
(AC_CONFIG_FILES): Include libinterp/Makefile in the list.
* find-docstring-files.sh: Look in libinterp, not src.
* gui/src/Makefile.am (liboctgui_la_CPPFLAGS): Find header files in
libinterp, not src.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Sat, 18 Aug 2012 16:23:39 -0400 |
parents | src/interpfcn/symtab.cc@60ff2cef569d |
children | 44d6ffdf9479 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libinterp/interpfcn/symtab.cc Sat Aug 18 16:23:39 2012 -0400 @@ -0,0 +1,1744 @@ +/* + +Copyright (C) 1993-2012 John W. Eaton +Copyright (C) 2009 VZLU Prague, a.s. + +This file is part of Octave. + +Octave is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the +Free Software Foundation; either version 3 of the License, or (at your +option) any later version. + +Octave is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with Octave; see the file COPYING. If not, see +<http://www.gnu.org/licenses/>. + +*/ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "file-ops.h" +#include "file-stat.h" +#include "oct-env.h" +#include "oct-time.h" +#include "singleton-cleanup.h" + +#include "debug.h" +#include "defun.h" +#include "dirfns.h" +#include "input.h" +#include "load-path.h" +#include "ov-fcn.h" +#include "ov-usr-fcn.h" +#include "pager.h" +#include "parse.h" +#include "pt-arg-list.h" +#include "symtab.h" +#include "unwind-prot.h" +#include "utils.h" + +symbol_table *symbol_table::instance = 0; + +symbol_table::scope_id_cache *symbol_table::scope_id_cache::instance = 0; + +std::map<symbol_table::scope_id, symbol_table*> symbol_table::all_instances; + +std::map<std::string, octave_value> symbol_table::global_table; + +std::map<std::string, symbol_table::fcn_info> symbol_table::fcn_table; + +std::map<std::string, std::set<std::string> > symbol_table::class_precedence_table; + +std::map<std::string, std::list<std::string> > symbol_table::parent_map; + +const symbol_table::scope_id symbol_table::xglobal_scope = 0; +const symbol_table::scope_id symbol_table::xtop_scope = 1; + +symbol_table::scope_id symbol_table::xcurrent_scope = 1; + +symbol_table::context_id symbol_table::xcurrent_context = 0; + +// Should Octave always check to see if function files have changed +// since they were last compiled? +static int Vignore_function_time_stamp = 1; + +void +symbol_table::scope_id_cache::create_instance (void) +{ + instance = new scope_id_cache (); + + 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 (); + + os << prefix << name; + + if (val.is_defined ()) + { + os << " [" + << (is_local () ? "l" : "") + << (is_automatic () ? "a" : "") + << (is_formal () ? "f" : "") + << (is_hidden () ? "h" : "") + << (is_inherited () ? "i" : "") + << (is_global () ? "g" : "") + << (is_persistent () ? "p" : "") + << "] "; + val.dump (os); + } + + os << "\n"; +} + +octave_value +symbol_table::symbol_record::find (const octave_value_list& args) const +{ + octave_value retval; + + if (is_global ()) + retval = symbol_table::global_varref (name ()); + else + { + retval = varval (); + + if (retval.is_undefined ()) + { + // Use cached fcn_info pointer if possible. + if (rep->finfo) + retval = rep->finfo->find (args); + else + { + retval = symbol_table::find_function (name (), args); + + if (retval.is_defined ()) + rep->finfo = get_fcn_info (name ()); + } + } + } + + return retval; +} + +// Check the load path to see if file that defined this is still +// visible. If the file is no longer visible, then erase the +// definition and move on. If the file is visible, then we also +// need to check to see whether the file has changed since the the +// function was loaded/parsed. However, this check should only +// happen once per prompt (for files found from relative path +// elements, we also check if the working directory has changed +// since the last time the function was loaded/parsed). +// +// FIXME -- perhaps this should be done for all loaded functions when +// the prompt is printed or the directory has changed, and then we +// would not check for it when finding symbol definitions. + +static inline bool +load_out_of_date_fcn (const std::string& ff, const std::string& dir_name, + octave_value& function, + const std::string& dispatch_type = std::string ()) +{ + bool retval = false; + + octave_function *fcn = load_fcn_from_file (ff, dir_name, dispatch_type); + + if (fcn) + { + retval = true; + + function = octave_value (fcn); + } + else + function = octave_value (); + + return retval; +} + +bool +out_of_date_check (octave_value& function, + const std::string& dispatch_type, + bool check_relative) +{ + bool retval = false; + + octave_function *fcn = function.function_value (true); + + if (fcn) + { + // FIXME -- we need to handle subfunctions properly here. + + if (! fcn->is_subfunction ()) + { + std::string ff = fcn->fcn_file_name (); + + if (! ff.empty ()) + { + octave_time tc = fcn->time_checked (); + + bool relative = check_relative && fcn->is_relative (); + + if (tc < Vlast_prompt_time + || (relative && tc < Vlast_chdir_time)) + { + bool clear_breakpoints = false; + std::string nm = fcn->name (); + + bool is_same_file = false; + + std::string file; + std::string dir_name; + + if (check_relative) + { + int nm_len = nm.length (); + + if (octave_env::absolute_pathname (nm) + && ((nm_len > 4 && (nm.substr (nm_len-4) == ".oct" + || nm.substr (nm_len-4) == ".mex")) + || (nm_len > 2 && nm.substr (nm_len-2) == ".m"))) + file = nm; + else + { + // We don't want to make this an absolute name, + // because load_fcn_file looks at the name to + // decide whether it came from a relative lookup. + + if (! dispatch_type.empty ()) + { + file = load_path::find_method (dispatch_type, nm, + dir_name); + + if (file.empty ()) + { + const std::list<std::string>& plist + = symbol_table::parent_classes (dispatch_type); + std::list<std::string>::const_iterator it + = plist.begin (); + + while (it != plist.end ()) + { + file = load_path::find_method (*it, nm, dir_name); + if (! file.empty ()) + break; + + it++; + } + } + } + + // Maybe it's an autoload? + if (file.empty ()) + file = lookup_autoload (nm); + + if (file.empty ()) + file = load_path::find_fcn (nm, dir_name); + } + + if (! file.empty ()) + is_same_file = same_file (file, ff); + } + else + { + is_same_file = true; + file = ff; + } + + if (file.empty ()) + { + // Can't see this function from current + // directory, so we should clear it. + + function = octave_value (); + + clear_breakpoints = true; + } + else if (is_same_file) + { + // Same file. If it is out of date, then reload it. + + octave_time ottp = fcn->time_parsed (); + time_t tp = ottp.unix_time (); + + fcn->mark_fcn_file_up_to_date (octave_time ()); + + if (! (Vignore_function_time_stamp == 2 + || (Vignore_function_time_stamp + && fcn->is_system_fcn_file ()))) + { + file_stat fs (ff); + + if (fs) + { + if (fs.is_newer (tp)) + { + retval = load_out_of_date_fcn (ff, dir_name, + function, + dispatch_type); + + clear_breakpoints = true; + } + } + else + { + function = octave_value (); + + clear_breakpoints = true; + } + } + } + else + { + // Not the same file, so load the new file in + // place of the old. + + retval = load_out_of_date_fcn (file, dir_name, function, + dispatch_type); + + clear_breakpoints = true; + } + + // If the function has been replaced then clear any + // breakpoints associated with it + if (clear_breakpoints) + bp_table::remove_all_breakpoints_in_file (nm, true); + } + } + } + } + + return retval; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::load_private_function + (const std::string& dir_name) +{ + octave_value retval; + + std::string file_name = load_path::find_private_fcn (dir_name, name); + + if (! file_name.empty ()) + { + octave_function *fcn = load_fcn_from_file (file_name, dir_name); + + if (fcn) + { + std::string class_name; + + size_t pos = dir_name.find_last_of (file_ops::dir_sep_chars ()); + + if (pos != std::string::npos) + { + std::string tmp = dir_name.substr (pos+1); + + if (tmp[0] == '@') + class_name = tmp.substr (1); + } + + fcn->mark_as_private_function (class_name); + + retval = octave_value (fcn); + + private_functions[dir_name] = retval; + } + } + + return retval; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::load_class_constructor (void) +{ + octave_value retval; + + std::string dir_name; + + std::string file_name = load_path::find_method (name, name, dir_name); + + if (! file_name.empty ()) + { + octave_function *fcn = load_fcn_from_file (file_name, dir_name, name); + + if (fcn) + { + retval = octave_value (fcn); + + class_constructors[name] = retval; + } + } + + return retval; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::load_class_method + (const std::string& dispatch_type) +{ + octave_value retval; + + if (name == dispatch_type) + retval = load_class_constructor (); + else + { + std::string dir_name; + + std::string file_name = load_path::find_method (dispatch_type, name, + dir_name); + + if (! file_name.empty ()) + { + octave_function *fcn = load_fcn_from_file (file_name, dir_name, + dispatch_type); + + if (fcn) + { + retval = octave_value (fcn); + + class_methods[dispatch_type] = retval; + } + } + + if (retval.is_undefined ()) + { + // Search parent classes + + const std::list<std::string>& plist = parent_classes (dispatch_type); + + std::list<std::string>::const_iterator it = plist.begin (); + + while (it != plist.end ()) + { + retval = find_method (*it); + + if (retval.is_defined ()) + { + class_methods[dispatch_type] = retval; + break; + } + + it++; + } + } + } + + return retval; +} + +void +symbol_table::fcn_info::fcn_info_rep:: mark_subfunction_in_scope_as_private + (scope_id scope, const std::string& class_name) +{ + scope_val_iterator p = subfunctions.find (scope); + + if (p != subfunctions.end ()) + { + octave_function *fcn = p->second.function_value (); + + if (fcn) + fcn->mark_as_private_function (class_name); + } +} + +void +symbol_table::fcn_info::fcn_info_rep::print_dispatch (std::ostream& os) const +{ + if (dispatch_map.empty ()) + os << "dispatch: " << name << " is not overloaded" << std::endl; + else + { + os << "Overloaded function " << name << ":\n\n"; + + for (dispatch_map_const_iterator p = dispatch_map.begin (); + p != dispatch_map.end (); p++) + os << " " << name << " (" << p->first << ", ...) -> " + << p->second << " (" << p->first << ", ...)\n"; + + os << std::endl; + } +} + +std::string +symbol_table::fcn_info::fcn_info_rep::help_for_dispatch (void) const +{ + std::string retval; + + if (! dispatch_map.empty ()) + { + retval = "Overloaded function:\n\n"; + + for (dispatch_map_const_iterator p = dispatch_map.begin (); + p != dispatch_map.end (); p++) + retval += " " + p->second + " (" + p->first + ", ...)\n\n"; + } + + return retval; +} + +// :-) JWE, can you parse this? Returns a 2D array with second dimension equal +// to btyp_num_types (static constant). Only the leftmost dimension can be +// variable in C/C++. Typedefs are boring. + +static builtin_type_t (*build_sup_table (void))[btyp_num_types] +{ + static builtin_type_t sup_table[btyp_num_types][btyp_num_types]; + for (int i = 0; i < btyp_num_types; i++) + for (int j = 0; j < btyp_num_types; j++) + { + builtin_type_t ityp = static_cast<builtin_type_t> (i); + builtin_type_t jtyp = static_cast<builtin_type_t> (j); + // FIXME: Is this really right? + bool use_j = + (jtyp == btyp_func_handle || ityp == btyp_bool + || (btyp_isarray (ityp) + && (! btyp_isarray (jtyp) + || (btyp_isinteger (jtyp) && ! btyp_isinteger (ityp)) + || ((ityp == btyp_double || ityp == btyp_complex || ityp == btyp_char) + && (jtyp == btyp_float || jtyp == btyp_float_complex))))); + + sup_table[i][j] = use_j ? jtyp : ityp; + } + + return sup_table; +} + +std::string +get_dispatch_type (const octave_value_list& args, + builtin_type_t& builtin_type) +{ + static builtin_type_t (*sup_table)[btyp_num_types] = build_sup_table (); + std::string dispatch_type; + + int n = args.length (); + + if (n > 0) + { + int i = 0; + builtin_type = args(0).builtin_type (); + if (builtin_type != btyp_unknown) + { + for (i = 1; i < n; i++) + { + builtin_type_t bti = args(i).builtin_type (); + if (bti != btyp_unknown) + builtin_type = sup_table[builtin_type][bti]; + else + { + builtin_type = btyp_unknown; + break; + } + } + } + + if (builtin_type == btyp_unknown) + { + // There's a non-builtin class in the argument list. + dispatch_type = args(i).class_name (); + + for (int j = i+1; j < n; j++) + { + octave_value arg = args(j); + + if (arg.builtin_type () == btyp_unknown) + { + std::string cname = arg.class_name (); + + // Only switch to type of ARG if it is marked superior + // to the current DISPATCH_TYPE. + if (! symbol_table::is_superiorto (dispatch_type, cname) + && symbol_table::is_superiorto (cname, dispatch_type)) + dispatch_type = cname; + } + } + } + else + dispatch_type = btyp_class_name[builtin_type]; + } + else + builtin_type = btyp_unknown; + + return dispatch_type; +} + +std::string +get_dispatch_type (const octave_value_list& args) +{ + builtin_type_t builtin_type; + return get_dispatch_type (args, builtin_type); +} + +// Find the definition of NAME according to the following precedence +// list: +// +// variable +// subfunction +// private function +// class method +// class constructor +// legacy dispatch +// command-line function +// autoload function +// function on the path +// built-in function +// +// Matlab documentation states that constructors have higher precedence +// than methods, but that does not seem to be the case. + +octave_value +symbol_table::fcn_info::fcn_info_rep::find (const octave_value_list& args, + bool local_funcs) +{ + octave_value retval = xfind (args, local_funcs); + + if (! (error_state || retval.is_defined ())) + { + // It is possible that the user created a file on the fly since + // the last prompt or chdir, so try updating the load path and + // searching again. + + load_path::update (); + + retval = xfind (args, local_funcs); + } + + return retval; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::xfind (const octave_value_list& args, + bool local_funcs) +{ + if (local_funcs) + { + // 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 *curr_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. + + 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. + + if (curr_fcn) + { + std::string dir_name = curr_fcn->dir_name (); + + if (! dir_name.empty ()) + { + str_val_iterator q = private_functions.find (dir_name); + + if (q == private_functions.end ()) + { + octave_value val = load_private_function (dir_name); + + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval, "", false); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_private_function (dir_name); + + if (val.is_defined ()) + return val; + } + } + } + } + } + + // Class methods. + + if (! args.empty ()) + { + std::string dispatch_type = get_dispatch_type (args); + + octave_value fcn = find_method (dispatch_type); + + if (fcn.is_defined ()) + return fcn; + } + + // Class constructors. The class name and function name are the same. + + str_val_iterator q = class_constructors.find (name); + + if (q == class_constructors.end ()) + { + octave_value val = load_class_constructor (); + + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval, name); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_class_constructor (); + + if (val.is_defined ()) + return val; + } + } + + // Legacy dispatch. + + if (! args.empty () && ! dispatch_map.empty ()) + { + std::string dispatch_type = args(0).type_name (); + + std::string fname; + + dispatch_map_iterator p = dispatch_map.find (dispatch_type); + + if (p == dispatch_map.end ()) + p = dispatch_map.find ("any"); + + if (p != dispatch_map.end ()) + { + fname = p->second; + + octave_value fcn + = symbol_table::find_function (fname, args); + + if (fcn.is_defined ()) + return fcn; + } + } + + // Command-line function. + + if (cmdline_function.is_defined ()) + return cmdline_function; + + // Autoload? + + octave_value fcn = find_autoload (); + + if (fcn.is_defined ()) + return fcn; + + // Function on the path. + + fcn = find_user_function (); + + if (fcn.is_defined ()) + return fcn; + + // Built-in function (might be undefined). + + return built_in_function; +} + +// Find the definition of NAME according to the following precedence +// list: +// +// built-in function +// function on the path +// autoload function +// command-line function +// private function +// subfunction + +// This function is used to implement the "builtin" function, which +// searches for "built-in" functions. In Matlab, "builtin" only +// returns functions that are actually built-in to the interpreter. +// But since the list of built-in functions is different in Octave and +// Matlab, we also search up the precedence list until we find +// something that matches. Note that we are only searching by name, +// so class methods, constructors, and legacy dispatch functions are +// skipped. + +octave_value +symbol_table::fcn_info::fcn_info_rep::builtin_find (void) +{ + octave_value retval = x_builtin_find (); + + if (! retval.is_defined ()) + { + // It is possible that the user created a file on the fly since + // the last prompt or chdir, so try updating the load path and + // searching again. + + load_path::update (); + + retval = x_builtin_find (); + } + + return retval; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::x_builtin_find (void) +{ + // Built-in function. + if (built_in_function.is_defined ()) + return built_in_function; + + // Function on the path. + + octave_value fcn = find_user_function (); + + if (fcn.is_defined ()) + return fcn; + + // Autoload? + + fcn = find_autoload (); + + if (fcn.is_defined ()) + return fcn; + + // Command-line function. + + if (cmdline_function.is_defined ()) + return cmdline_function; + + // Private function. + + octave_user_function *curr_fcn = symbol_table::get_curr_fcn (); + + if (curr_fcn) + { + std::string dir_name = curr_fcn->dir_name (); + + if (! dir_name.empty ()) + { + str_val_iterator q = private_functions.find (dir_name); + + if (q == private_functions.end ()) + { + octave_value val = load_private_function (dir_name); + + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_private_function (dir_name); + + if (val.is_defined ()) + return val; + } + } + } + } + + // Subfunction. I think it only makes sense to check for + // subfunctions if we are currently executing a function defined + // from a .m file. + + 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. + + 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 (); +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::find_method (const std::string& dispatch_type) +{ + octave_value retval; + + str_val_iterator q = class_methods.find (dispatch_type); + + if (q == class_methods.end ()) + { + octave_value val = load_class_method (dispatch_type); + + if (val.is_defined ()) + return val; + } + else + { + octave_value& fval = q->second; + + if (fval.is_defined ()) + out_of_date_check (fval, dispatch_type); + + if (fval.is_defined ()) + return fval; + else + { + octave_value val = load_class_method (dispatch_type); + + if (val.is_defined ()) + return val; + } + } + + return retval; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::find_autoload (void) +{ + octave_value retval; + + // Autoloaded function. + + if (autoload_function.is_defined ()) + out_of_date_check (autoload_function); + + if (! autoload_function.is_defined ()) + { + std::string file_name = lookup_autoload (name); + + if (! file_name.empty ()) + { + size_t pos = file_name.find_last_of (file_ops::dir_sep_chars ()); + + std::string dir_name = file_name.substr (0, pos); + + octave_function *fcn = load_fcn_from_file (file_name, dir_name, + "", name, true); + + if (fcn) + autoload_function = octave_value (fcn); + } + } + + return autoload_function; +} + +octave_value +symbol_table::fcn_info::fcn_info_rep::find_user_function (void) +{ + // Function on the path. + + if (function_on_path.is_defined ()) + out_of_date_check (function_on_path); + + if (! (error_state || function_on_path.is_defined ())) + { + std::string dir_name; + + std::string file_name = load_path::find_fcn (name, dir_name); + + if (! file_name.empty ()) + { + octave_function *fcn = load_fcn_from_file (file_name, dir_name); + + if (fcn) + function_on_path = octave_value (fcn); + } + } + + return function_on_path; +} + +// Insert INF_CLASS in the set of class names that are considered +// inferior to SUP_CLASS. Return FALSE if INF_CLASS is currently +// marked as superior to SUP_CLASS. + +bool +symbol_table::set_class_relationship (const std::string& sup_class, + const std::string& inf_class) +{ + class_precedence_table_const_iterator p + = class_precedence_table.find (inf_class); + + if (p != class_precedence_table.end ()) + { + const std::set<std::string>& inferior_classes = p->second; + + std::set<std::string>::const_iterator q + = inferior_classes.find (sup_class); + + if (q != inferior_classes.end ()) + return false; + } + + class_precedence_table[sup_class].insert (inf_class); + + return true; +} + +// Has class A been marked as superior to class B? Also returns +// TRUE if B has been marked as inferior to A, since we only keep +// one table, and convert inferiort information to a superiorto +// relationship. Two calls are required to determine whether there +// is no relationship between two classes: +// +// if (symbol_table::is_superiorto (a, b)) +// // A is superior to B, or B has been marked inferior to A. +// else if (symbol_table::is_superiorto (b, a)) +// // B is superior to A, or A has been marked inferior to B. +// else +// // No relation. + +bool +symbol_table::is_superiorto (const std::string& a, const std::string& b) +{ + bool retval = false; + + class_precedence_table_const_iterator p = class_precedence_table.find (a); + + if (p != class_precedence_table.end ()) + { + const std::set<std::string>& inferior_classes = p->second; + std::set<std::string>::const_iterator q = inferior_classes.find (b); + + if (q != inferior_classes.end ()) + retval = true; + } + + return retval; +} + +static std::string +fcn_file_name (const octave_value& fcn) +{ + const octave_function *f = fcn.function_value (); + + return f ? f->fcn_file_name () : std::string (); +} + +void +symbol_table::fcn_info::fcn_info_rep::dump + (std::ostream& os, const std::string& prefix) const +{ + os << prefix << name + << " [" + << (cmdline_function.is_defined () ? "c" : "") + << (built_in_function.is_defined () ? "b" : "") + << "]\n"; + + std::string tprefix = prefix + " "; + + if (autoload_function.is_defined ()) + os << tprefix << "autoload: " + << fcn_file_name (autoload_function) << "\n"; + + if (function_on_path.is_defined ()) + os << tprefix << "function from path: " + << fcn_file_name (function_on_path) << "\n"; + + if (! subfunctions.empty ()) + { + for (scope_val_const_iterator p = subfunctions.begin (); + p != subfunctions.end (); p++) + os << tprefix << "subfunction: " << fcn_file_name (p->second) + << " [" << p->first << "]\n"; + } + + if (! private_functions.empty ()) + { + for (str_val_const_iterator p = private_functions.begin (); + p != private_functions.end (); p++) + os << tprefix << "private: " << fcn_file_name (p->second) + << " [" << p->first << "]\n"; + } + + if (! class_constructors.empty ()) + { + for (str_val_const_iterator p = class_constructors.begin (); + p != class_constructors.end (); p++) + os << tprefix << "constructor: " << fcn_file_name (p->second) + << " [" << p->first << "]\n"; + } + + if (! class_methods.empty ()) + { + for (str_val_const_iterator p = class_methods.begin (); + p != class_methods.end (); p++) + os << tprefix << "method: " << fcn_file_name (p->second) + << " [" << p->first << "]\n"; + } + + if (! dispatch_map.empty ()) + { + for (dispatch_map_const_iterator p = dispatch_map.begin (); + p != dispatch_map.end (); p++) + os << tprefix << "dispatch: " << fcn_file_name (p->second) + << " [" << p->first << "]\n"; + } +} + +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_loc = get_instance (fv->scope ()); + + symbol_table *parent_table = get_instance (parent_scope); + + parent_table->add_nest_child (*fcn_table_loc); +} + +octave_value +symbol_table::find (const std::string& name, + const octave_value_list& args, + bool skip_variables, + bool local_funcs) +{ + symbol_table *inst = get_instance (xcurrent_scope); + + return inst + ? inst->do_find (name, args, skip_variables, local_funcs) + : octave_value (); +} + +octave_value +symbol_table::builtin_find (const std::string& name) +{ + symbol_table *inst = get_instance (xcurrent_scope); + + return inst ? inst->do_builtin_find (name) : octave_value (); +} + +octave_value +symbol_table::find_function (const std::string& name, + const octave_value_list& args, + bool local_funcs) +{ + octave_value retval; + + if (! name.empty () && name[0] == '@') + { + // Look for a class specific function. + std::string dispatch_type = + name.substr (1, name.find_first_of (file_ops::dir_sep_str ()) - 1); + + std::string method = + name.substr (name.find_last_of (file_ops::dir_sep_str ()) + 1, + std::string::npos); + + retval = find_method (method, dispatch_type); + } + else + { + size_t pos = name.find_first_of (Vfilemarker); + + if (pos == std::string::npos) + retval = find (name, args, true, local_funcs); + else + { + std::string fcn_scope = name.substr (0, pos); + scope_id stored_scope = xcurrent_scope; + xcurrent_scope = xtop_scope; + octave_value parent = find_function (name.substr (0, pos), + octave_value_list (), false); + + if (parent.is_defined ()) + { + octave_function *parent_fcn = parent.function_value (); + + if (parent_fcn) + { + xcurrent_scope = parent_fcn->scope (); + + if (xcurrent_scope > 1) + retval = find_function (name.substr (pos + 1), args); + } + } + + xcurrent_scope = stored_scope; + } + } + + return retval; +} + +void +symbol_table::dump (std::ostream& os, scope_id scope) +{ + if (scope == xglobal_scope) + dump_global (os); + else + { + symbol_table *inst = get_instance (scope, false); + + if (inst) + { + os << "*** dumping symbol table scope " << scope + << " (" << inst->table_name << ")\n\n"; + + std::map<std::string, octave_value> sfuns + = symbol_table::subfunctions_defined_in_scope (scope); + + if (! sfuns.empty ()) + { + os << " subfunctions defined in this scope:\n"; + + for (std::map<std::string, octave_value>::const_iterator p = sfuns.begin (); + p != sfuns.end (); p++) + os << " " << p->first << "\n"; + + os << "\n"; + } + + inst->do_dump (os); + } + } +} + +void +symbol_table::dump_global (std::ostream& os) +{ + if (! global_table.empty ()) + { + os << "*** dumping global symbol table\n\n"; + + for (global_table_const_iterator p = global_table.begin (); + p != global_table.end (); p++) + { + std::string nm = p->first; + octave_value val = p->second; + + os << " " << nm << " "; + val.dump (os); + os << "\n"; + } + } +} + +void +symbol_table::dump_functions (std::ostream& os) +{ + if (! fcn_table.empty ()) + { + os << "*** dumping globally visible functions from symbol table\n" + << " (c=commandline, b=built-in)\n\n"; + + for (fcn_table_const_iterator p = fcn_table.begin (); + p != fcn_table.end (); p++) + p->second.dump (os, " "); + + os << "\n"; + } +} + +void +symbol_table::stash_dir_name_for_subfunctions (scope_id scope, + const std::string& dir_name) +{ + // FIXME -- is this the best way to do this? Maybe it would be + // better if we had a map from scope to list of subfunctions + // stored with the function. Do we? + + for (fcn_table_const_iterator p = fcn_table.begin (); + p != fcn_table.end (); p++) + { + std::pair<std::string, octave_value> tmp + = p->second.subfunction_defined_in_scope (scope); + + std::string nm = tmp.first; + + if (! nm.empty ()) + { + octave_value& fcn = tmp.second; + + octave_user_function *f = fcn.user_function_value (); + + if (f) + f->stash_dir_name (dir_name); + } + } +} + +octave_value +symbol_table::do_find (const std::string& name, + const octave_value_list& args, + bool skip_variables, + bool local_funcs) +{ + octave_value retval; + + // Variable. + + if (! skip_variables) + { + table_iterator p = table.find (name); + + if (p != table.end ()) + { + symbol_record sr = p->second; + + // FIXME -- should we be using something other than varref here? + + if (sr.is_global ()) + return symbol_table::global_varref (name); + else + { + octave_value& val = sr.varref (); + + if (val.is_defined ()) + return val; + } + } + } + + fcn_table_iterator p = fcn_table.find (name); + + if (p != fcn_table.end ()) + return p->second.find (args, local_funcs); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.find (args, local_funcs); + + if (fcn.is_defined ()) + fcn_table[name] = finfo; + + return fcn; + } + + return retval; +} + +octave_value +symbol_table::do_builtin_find (const std::string& name) +{ + octave_value retval; + + fcn_table_iterator p = fcn_table.find (name); + + if (p != fcn_table.end ()) + return p->second.builtin_find (); + else + { + fcn_info finfo (name); + + octave_value fcn = finfo.builtin_find (); + + if (fcn.is_defined ()) + fcn_table[name] = finfo; + + return fcn; + } + + return retval; +} + +void +symbol_table::do_dump (std::ostream& os) +{ + if (! persistent_table.empty ()) + { + os << " persistent variables in this scope:\n\n"; + + for (persistent_table_const_iterator p = persistent_table.begin (); + p != persistent_table.end (); p++) + { + std::string nm = p->first; + octave_value val = p->second; + + os << " " << nm << " "; + val.dump (os); + os << "\n"; + } + + os << "\n"; + } + + if (! table.empty ()) + { + os << " other symbols in this scope (l=local; a=auto; f=formal\n" + << " h=hidden; i=inherited; g=global; p=persistent)\n\n"; + + for (table_const_iterator p = table.begin (); p != table.end (); p++) + p->second.dump (os, " "); + + os << "\n"; + } +} + +void symbol_table::cleanup (void) +{ + // Clear variables in top scope. + all_instances[xtop_scope]->clear_variables (); + + // Clear function table. This is a hard clear, ignoring mlocked functions. + fcn_table.clear (); + + // Clear variables in global scope. + // FIXME: are there any? + all_instances[xglobal_scope]->clear_variables (); + + // Clear global variables. + global_table.clear (); + + // Delete all possibly remaining scopes. + for (all_instances_iterator iter = all_instances.begin (); + iter != all_instances.end (); iter++) + { + scope_id scope = iter->first; + if (scope != xglobal_scope && scope != xtop_scope) + scope_id_cache::free (scope); + + // First zero the table entry to avoid possible duplicate delete. + symbol_table *inst = iter->second; + iter->second = 0; + + // Now delete the scope. Note that there may be side effects, such as + // deleting other scopes. + delete inst; + } +} + +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\ +@deftypefnx {Built-in Function} {@var{old_val} =} ignore_function_time_stamp (@var{new_val})\n\ +Query or set the internal variable that controls whether Octave checks\n\ +the time stamp on files each time it looks up functions defined in\n\ +function files. If the internal variable is set to @code{\"system\"},\n\ +Octave will not automatically recompile function files in subdirectories of\n\ +@file{@var{octave-home}/lib/@var{version}} if they have changed since\n\ +they were last compiled, but will recompile other function files in the\n\ +search path if they change. If set to @code{\"all\"}, Octave will not\n\ +recompile any function files unless their definitions are removed with\n\ +@code{clear}. If set to \"none\", Octave will always check time stamps\n\ +on files to determine whether functions defined in function files\n\ +need to recompiled.\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + if (nargout > 0 || nargin == 0) + { + switch (Vignore_function_time_stamp) + { + case 1: + retval = "system"; + break; + + case 2: + retval = "all"; + break; + + default: + retval = "none"; + break; + } + } + + if (nargin == 1) + { + std::string sval = args(0).string_value (); + + if (! error_state) + { + if (sval == "all") + Vignore_function_time_stamp = 2; + else if (sval == "system") + Vignore_function_time_stamp = 1; + else if (sval == "none") + Vignore_function_time_stamp = 0; + else + error ("ignore_function_time_stamp: expecting argument to be \"all\", \"system\", or \"none\""); + } + else + error ("ignore_function_time_stamp: expecting argument to be character string"); + } + else if (nargin > 1) + print_usage (); + + return retval; +} + +/* +%!shared old_state +%! old_state = ignore_function_time_stamp (); +%!test +%! state = ignore_function_time_stamp ("all"); +%! assert (state, old_state); +%! assert (ignore_function_time_stamp (), "all"); +%! state = ignore_function_time_stamp ("system"); +%! assert (state, "all"); +%! assert (ignore_function_time_stamp (), "system"); +%! ignore_function_time_stamp (old_state); + +## Test input validation +%!error (ignore_function_time_stamp ("all", "all")) +%!error (ignore_function_time_stamp ("UNKNOWN_VALUE")) +%!error (ignore_function_time_stamp (42)) +*/ + +DEFUN (__current_scope__, , , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {[@var{scope}, @var{context}]} __dump_symtab_info__ ()\n\ +Undocumented internal function.\n\ +@end deftypefn") +{ + octave_value_list retval; + + retval(1) = symbol_table::current_context (); + retval(0) = symbol_table::current_scope (); + + return retval; +} + +DEFUN (__dump_symtab_info__, args, , + "-*- texinfo -*-\n\ +@deftypefn {Built-in Function} {} __dump_symtab_info__ ()\n\ +@deftypefnx {Built-in Function} {} __dump_symtab_info__ (@var{scope})\n\ +@deftypefnx {Built-in Function} {} __dump_symtab_info__ (\"scopes\")\n\ +@deftypefnx {Built-in Function} {} __dump_symtab_info__ (\"functions\")\n\ +Undocumented internal function.\n\ +@end deftypefn") +{ + octave_value retval; + + int nargin = args.length (); + + if (nargin == 0) + { + symbol_table::dump_functions (octave_stdout); + + symbol_table::dump_global (octave_stdout); + + std::list<symbol_table::scope_id> lst = symbol_table::scopes (); + + for (std::list<symbol_table::scope_id>::const_iterator p = lst.begin (); + p != lst.end (); p++) + symbol_table::dump (octave_stdout, *p); + } + else if (nargin == 1) + { + octave_value arg = args(0); + + if (arg.is_string ()) + { + std::string s_arg = arg.string_value (); + + if (s_arg == "scopes") + { + std::list<symbol_table::scope_id> lst = symbol_table::scopes (); + + RowVector v (lst.size ()); + + octave_idx_type k = 0; + + for (std::list<symbol_table::scope_id>::const_iterator p = lst.begin (); + p != lst.end (); p++) + v.xelem (k++) = *p; + + retval = v; + } + else if (s_arg == "functions") + { + symbol_table::dump_functions (octave_stdout); + } + else + error ("__dump_symtab_info__: expecting \"functions\" or \"scopes\""); + } + else + { + int s = arg.int_value (); + + if (! error_state) + symbol_table::dump (octave_stdout, s); + else + error ("__dump_symtab_info__: expecting string or scope id"); + } + } + else + print_usage (); + + return retval; +} + +#if 0 + +// FIXME -- should we have functions like this in Octave? + +DEFUN (set_variable, args, , "set_variable (NAME, VALUE)") +{ + octave_value retval; + + if (args.length () == 2) + { + std::string name = args(0).string_value (); + + if (! error_state) + symbol_table::varref (name) = args(1); + else + error ("set_variable: expecting variable name as first argument"); + } + else + print_usage (); + + return retval; +} + +DEFUN (variable_value, args, , "VALUE = variable_value (NAME)") +{ + octave_value retval; + + if (args.length () == 1) + { + std::string name = args(0).string_value (); + + if (! error_state) + { + retval = symbol_table::varval (name); + + if (retval.is_undefined ()) + error ("variable_value: `%s' is not a variable in the current scope", + name.c_str ()); + } + else + error ("variable_value: expecting variable name as first argument"); + } + else + print_usage (); + + return retval; +} +#endif + + +/* +bug #34497: 'clear -f' does not work for command line functions + +This test relies on bar being a core function that is implemented in an m-file. +If the first assert fails, this is no longer the case and the tests need to be +updated to use some other function. + +%!assert (! strcmp (which ("bar"), "")); + +%!function x = bar () +%! x = 5; +%!endfunction +%!test +%! assert (bar == 5); +%! assert (strcmp (which ("bar"), "")); +%! clear -f bar; +%! assert (! strcmp (which ("bar"), "")); + +%!function x = bar () +%! x = 5; +%!endfunction +%!test +%! assert (bar == 5); +%! assert (strcmp (which ("bar"), "")); +%! clear bar; +%! assert (! strcmp (which ("bar"), "")); + */