Mercurial > hg > octave-nkf
diff src/ov-usr-fcn.cc @ 2974:ebbc34ff7f66
[project @ 1997-05-15 19:36:16 by jwe]
author | jwe |
---|---|
date | Thu, 15 May 1997 19:42:59 +0000 |
parents | |
children | 20f5cec4f11c |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/ov-usr-fcn.cc @@ -0,0 +1,563 @@ +/* + +Copyright (C) 1996, 1997 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 2, 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, write to the Free +Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + +*/ + +#if defined (__GNUG__) +#pragma implementation +#endif + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include "str-vec.h" + +#include <defaults.h> +#include "defun.h" +#include "error.h" +#include "help.h" +#include "input.h" +#include "oct-obj.h" +#include "ov-usr-fcn.h" +#include "ov.h" +#include "pager.h" +#include "pt-misc.h" +#include "pt-pr-code.h" +#include "pt-walk.h" +#include "symtab.h" +#include "toplev.h" +#include "unwind-prot.h" +#include "utils.h" +#include "variables.h" + +// If TRUE, variables returned from functions have default values even +// if they are not explicitly initialized. +static bool Vdefine_all_return_values; + +// If TRUE, the last computed value is returned from functions that +// don't actually define any return variables. +static bool Vreturn_last_computed_value; + +// Nonzero means we're breaking out of a loop or function body. +extern int breaking; + +// Nonzero means we're returning from a function. +extern int returning; + +// User defined functions. + +octave_allocator +octave_user_function::allocator (sizeof (octave_user_function)); + +int +octave_user_function::t_id (-1); + +const string +octave_user_function::t_name ("user-defined function"); + +// Ugh. This really needs to be simplified (code/data? +// extrinsic/intrinsic state?). + +octave_user_function::octave_user_function + (tree_parameter_list *pl, tree_parameter_list *rl, + tree_statement_list *cl, symbol_table *st) + : octave_function (string (), string ()), + param_list (pl), ret_list (rl), cmd_list (cl), + sym_tab (st), file_name (), fcn_name (), t_parsed (0), + system_fcn_file (false), call_depth (0), num_named_args (0), + args_passed (), num_args_passed (0), curr_va_arg_number (0), + vr_list (0), symtab_entry (0), argn_sr (0), nargin_sr (0), + nargout_sr (0) +{ + install_automatic_vars (); + + if (param_list) + { + num_named_args = param_list->length (); + curr_va_arg_number = num_named_args; + } +} + +octave_user_function::~octave_user_function (void) +{ + delete param_list; + delete ret_list; + delete sym_tab; + delete cmd_list; + delete vr_list; +} + +octave_user_function * +octave_user_function::define_ret_list (tree_parameter_list *t) +{ + ret_list = t; + + if (ret_list && ret_list->takes_varargs ()) + vr_list = new tree_va_return_list; + + return this; +} + +void +octave_user_function::stash_fcn_file_name (void) +{ + if (fcn_name.empty ()) + file_name = ""; + else + file_name = fcn_file_in_path (fcn_name); +} + +void +octave_user_function::mark_as_system_fcn_file (void) +{ + if (! file_name.empty ()) + { + // We really should stash the whole path to the file we found, + // when we looked it up, to avoid possible race conditions... + // XXX FIXME XXX + // + // We probably also don't need to get the library directory + // every time, but since this function is only called when the + // function file is parsed, it probably doesn't matter that + // much. + + string ff_name = fcn_file_in_path (file_name); + + if (Vfcn_file_dir.compare (ff_name, 0, Vfcn_file_dir.length ()) == 0) + system_fcn_file = 1; + } + else + system_fcn_file = 0; +} + +bool +octave_user_function::takes_varargs (void) const +{ + return (param_list && param_list->takes_varargs ()); +} + +octave_value +octave_user_function::octave_va_arg (void) +{ + octave_value retval; + + if (curr_va_arg_number < num_args_passed) + retval = args_passed (curr_va_arg_number++); + else + ::error ("va_arg: error getting arg number %d -- only %d provided", + curr_va_arg_number + 1, num_args_passed); + + return retval; +} + +octave_value_list +octave_user_function::octave_all_va_args (void) +{ + octave_value_list retval; + + retval.resize (num_args_passed - num_named_args); + + int k = 0; + for (int i = num_named_args; i < num_args_passed; i++) + retval(k++) = args_passed(i); + + return retval; +} + +bool +octave_user_function::takes_var_return (void) const +{ + return (ret_list && ret_list->takes_varargs ()); +} + +void +octave_user_function::octave_vr_val (const octave_value& val) +{ + assert (vr_list); + + vr_list->append (val); +} + +void +octave_user_function::stash_function_name (const string& s) +{ + fcn_name = s; +} + +// For unwind protect. + +static void +pop_symbol_table_context (void *table) +{ + symbol_table *tmp = static_cast<symbol_table *> (table); + tmp->pop_context (); +} + +static void +delete_vr_list (void *list) +{ + tree_va_return_list *tmp = static_cast<tree_va_return_list *> (list); + tmp->clear (); + delete tmp; +} + +static void +clear_symbol_table (void *table) +{ + symbol_table *tmp = static_cast<symbol_table *> (table); + tmp->clear (); +} + +static void +unprotect_function (void *sr_arg) +{ + symbol_record *sr = static_cast<symbol_record *> (sr_arg); + sr->unprotect (); +} + +octave_value_list +octave_user_function::do_index_op (int nargout, const octave_value_list& args) +{ + octave_value_list retval; + + if (error_state) + return retval; + + if (! cmd_list) + return retval; + + int nargin = args.length (); + + begin_unwind_frame ("func_eval"); + + unwind_protect_int (call_depth); + call_depth++; + + if (symtab_entry && ! symtab_entry->is_read_only ()) + { + symtab_entry->protect (); + add_unwind_protect (unprotect_function, symtab_entry); + } + + if (call_depth > 1) + { + sym_tab->push_context (); + add_unwind_protect (pop_symbol_table_context, sym_tab); + + if (vr_list) + { + // Push new vr_list. + + unwind_protect_ptr (vr_list); + vr_list = new tree_va_return_list; + + // Clear and delete the new one before restoring the old + // one. + + add_unwind_protect (delete_vr_list, vr_list); + } + } + + if (vr_list) + vr_list->clear (); + + // Force symbols to be undefined again when this function exits. + + add_unwind_protect (clear_symbol_table, sym_tab); + + // Save old and set current symbol table context, for + // eval_undefined_error(). + + unwind_protect_ptr (curr_sym_tab); + curr_sym_tab = sym_tab; + + unwind_protect_ptr (curr_function); + curr_function = this; + + // XXX FIXME XXX -- ??? + // unwind_protect_ptr (args_passed); + + args_passed = args; + + string_vector arg_names = args.name_tags (); + + unwind_protect_int (num_args_passed); + num_args_passed = nargin; + + unwind_protect_int (num_named_args); + unwind_protect_int (curr_va_arg_number); + + if (param_list && ! param_list->varargs_only ()) + { + param_list->define_from_arg_vector (args); + if (error_state) + goto abort; + } + + if (ret_list && Vdefine_all_return_values) + { + octave_value tmp = builtin_any_variable ("default_return_value"); + + if (tmp.is_defined ()) + ret_list->initialize_undefined_elements (tmp); + } + + // The following code is in a separate scope to avoid warnings from + // G++ about `goto abort' crossing the initialization of some + // variables. + + { + bind_automatic_vars (arg_names, nargin, nargout); + + bool echo_commands = (Vecho_executing_commands & ECHO_FUNCTIONS); + + if (echo_commands) + print_code_function_header (); + + // Evaluate the commands that make up the function. + + octave_value_list tmp = cmd_list->eval (); + + octave_value last_computed_value; + + if (! tmp.empty ()) + last_computed_value = tmp(0); + + if (echo_commands) + print_code_function_trailer (); + + if (returning) + returning = 0; + + if (breaking) + breaking--; + + if (error_state) + { + traceback_error (); + goto abort; + } + + // Copy return values out. + + if (ret_list) + retval = ret_list->convert_to_const_vector (vr_list); + else if (Vreturn_last_computed_value) + retval(0) = last_computed_value; + } + + abort: + run_unwind_frame ("func_eval"); + + return retval; +} + +void +octave_user_function::traceback_error (void) +{ + if (error_state >= 0) + error_state = -1; + + if (fcn_name.empty ()) + { + if (file_name.empty ()) + ::error ("called from `?unknown?'"); + else + ::error ("called from file `%s'", file_name.c_str ()); + } + else + { + if (file_name.empty ()) + ::error ("called from `%s'", fcn_name.c_str ()); + else + ::error ("called from `%s' in file `%s'", + fcn_name.c_str (), file_name.c_str ()); + } +} + +void +octave_user_function::accept (tree_walker& tw) +{ + tw.visit_octave_user_function (*this); +} + +void +octave_user_function::print_code_function_header (void) +{ + tree_print_code tpc (octave_stdout, Vps4); + + tpc.visit_octave_user_function_header (*this); +} + +void +octave_user_function::print_code_function_trailer (void) +{ + tree_print_code tpc (octave_stdout, Vps4); + + tpc.visit_octave_user_function_trailer (*this); +} + +void +octave_user_function::install_automatic_vars (void) +{ + argn_sr = sym_tab->lookup ("argn", true); + nargin_sr = sym_tab->lookup ("nargin", true); + nargout_sr = sym_tab->lookup ("nargout", true); +} + +void +octave_user_function::bind_automatic_vars + (const string_vector& arg_names, int nargin, int nargout) +{ + if (! arg_names.empty ()) + argn_sr->define (arg_names); + + nargin_sr->define (static_cast<double> (nargin)); + nargout_sr->define (static_cast<double> (nargout)); +} + +DEFUN (va_arg, args, , + "va_arg (): return next argument in a function that takes a\n\ +variable number of parameters") +{ + octave_value_list retval; + + int nargin = args.length (); + + if (nargin == 0) + { + if (curr_function) + { + if (curr_function->takes_varargs ()) + retval = curr_function->octave_va_arg (); + else + { + ::error ("va_arg only valid within function taking variable"); + ::error ("number of arguments"); + } + } + else + ::error ("va_arg only valid within function body"); + } + else + print_usage ("va_arg"); + + return retval; +} + +DEFUN (va_start, args, , + "va_start (): reset the pointer to the list of optional arguments\n\ +to the beginning") +{ + octave_value_list retval; + + int nargin = args.length (); + + if (nargin == 0) + { + if (curr_function) + { + if (curr_function->takes_varargs ()) + curr_function->octave_va_start (); + else + { + ::error ("va_start only valid within function taking variable"); + ::error ("number of arguments"); + } + } + else + ::error ("va_start only valid within function body"); + } + else + print_usage ("va_start"); + + return retval; +} + +DEFUN (vr_val, args, , + "vr_val (X): append X to the list of optional return values for a\n\ +function that allows a variable number of return values") +{ + octave_value_list retval; + + int nargin = args.length (); + + if (nargin == 1) + { + if (curr_function) + { + if (curr_function->takes_var_return ()) + curr_function->octave_vr_val (args(0)); + else + { + ::error ("vr_val only valid within function declared to"); + ::error ("produce a variable number of values"); + } + } + else + ::error ("vr_val only valid within function body"); + } + else + print_usage ("vr_val"); + + return retval; +} + +static int +define_all_return_values (void) +{ + Vdefine_all_return_values = check_preference ("define_all_return_values"); + + return 0; +} + +static int +return_last_computed_value (void) +{ + Vreturn_last_computed_value + = check_preference ("return_last_computed_value"); + + return 0; +} + +void +symbols_of_ov_usr_fcn (void) +{ + DEFVAR (default_return_value, Matrix (), 0, 0, + "the default for value for unitialized variables returned from\n\ +functions. Only used if the variable initialize_return_values is\n\ +set to \"true\"."); + + DEFVAR (define_all_return_values, 0.0, 0, define_all_return_values, + "control whether values returned from functions should have a\n\ +value even if one has not been explicitly assigned. See also\n\ +default_return_value"); + + DEFVAR (return_last_computed_value, 0.0, 0, return_last_computed_value, + "if a function does not return any values explicitly, return the\n\ + last computed value"); +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; End: *** +*/