# HG changeset patch # User jwe # Date 773506523 0 # Node ID 2c4d694b87e9065bb43373246d06dfb2055b9463 # Parent 1391e7ed65f650ab3dc6fd4ef5d86b989effa2d4 [project @ 1994-07-06 14:55:23 by jwe] Initial revision diff --git a/src/pt-cmd.cc b/src/pt-cmd.cc new file mode 100644 --- /dev/null +++ b/src/pt-cmd.cc @@ -0,0 +1,827 @@ +// Tree class. -*- C++ -*- +/* + +Copyright (C) 1992, 1993, 1994 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined (__GNUG__) +#pragma implementation +#endif + +#include + +// For NULL. +#include + +#include "user-prefs.h" +#include "variables.h" +#include "symtab.h" +#include "error.h" +#include "gripes.h" +#include "tree.h" +#include "tree-cmd.h" +#include "tree-const.h" + +// Nonzero means we're breaking out of a loop. +static int breaking = 0; + +// Nonzero means we're jumping to the end of a loop. +static int continuing = 0; + +// Nonzero means we're returning from a function. Global because it +// is also needed in tree-expr.cc. +int returning = 0; + +// Decide if it's time to quit a for or while loop. +static int +quit_loop_now (void) +{ +// Maybe handle `continue N' someday... + + if (continuing) + continuing--; + + int quit = (returning || breaking || continuing); + + if (breaking) + breaking--; + + return quit; +} + +// But first, some extra functions used by the tree classes. + +// We seem to have no use for this now. Maybe it will be needed at +// some future date, so here it is. +#if 0 +/* + * Convert a linked list of trees to a vector of pointers to trees. + */ +static tree ** +list_to_vector (tree *list, int& len) +{ + len = list->length () + 1; + + tree **args = new tree * [len]; + +// args[0] may eventually hold something useful, like the function +// name. + tree *tmp_list = list; + for (int k = 1; k < len; k++) + { + args[k] = tmp_list; + tmp_list = tmp_list->next_elem (); + } + return args; +} +#endif + +/* + * A command or two to be executed. + */ +tree_command_list::tree_command_list (void) +{ + command = NULL_TREE; + print_flag = 1; + next = (tree_command_list *) NULL; +} + +tree_command_list::tree_command_list (tree *t) +{ + command = t; + print_flag = 1; + next = (tree_command_list *) NULL; +} + +tree_command_list::~tree_command_list (void) +{ + delete command; + delete next; +} + +void +tree_command_list::set_print_flag (int flag) +{ + print_flag = flag; +} + +tree_command_list * +tree_command_list::chain (tree *t) +{ + tree_command_list *tmp = new tree_command_list (t); + tmp->next = this; + return tmp; +} + +tree_command_list * +tree_command_list::reverse (void) +{ + tree_command_list *list = this; + tree_command_list *next; + tree_command_list *prev = (tree_command_list *) NULL; + + while (list != (tree_command_list *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +tree_constant +tree_command_list::eval (int print) +{ + int pf; + tree_constant retval; + + if (error_state) + return retval; + + tree_command_list *list; + for (list = this; list != (tree_command_list *) NULL; list = list->next) + { + if (print == 0) + pf = 0; + else + pf = list->print_flag; + + tree *cmd = list->command; + if (cmd == NULL_TREE) + retval = tree_constant (); + else + { + retval = cmd->eval (pf); + + if (error_state) + return tree_constant (); + + if (breaking || continuing) + break; + + if (returning) + break; + } + } + return retval; +} + +/* + * Global. + */ +tree_global_command::tree_global_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + sr = (symbol_record *) NULL; + rhs = (tree_expression *) NULL; + next = (tree_global_command *) NULL; +} + +tree_global_command::tree_global_command (symbol_record *s, + int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + sr = s; + rhs = (tree_expression *) NULL; + next = (tree_global_command *) NULL; +} + +tree_global_command::tree_global_command (symbol_record *s, + tree_expression *e, + int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + sr = s; + rhs = e; + next = (tree_global_command *) NULL; +} + +tree_global_command::~tree_global_command (void) +{ + delete next; +} + +tree_global_command * +tree_global_command::chain (symbol_record *s, int l = -1, int c = -1) +{ + tree_global_command *tmp = new tree_global_command (s, l, c); + tmp->next = this; + return tmp; +} + +tree_global_command * +tree_global_command::chain (symbol_record *s, tree_expression *e, + int l = -1, int c = -1) +{ + tree_global_command *tmp = new tree_global_command (s, e, l, c); + tmp->next = this; + return tmp; +} + +tree_global_command * +tree_global_command::reverse (void) +{ + tree_global_command *list = this; + tree_global_command *next; + tree_global_command *prev = (tree_global_command *) NULL; + + while (list != (tree_global_command *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +tree_constant +tree_global_command::eval (int print) +{ + tree_constant retval; + + link_to_global_variable (sr); + + if (rhs != NULL_TREE) + { + tree_identifier *id = new tree_identifier (sr); + tree_constant tmp_rhs = rhs->eval (0); + if (error_state) + { + delete id; + eval_error (); + return retval; + } + else + { + tree_constant *tmp_val = new tree_constant (tmp_rhs); + + tree_simple_assignment_expression tmp_ass (id, tmp_val); + + tmp_ass.eval (0); + + delete id; // XXX FIXME XXX + + if (error_state) + { + eval_error (); + return retval; + } + } + } + + if (next != (tree_global_command *) NULL) + next->eval (print); + + return retval; +} + +void +tree_global_command::eval_error (void) +{ + if (error_state > 0) + ::error ("evaluating global command near line %d, column %d", + line (), column ()); +} + +/* + * While. + */ +tree_while_command::tree_while_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + expr = (tree_expression *) NULL; + list = NULL_TREE; +} + +tree_while_command::tree_while_command (tree_expression *e, + int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + expr = e; + list = NULL_TREE; +} + +tree_while_command::tree_while_command (tree_expression *e, tree *lst, + int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + expr = e; + list = lst; +} + +tree_while_command::~tree_while_command (void) +{ + delete expr; + delete list; +} + +tree_constant +tree_while_command::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + for (;;) + { + int expr_value = 0; + if (expr == (tree_expression *) NULL) + return tree_constant (); + tree_constant t1 = expr->eval (0); + + if (error_state) + { + eval_error (); + return tree_constant (); + } + + if (t1.rows () == 0 || t1.columns () == 0) + { + int flag = user_pref.propagate_empty_matrices; + if (flag < 0) + warning ("while: empty matrix used in conditional"); + else if (flag == 0) + { + ::error ("while: empty matrix used in conditional"); + return tree_constant (); + } + t1 = tree_constant (0.0); + } + else if (! t1.is_scalar_type ()) + { + tree_constant t2 = t1.all (); + t1 = t2.all (); + } + + tree_constant_rep::constant_type t = t1.const_type (); + if (t == tree_constant_rep::scalar_constant) + expr_value = (int) t1.double_value (); + else if (t == tree_constant_rep::complex_scalar_constant) + expr_value = t1.complex_value () != 0.0; + else + panic_impossible (); + + if (expr_value) + { + if (list != NULL_TREE) + { + retval = list->eval (1); + if (error_state) + { + eval_error (); + return tree_constant (); + } + } + + if (quit_loop_now ()) + break; + } + else + break; + } + return retval; +} + +void +tree_while_command::eval_error (void) +{ + if (error_state > 0) + ::error ("evaluating while command near line %d, column %d", + line (), column ()); +} + +/* + * For. + */ +tree_for_command::tree_for_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + id = (tree_index_expression *) NULL; + expr = (tree_expression *) NULL; + list = NULL_TREE; +} + +tree_for_command::tree_for_command (tree_index_expression *ident, + tree_expression *e, tree *lst, + int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + id = ident; + expr = e; + list = lst; +} + +tree_for_command::~tree_for_command (void) +{ + delete id; + delete expr; + delete list; +} + +tree_constant +tree_for_command::eval (int print) +{ + tree_constant retval; + + if (error_state || expr == (tree_expression *) NULL) + return retval; + + tree_constant tmp_expr = expr->eval (0); + + if (error_state || tmp_expr.is_undefined ()) + { + eval_error (); + return retval; + } + + tree_constant_rep::constant_type expr_type = tmp_expr.const_type (); + switch (expr_type) + { + case tree_constant_rep::complex_scalar_constant: + case tree_constant_rep::scalar_constant: + { + tree_constant *rhs = new tree_constant (tmp_expr); + int quit = 0; + retval = do_for_loop_once (rhs, quit); + } + break; + case tree_constant_rep::complex_matrix_constant: + case tree_constant_rep::matrix_constant: + { + Matrix m_tmp; + ComplexMatrix cm_tmp; + int nr; + int steps; + if (expr_type == tree_constant_rep::matrix_constant) + { + m_tmp = tmp_expr.matrix_value (); + nr = m_tmp.rows (); + steps = m_tmp.columns (); + } + else + { + cm_tmp = tmp_expr.complex_matrix_value (); + nr = cm_tmp.rows (); + steps = cm_tmp.columns (); + } + + for (int i = 0; i < steps; i++) + { + tree_constant *rhs; + + if (nr == 1) + { + if (expr_type == tree_constant_rep::matrix_constant) + rhs = new tree_constant (m_tmp (0, i)); + else + rhs = new tree_constant (cm_tmp (0, i)); + } + else + { + if (expr_type == tree_constant_rep::matrix_constant) + rhs = new tree_constant (m_tmp.extract (0, i, nr-1, i)); + else + rhs = new tree_constant (cm_tmp.extract (0, i, nr-1, i)); + } + + int quit = 0; + retval = do_for_loop_once (rhs, quit); + if (quit) + break; + } + } + break; + case tree_constant_rep::string_constant: + gripe_string_invalid (); + break; + case tree_constant_rep::range_constant: + { + Range rng = tmp_expr.range_value (); + + int steps = rng.nelem (); + double b = rng.base (); + double increment = rng.inc (); + + for (int i = 0; i < steps; i++) + { + double tmp_val = b + i * increment; + + tree_constant *rhs = new tree_constant (tmp_val); + + int quit = 0; + retval = do_for_loop_once (rhs, quit); + if (quit) + break; + } + } + break; + default: + panic_impossible (); + break; + } + + return retval; +} + +void +tree_for_command::eval_error (void) +{ + if (error_state > 0) + ::error ("evaluating for command near line %d, column %d", + line (), column ()); +} + +tree_constant +tree_for_command::do_for_loop_once (tree_constant *rhs, int& quit) +{ + tree_constant retval; + + quit = 0; + + tree_simple_assignment_expression tmp_ass (id, rhs); + + tmp_ass.eval (0); + + if (error_state) + { + eval_error (); + return tree_constant (); + } + + if (list != NULL_TREE) + { + retval = list->eval (1); + if (error_state) + { + eval_error (); + quit = 1; + return tree_constant (); + } + } + + quit = quit_loop_now (); + + return retval; +} + +/* + * If. + */ +tree_if_command::tree_if_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + expr = (tree_expression *) NULL; + list = NULL_TREE; + next = (tree_if_command *) NULL; +} + +tree_if_command::tree_if_command (tree *lst, int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + expr = (tree_expression *) NULL; + list = lst; + next = (tree_if_command *) NULL; +} + +tree_if_command::tree_if_command (tree_expression *e, tree *lst, + int l = -1, int c = -1) +{ + line_num = l; + column_num = c; + expr = e; + list = lst; + next = (tree_if_command *) NULL; +} + +tree_if_command::~tree_if_command (void) +{ + delete expr; + delete list; + delete next; +} + +tree_if_command * +tree_if_command::chain (tree *lst, int l = -1, int c = -1) +{ + tree_if_command *tmp = new tree_if_command (lst, l, c); + tmp->next = this; + return tmp; +} + +tree_if_command * +tree_if_command::chain (tree_expression *e, tree *lst, int l = -1, int c = -1) +{ + tree_if_command *tmp = new tree_if_command (e, lst, l, c); + tmp->next = this; + return tmp; +} + +tree_if_command * +tree_if_command::reverse (void) +{ + tree_if_command *list = this; + tree_if_command *next; + tree_if_command *prev = (tree_if_command *) NULL; + + while (list != (tree_if_command *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +tree_constant +tree_if_command::eval (int print) +{ + int expr_value = 0; + tree_constant retval; + + if (error_state) + return retval; + + tree_if_command *lst; + for (lst = this; lst != (tree_if_command *) NULL; lst = lst->next) + { + if (lst->expr != (tree_expression *) NULL) + { + tree_expression *tmp = lst->expr; + if (tmp == (tree_expression *) NULL) + return tree_constant (); + tree_constant t1 = tmp->eval (0); + if (error_state || t1.is_undefined ()) + { + lst->eval_error (); + break; + } + + if (t1.rows () == 0 || t1.columns () == 0) + { + int flag = user_pref.propagate_empty_matrices; + if (flag < 0) + warning ("if: empty matrix used in conditional"); + else if (flag == 0) + { + ::error ("if: empty matrix used in conditional"); + lst->eval_error (); + return tree_constant (); + } + t1 = tree_constant (0.0); + } + else if (! t1.is_scalar_type ()) + { + tree_constant t2 = t1.all (); + t1 = t2.all (); + } + + tree_constant_rep::constant_type t = t1.const_type (); + if (t == tree_constant_rep::scalar_constant) + expr_value = (int) t1.double_value (); + else if (t == tree_constant_rep::complex_scalar_constant) + expr_value = t1.complex_value () != 0.0; + else + panic_impossible (); + + if (expr_value) + { + if (lst->list != NULL_TREE) + retval = lst->list->eval (1); + else + ::error ("if: empty command list"); + + if (error_state) + lst->eval_error (); + + break; + } + } + else + { + if (lst->list != NULL_TREE) + retval = lst->list->eval (1); + else + ::error ("if: empty command list"); + + if (error_state) + lst->eval_error (); + + break; + } + } + + return retval; +} + +void +tree_if_command::eval_error (void) +{ + if (error_state > 0) + ::error ("evaluating if command near line %d, column %d", + line (), column ()); +} + +/* + * Break. Is this overkill, or what? + */ +tree_break_command::tree_break_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; +} + +tree_break_command::~tree_break_command (void) +{ +} + +tree_constant +tree_break_command::eval (int print) +{ + if (! error_state) + breaking = 1; + return tree_constant (); +} + +/* + * Continue. + */ +tree_continue_command::tree_continue_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; +} + +tree_continue_command::~tree_continue_command (void) +{ +} + +tree_constant +tree_continue_command::eval (int print) +{ + if (! error_state) + continuing = 1; + return tree_constant (); +} + +/* + * Return. + */ +tree_return_command::tree_return_command (int l = -1, int c = -1) +{ + line_num = l; + column_num = c; +} + +tree_return_command::~tree_return_command (void) +{ +} + +tree_constant +tree_return_command::eval (int print) +{ + if (! error_state) + returning = 1; + return tree_constant (); +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; page-delimiter: "^/\\*" *** +;;; End: *** +*/ diff --git a/src/pt-cmd.h b/src/pt-cmd.h new file mode 100644 --- /dev/null +++ b/src/pt-cmd.h @@ -0,0 +1,232 @@ +// Tree classes. -*- C++ -*- +/* + +Copyright (C) 1992, 1993, 1994 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#if !defined (octave_tree_cmd_h) +#define octave_tree_cmd_h 1 + +#if defined (__GNUG__) +#pragma interface +#endif + +class tree_expression; +class tree_index_expression; +class tree_constant; +class symbol_record; + +class tree_command; +class tree_command_list; +class tree_global_command; +class tree_while_command; +class tree_for_command; +class tree_if_command; +class tree_break_command; +class tree_continue_command; +class tree_return_command; + +/* + * A base class for commands. + */ +class +tree_command : public tree +{ +}; + +/* + * A command or two to be executed. + */ +class +tree_command_list : public tree_command +{ + public: + tree_command_list (void); + tree_command_list (tree *t); + + ~tree_command_list (void); + + tree_command_list *chain (tree *t); + tree_command_list *reverse (void); + + void set_print_flag (int print); + + tree_constant eval (int print); + + private: + tree *command; // Command to execute. + int print_flag; // Print result of eval for this command? + tree_command_list *next; // Next command in list. +}; + +/* + * Global. + */ +class +tree_global_command : public tree_command +{ + public: + tree_global_command (int l = -1, int c = -1); + tree_global_command (symbol_record *s, int l = -1, int c = -1); + tree_global_command (symbol_record *s, tree_expression *e, + int l = -1, int c = -1); + + ~tree_global_command (void); + + tree_global_command *chain (symbol_record *s, int l = -1, int c = -1); + tree_global_command *chain (symbol_record *s, tree_expression *e, + int l = -1, int c = -1); + tree_global_command *reverse (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + symbol_record *sr; // Symbol record from local symbol table. + tree_expression *rhs; // RHS of assignment. + tree_global_command *next; // Next global command. +}; + +/* + * While. + */ +class +tree_while_command : public tree_command +{ + public: + tree_while_command (int l = -1, int c = -1); + tree_while_command (tree_expression *e, int l = -1, int c = -1); + tree_while_command (tree_expression *e, tree *lst, int l = -1, int c = -1); + + ~tree_while_command (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_expression *expr; // Expression to test. + tree *list; // List of commands to execute. +}; + +/* + * For. + */ +class +tree_for_command : public tree_command +{ + public: + tree_for_command (int l = -1, int c = -1); + tree_for_command (tree_index_expression *id, tree_expression *e, tree *lst, + int l = -1, int c = -1); + + ~tree_for_command (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_constant do_for_loop_once (tree_constant *rhs, int& quit); + + tree_index_expression *id; // Identifier to modify. + tree_expression *expr; // Expression to evaluate. + tree *list; // List of commands to execute. +}; + +/* + * Simple if. + */ +class +tree_if_command : public tree_command +{ + public: + tree_if_command (int l = -1, int c = -1); + tree_if_command (tree *lst, int l = -1, int c = -1); + tree_if_command (tree_expression *e, tree *lst, int l = -1, int c = -1); + + ~tree_if_command (void); + + tree_if_command *chain (tree *lst, int l = -1, int c = -1); + tree_if_command *chain (tree_expression *e, tree *lst, int l = -1, + int c = -1); + tree_if_command *reverse (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_expression *expr; // Expression to test. + tree *list; // Commands to execute. + tree_if_command *next; // Next if command. +}; + +/* + * Break. + */ +class +tree_break_command : public tree_command +{ + public: + tree_break_command (int l = -1, int c = -1); + + ~tree_break_command (void); + + tree_constant eval (int print); +}; + +/* + * Continue. + */ +class +tree_continue_command : public tree_command +{ + public: + tree_continue_command (int l = -1, int c = -1); + + ~tree_continue_command (void); + + tree_constant eval (int print); +}; + +/* + * Return. + */ +class +tree_return_command : public tree_command +{ + public: + tree_return_command (int l = -1, int c = -1); + + ~tree_return_command (void); + + tree_constant eval (int print); +}; + +#endif + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; page-delimiter: "^/\\*" *** +;;; End: *** +*/ diff --git a/src/pt-exp-base.cc b/src/pt-exp-base.cc new file mode 100644 --- /dev/null +++ b/src/pt-exp-base.cc @@ -0,0 +1,3026 @@ +// Tree class. -*- C++ -*- +/* + +Copyright (C) 1992, 1993, 1994 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#if defined (__GNUG__) +#pragma implementation +#endif + +#include +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include +#include +#include +#include + +#include "variables.h" +#include "user-prefs.h" +#include "error.h" +#include "pager.h" +#include "tree.h" +#include "tree-expr.h" +#include "tree-const.h" +#include "input.h" +#include "symtab.h" +#include "utils.h" +#include "octave.h" +#include "octave-hist.h" +#include "unwind-prot.h" +#include "parse.h" +#include "lex.h" + +extern "C" +{ +#include +} + +// Nonzero means we're returning from a function. +extern int returning; + +// But first, some extra functions used by the tree classes. + +// We seem to have no use for this now. Maybe it will be needed at +// some future date, so here it is. +#if 0 +/* + * Convert a linked list of trees to a vector of pointers to trees. + */ +static tree ** +list_to_vector (tree *list, int& len) +{ + len = list->length () + 1; + + tree **args = new tree * [len]; + +// args[0] may eventually hold something useful, like the function +// name. + tree *tmp_list = list; + for (int k = 1; k < len; k++) + { + args[k] = tmp_list; + tmp_list = tmp_list->next_elem (); + } + return args; +} +#endif + +static int +print_as_scalar (const tree_constant& val) +{ + int nr = val.rows (); + int nc = val.columns (); + return (val.is_scalar_type () + || val.is_string_type () + || (val.is_matrix_type () + && ((nr == 1 && nc == 1) + || nr == 0 + || nc == 0))); +} + +/* + * Make sure that all arguments have values. + */ +static int +all_args_defined (tree_constant *args, int nargs) +{ + while (--nargs > 0) + { + if (args[nargs].is_undefined ()) + return 0; + } + return 1; +} + +/* + * Are any of the arguments `:'? + */ +static int +any_arg_is_magic_colon (const tree_constant *args, int nargs) +{ + while (--nargs > 0) + { + if (args[nargs].const_type () == tree_constant_rep::magic_colon) + return 1; + } + return 0; +} + +// NOTE: functions for the tree_constant_rep and tree_constant classes +// are now defined in tree-const.cc. This should help speed up +// compilation when working only on the tree_constant class. + +/* + * General matrices. This list type is much more work to handle than + * constant matrices, but it allows us to construct matrices from + * other matrices, variables, and functions. + */ +tree_matrix::tree_matrix (void) +{ + dir = tree::md_none; + element = (tree_expression *) NULL; + next = (tree_matrix *) NULL; +} + +tree_matrix::tree_matrix (tree_expression *e, tree::matrix_dir d) +{ + dir = d; + element = e; + next = (tree_matrix *) NULL; +} + +tree_matrix::~tree_matrix (void) +{ + delete element; + delete next; +} + +tree_matrix * +tree_matrix::chain (tree_expression *t, tree::matrix_dir d) +{ + tree_matrix *tmp = new tree_matrix (t, d); + tmp->next = this; + return tmp; +} + +tree_matrix * +tree_matrix::reverse (void) +{ + tree_matrix *list = this; + tree_matrix *next; + tree_matrix *prev = (tree_matrix *) NULL; + + while (list != (tree_matrix *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +int +tree_matrix::length (void) +{ + tree_matrix *list = this; + int len = 0; + while (list != (tree_matrix *) NULL) + { + len++; + list = list->next; + } + return len; +} + +tree_return_list * +tree_matrix::to_return_list (void) +{ + tree_return_list *retval = (tree_return_list *) NULL; + tree_matrix *list; + for (list = this; list != (tree_matrix *) NULL; list = list->next) + { + tree_expression *elem = list->element; + if (elem->is_identifier ()) + { + tree_identifier *id = (tree_identifier *) elem; + if (list == this) + retval = new tree_return_list (id); + else + retval = retval->chain (id); + } + else if (elem->is_index_expression ()) +// && (((tree_index_expression *) elem) -> arg_list () +// == (tree_argument_list *) NULL)) + { + tree_index_expression *idx_expr = (tree_index_expression *) elem; + if (list == this) + retval = new tree_return_list (idx_expr); + else + retval = retval->chain (idx_expr); + } + else + { + delete retval; + retval = (tree_return_list *) NULL; + break; + } + } + + if (retval != (tree_return_list *) NULL) + retval = retval->reverse (); + return retval; +} + +// Just about as ugly as it gets. + +struct const_matrix_list +{ + tree::matrix_dir dir; + tree_constant elem; + int nr; + int nc; +}; + +// Less ugly than before, anyway. + +tree_constant +tree_matrix::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + +// Just count the elements without looking at them. + + int total_len = length (); + +// Easier to deal with this later instead of a tree_matrix structure. + + const_matrix_list *list = new const_matrix_list [total_len]; + +// Stats we want to keep track of. + + int all_strings = 1; + + int found_complex = 0; + + int row_total = 0; + int col_total = 0; + + int row_height = 0; + + int cols_this_row = 0; + + int first_row = 1; + + int empties_ok = user_pref.empty_list_elements_ok; + + tree_matrix *ptr = this; + +// Stuff for the result matrix or string. Declared here so that we +// don't get warnings from gcc about the goto crossing the +// initialization of these values. + + int put_row = 0; + int put_col = 0; + + int prev_nr = 0; + int prev_nc = 0; + + Matrix m; + ComplexMatrix cm; + + char *string = (char *) NULL; + char *str_ptr = (char *) NULL; + +// Eliminate empties and gather stats. + + int found_new_row_in_empties = 0; + + int len = 0; + for (int i = 0; i < total_len; i++) + { + tree_expression *elem = ptr->element; + if (elem == (tree_expression *) NULL) + { + retval = tree_constant (Matrix ()); + goto done; + } + + tree_constant tmp = elem->eval (0); + if (error_state || tmp.is_undefined ()) + { + retval = tree_constant (); + goto done; + } + + int nr = tmp.rows (); + int nc = tmp.columns (); + + matrix_dir direct = ptr->dir; + + if (nr == 0 || nc == 0) + { + if (empties_ok < 0) + warning ("empty matrix found in matrix list"); + else if (empties_ok == 0) + { + ::error ("empty matrix found in matrix list"); + retval = tree_constant (); + goto done; + } + + if (direct == md_down) + found_new_row_in_empties = 1; + + goto next; + } + + if (found_new_row_in_empties) + { + found_new_row_in_empties = 0; + list[len].dir = md_down; + } + else + list[len].dir = direct; + + list[len].elem = tmp; + list[len].nr = nr; + list[len].nc = nc; + + if (all_strings && ! tmp.is_string_type ()) + all_strings = 0; + + if (! found_complex && tmp.is_complex_type ()) + found_complex = 1; + + len++; + + next: + + ptr = ptr->next; + } + +// if (all_strings) +// cerr << "all strings\n"; + +// Compute size of result matrix, and check to see that the dimensions +// of all the elements will match up properly. + + for (i = 0; i < len; i++) + { + matrix_dir direct = list[i].dir; + int nr = list[i].nr; + int nc = list[i].nc; + + if (i == 0) + { + row_total = nr; + col_total = nc; + + row_height = nr; + cols_this_row = nc; + } + else + { + switch (direct) + { + case md_right: + { + if (nr != row_height) + { + ::error ("number of rows must match"); + goto done; + } + else + { + cols_this_row += nc; + if (first_row) + col_total = cols_this_row; + } + } + break; + case md_down: + { + if (cols_this_row != col_total) + { + ::error ("number of columns must match"); + goto done; + } + first_row = 0; + row_total += nr; + row_height = nr; + cols_this_row = nc; + } + break; + default: + panic_impossible (); + break; + } + } + } + +// Don\'t forget to check to see if the last element will fit. + + if (cols_this_row != col_total) + { + ::error ("number of columns must match"); + goto done; + } + +// Now, extract the values from the individual elements and insert +// them in the result matrix. + + if (all_strings && row_total == 1 && col_total > 0) + { + string = str_ptr = new char [col_total + 1]; + string[col_total] = '\0'; + } + else if (found_complex) + cm.resize (row_total, col_total, 0.0); + else + m.resize (row_total, col_total, 0.0); + + for (i = 0; i < len; i++) + { + tree_constant tmp = list[i].elem; + tree_constant_rep::constant_type tmp_type = tmp.const_type (); + + int nr = list[i].nr; + int nc = list[i].nc; + + if (nr == 0 || nc == 0) + continue; + + if (i == 0) + { + put_row = 0; + put_col = 0; + } + else + { + switch (list[i].dir) + { + case md_right: + put_col += prev_nc; + break; + case md_down: + put_row += prev_nr; + put_col = 0; + break; + default: + panic_impossible (); + break; + } + } + + if (found_complex) + { + switch (tmp_type) + { + case tree_constant_rep::scalar_constant: + cm (put_row, put_col) = tmp.double_value (); + break; + case tree_constant_rep::string_constant: + if (all_strings && str_ptr != (char *) NULL) + { + memcpy (str_ptr, tmp.string_value (), nc); + str_ptr += nc; + break; + } + case tree_constant_rep::range_constant: + tmp_type = tmp.force_numeric (1); + if (tmp_type == tree_constant_rep::scalar_constant) + m (put_row, put_col) = tmp.double_value (); + else if (tmp_type == tree_constant_rep::matrix_constant) + m.insert (tmp.matrix_value (), put_row, put_col); + else + panic_impossible (); + break; + case tree_constant_rep::matrix_constant: + cm.insert (tmp.matrix_value (), put_row, put_col); + break; + case tree_constant_rep::complex_scalar_constant: + cm (put_row, put_col) = tmp.complex_value (); + break; + case tree_constant_rep::complex_matrix_constant: + cm.insert (tmp.complex_matrix_value (), put_row, put_col); + break; + case tree_constant_rep::magic_colon: + default: + panic_impossible (); + break; + } + } + else + { + switch (tmp_type) + { + case tree_constant_rep::scalar_constant: + m (put_row, put_col) = tmp.double_value (); + break; + case tree_constant_rep::string_constant: + if (all_strings && str_ptr != (char *) NULL) + { + memcpy (str_ptr, tmp.string_value (), nc); + str_ptr += nc; + break; + } + case tree_constant_rep::range_constant: + tmp_type = tmp.force_numeric (1); + if (tmp_type == tree_constant_rep::scalar_constant) + m (put_row, put_col) = tmp.double_value (); + else if (tmp_type == tree_constant_rep::matrix_constant) + m.insert (tmp.matrix_value (), put_row, put_col); + else + panic_impossible (); + break; + case tree_constant_rep::matrix_constant: + m.insert (tmp.matrix_value (), put_row, put_col); + break; + case tree_constant_rep::complex_scalar_constant: + case tree_constant_rep::complex_matrix_constant: + case tree_constant_rep::magic_colon: + default: + panic_impossible (); + break; + } + } + + prev_nr = nr; + prev_nc = nc; + } + + if (all_strings && string != (char *) NULL) + retval = tree_constant (string); + else if (found_complex) + retval = tree_constant (cm); + else + retval = tree_constant (m); + + done: + delete [] list; + + return retval; +} + +tree_constant +tree_fvc::assign (tree_constant& t, tree_constant *args, int nargs) +{ + panic_impossible (); + return tree_constant (); +} + +/* + * Builtin functions. + */ +tree_builtin::tree_builtin (const char *nm = (char *) NULL) +{ + nargin_max = -1; + nargout_max = -1; + text_fcn = (Text_fcn) NULL; + general_fcn = (General_fcn) NULL; + if (nm != (char *) NULL) + my_name = strsave (nm); +} + +tree_builtin::tree_builtin (int i_max, int o_max, Mapper_fcn& m_fcn, + const char *nm = (char *) NULL) +{ + nargin_max = i_max; + nargout_max = o_max; + mapper_fcn = m_fcn; + text_fcn = (Text_fcn) NULL; + general_fcn = (General_fcn) NULL; + if (nm != (char *) NULL) + my_name = strsave (nm); +} + +tree_builtin::tree_builtin (int i_max, int o_max, Text_fcn t_fcn, + const char *nm = (char *) NULL) +{ + nargin_max = i_max; + nargout_max = o_max; + text_fcn = t_fcn; + general_fcn = (General_fcn) NULL; + if (nm != (char *) NULL) + my_name = strsave (nm); +} + +tree_builtin::tree_builtin (int i_max, int o_max, General_fcn g_fcn, + const char *nm = (char *) NULL) +{ + nargin_max = i_max; + nargout_max = o_max; + text_fcn = (Text_fcn) NULL; + general_fcn = g_fcn; + if (nm != (char *) NULL) + my_name = strsave (nm); +} + +tree_builtin::~tree_builtin (void) +{ +} + +#if 0 +int +tree_builtin::is_builtin (void) const +{ + return 1; +} +#endif + +tree_constant +tree_builtin::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + if (text_fcn != (Text_fcn) NULL) + { + char **argv = new char * [1]; + argv[0] = strsave (my_name); + tree_constant *tmp = (*text_fcn) (1, argv, 1); + if (tmp != NULL_TREE) + retval = tmp[0]; + delete [] tmp; + delete [] argv; + } + else if (general_fcn != (General_fcn) NULL) + { + tree_constant *args = new tree_constant [1]; + args[0] = tree_constant (my_name); + tree_constant *tmp = (*general_fcn) (args, 1, 1); + delete [] args; + if (tmp != NULL_TREE_CONST) + retval = tmp[0]; + delete [] tmp; + } + else // Assume mapper function + ::error ("%s: argument expected", my_name); + + return retval; +} + +tree_constant * +tree_builtin::eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0) +{ + tree_constant *retval = NULL_TREE_CONST; + + if (error_state) + return retval; + + if (text_fcn != (Text_fcn) NULL) + { +// XXX FIXME XXX -- what if some arg is not a string? + + int argc = nargin; + char **argv = new char * [argc + 1]; + argv[0] = strsave (my_name); + for (int i = 1; i < argc; i++) + argv[i] = strsave (args[i].string_value ()); + argv[argc] = (char *) NULL; + + retval = (*text_fcn) (argc, argv, nargout); + + for (i = 0; i < argc; i++) + delete [] argv[i]; + delete [] argv; + } + else if (general_fcn != (General_fcn) NULL) + { + if (any_arg_is_magic_colon (args, nargin)) + ::error ("invalid use of colon in function argument list"); + else + retval = (*general_fcn) (args, nargin, nargout); + } + else + { + if (nargin > nargin_max) + ::error ("%s: too many arguments", my_name); + else if (nargin > 0 && args != NULL_TREE_CONST && args[1].is_defined ()) + { + tree_constant tmp = args[1].mapper (mapper_fcn, 0); + retval = new tree_constant [2]; + retval[0] = tmp; + retval[1] = tree_constant (); + } + } + + return retval; +} + +char * +tree_builtin::name (void) const +{ + return my_name; +} + +int +tree_builtin::max_expected_args (void) +{ + int ea = nargin_max; + if (nargin_max < 0) + ea = INT_MAX; + else + ea = nargin_max; + return ea; +} + +/* + * Symbols from the symbol table. + */ +tree_identifier::tree_identifier (int l = -1, int c = -1) +{ + sym = (symbol_record *) NULL; + line_num = l; + column_num = c; + maybe_do_ans_assign = 0; +} + +tree_identifier::tree_identifier (symbol_record *s, int l = -1, int c = -1) +{ + sym = s; + line_num = l; + column_num = c; + maybe_do_ans_assign = 0; +} + +tree_identifier::~tree_identifier (void) +{ +} + +int +tree_identifier::is_identifier (void) const +{ + return 1; +} + +char * +tree_identifier::name (void) const +{ + return sym->name (); +} + +void +tree_identifier::rename (const char *n) +{ + sym->rename (n); +} + +tree_identifier * +tree_identifier::define (tree_constant *t) +{ + int status = sym->define (t); + if (status) + return this; + else + return (tree_identifier *) NULL; +} + +tree_identifier * +tree_identifier::define (tree_function *t) +{ + int status = sym->define (t); + if (status) + return this; + else + return (tree_identifier *) NULL; +} + +void +tree_identifier::document (char *s) +{ + if (sym != (symbol_record *) NULL && s != (char *) NULL) + { + char *tmp = strsave (s); + sym->document (tmp); + } +} + +tree_constant +tree_identifier::assign (tree_constant& rhs) +{ + int status = 0; + + if (rhs.is_defined ()) + { + if (! sym->is_defined ()) + { + if (! (sym->is_formal_parameter () + || sym->is_linked_to_global ())) + { + link_to_builtin_variable (sym); + } + } + else if (sym->is_function ()) + { + sym->clear (); + } + + tree_constant *tmp = new tree_constant (rhs); + status = sym->define (tmp); + } + + if (status) + return rhs; + else + return tree_constant (); +} + +tree_constant +tree_identifier::assign (tree_constant& rhs, tree_constant *args, int nargs) +{ + tree_constant retval; + + if (rhs.is_defined ()) + { + if (! sym->is_defined ()) + { + if (! (sym->is_formal_parameter () + || sym->is_linked_to_global ())) + { + link_to_builtin_variable (sym); + } + } + else if (sym->is_function ()) + { + sym->clear (); + } + + if (sym->is_variable () && sym->is_defined ()) + { + tree_fvc *tmp = sym->def (); + retval = tmp->assign (rhs, args, nargs); + } + else + { + assert (! sym->is_defined ()); + + if (! user_pref.resize_on_range_error) + { + ::error ("indexed assignment to previously undefined variables"); + ::error ("is only possible when resize_on_range_error is true"); + return retval; + } + + tree_constant *tmp = new tree_constant (); + retval = tmp->assign (rhs, args, nargs); + if (retval.is_defined ()) + sym->define (tmp); + } + } + + return retval; +} + +void +tree_identifier::bump_value (tree::expression_type etype) +{ + if (sym != (symbol_record *) NULL) + { + tree_fvc *tmp = sym->def (); + if (tmp != NULL_TREE) + tmp->bump_value (etype); + } +} + +int +tree_identifier::parse_fcn_file (int exec_script = 1) +{ + curr_fcn_file_name = name (); + char *ff = fcn_file_in_path (curr_fcn_file_name); + int script_file_executed = parse_fcn_file (ff, exec_script); + delete [] ff; + + if (! (error_state || script_file_executed)) + force_link_to_function (name ()); + + return script_file_executed; +} + +static void +gobble_leading_white_space (FILE *ffile) +{ + int in_comment = 0; + int c; + while ((c = getc (ffile)) != EOF) + { + if (in_comment) + { + if (c == '\n') + in_comment = 0; + } + else + { + if (c == ' ' || c == '\t' || c == '\n') + continue; + else if (c == '%' || c == '#') + in_comment = 1; + else + { + ungetc (c, ffile); + break; + } + } + } +} + +static int +is_function_file (FILE *ffile) +{ + int status = 0; + + gobble_leading_white_space (ffile); + + long pos = ftell (ffile); + + char buf [10]; + fgets (buf, 10, ffile); + int len = strlen (buf); + if (len > 8 && strncmp (buf, "function", 8) == 0 + && ! (isalnum (buf[8]) || buf[8] == '_')) + status = 1; + + fseek (ffile, pos, SEEK_SET); + + return status; +} + +int +tree_identifier::parse_fcn_file (char *ff, int exec_script = 1) +{ + begin_unwind_frame ("parse_fcn_file"); + + int script_file_executed = 0; + + if (ff != (char *) NULL) + { +// Open function file and parse. + + int old_reading_fcn_file_state = reading_fcn_file; + + unwind_protect_ptr (rl_instream); + unwind_protect_ptr (ff_instream); + + unwind_protect_int (using_readline); + unwind_protect_int (input_line_number); + unwind_protect_int (current_input_column); + unwind_protect_int (reading_fcn_file); + + using_readline = 0; + reading_fcn_file = 1; + input_line_number = 0; + current_input_column = 1; + + FILE *ffile = get_input_from_file (ff, 0); + + if (ffile != (FILE *) NULL) + { +// Check to see if this file defines a function or is just a list of +// commands. + + if (is_function_file (ffile)) + { + parse_fcn_file (ffile, ff); + } + else if (exec_script) + { +// The value of `reading_fcn_file' will be restored to the proper value +// when we unwind from this frame. + reading_fcn_file = old_reading_fcn_file_state; + + unwind_protect_int (reading_script_file); + reading_script_file = 1; + + parse_and_execute (ffile, 1); + + script_file_executed = 1; + } + fclose (ffile); + } + + run_unwind_frame ("parse_fcn_file"); + } + + return script_file_executed; +} + +void +tree_identifier::parse_fcn_file (FILE *ffile, char *ff) +{ + begin_unwind_frame ("parse_fcn_file_2"); + + unwind_protect_int (echo_input); + unwind_protect_int (saving_history); + unwind_protect_int (reading_fcn_file); + + echo_input = 0; + saving_history = 0; + reading_fcn_file = 1; + + YY_BUFFER_STATE old_buf = current_buffer (); + YY_BUFFER_STATE new_buf = create_buffer (ffile); + + add_unwind_protect (restore_input_buffer, (void *) old_buf); + add_unwind_protect (delete_input_buffer, (void *) new_buf); + + switch_to_buffer (new_buf); + + unwind_protect_ptr (curr_sym_tab); + + reset_parser (); + + int status = yyparse (); + + if (status != 0) + { + ::error ("parse error while reading function file %s", ff); + global_sym_tab->clear (curr_fcn_file_name); + } + + run_unwind_frame ("parse_fcn_file_2"); +} + +void +tree_identifier::eval_undefined_error (void) +{ + char *nm = sym->name (); + int l = line (); + int c = column (); + if (l == -1 && c == -1) + ::error ("`%s' undefined"); + else + ::error ("`%s' undefined near line %d column %d", nm, l, c); +} + +/* + * Try to find a definition for an identifier. Here's how: + * + * * If the identifier is already defined and is a function defined + * in an function file that has been modified since the last time + * we parsed it, parse it again. + * + * * If the identifier is not defined, try to find a builtin + * variable or an already compiled function with the same name. + * + * * If the identifier is still undefined, try looking for an + * function file to parse. + */ +tree_fvc * +tree_identifier::do_lookup (int& script_file_executed) +{ + script_file_executed = 0; + + if (! sym->is_linked_to_global ()) + { + if (sym->is_defined ()) + { + if (sym->is_function () && symbol_out_of_date (sym)) + { + script_file_executed = parse_fcn_file (); + } + } + else if (! sym->is_formal_parameter ()) + { + link_to_builtin_or_function (sym); + + if (! sym->is_defined ()) + { + script_file_executed = parse_fcn_file (); + } + else if (sym->is_function () && symbol_out_of_date (sym)) + { + script_file_executed = parse_fcn_file (); + } + } + } + + tree_fvc *ans = (tree_fvc *) NULL; + + if (! script_file_executed) + ans = sym->def (); + + return ans; +} + +void +tree_identifier::mark_as_formal_parameter (void) +{ + if (sym != (symbol_record *) NULL) + sym->mark_as_formal_parameter (); +} + +void +tree_identifier::mark_for_possible_ans_assign (void) +{ + maybe_do_ans_assign = 1; +} + +tree_constant +tree_identifier::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + int script_file_executed = 0; + + tree_fvc *ans = do_lookup (script_file_executed); + + if (! script_file_executed) + { + if (ans == (tree_fvc *) NULL) + eval_undefined_error (); + else + { + int nargout = maybe_do_ans_assign ? 0 : 1; + + tree_constant *tmp = ans->eval (0, nargout); + + if (tmp != NULL_TREE_CONST) + retval = tmp[0]; + + delete [] tmp; + } + } + + if (! error_state && retval.is_defined ()) + { + if (maybe_do_ans_assign && ! ans->is_constant ()) + { + symbol_record *sr = global_sym_tab->lookup ("ans", 1, 0); + + assert (sr != (symbol_record *) NULL); + + tree_identifier *ans_id = new tree_identifier (sr); + + tree_constant *tmp = new tree_constant (retval); + + tree_simple_assignment_expression tmp_ass (ans_id, tmp); + + tmp_ass.eval (print); + + delete ans_id; // XXX FIXME XXX + } + else + { + if (print) + { + int pad_after = 0; + if (user_pref.print_answer_id_name) + { + char *result_tag = name (); + + if (print_as_scalar (retval)) + { + ostrstream output_buf; + output_buf << result_tag << " = " << ends; + maybe_page_output (output_buf); + } + else + { + pad_after = 1; + ostrstream output_buf; + output_buf << result_tag << " =\n\n" << ends; + maybe_page_output (output_buf); + } + } + + retval.eval (print); + + if (pad_after) + { + ostrstream output_buf; + output_buf << "\n" << ends; + maybe_page_output (output_buf); + } + } + } + } + return retval; +} + +tree_constant * +tree_identifier::eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0) +{ + tree_constant *retval = NULL_TREE_CONST; + + if (error_state) + return retval; + + int script_file_executed = 0; + + tree_fvc *ans = do_lookup (script_file_executed); + + if (! script_file_executed) + { + if (ans == (tree_fvc *) NULL) + eval_undefined_error (); + else + { + if (maybe_do_ans_assign && nargout == 1) + { + +// Don't count the output arguments that we create automatically. + + nargout = 0; + + retval = ans->eval (0, nargout, args, nargin); + + if (retval != NULL_TREE_CONST && retval[0].is_defined ()) + { + symbol_record *sr = global_sym_tab->lookup ("ans", 1, 0); + + assert (sr != (symbol_record *) NULL); + + tree_identifier *ans_id = new tree_identifier (sr); + + tree_constant *tmp = new tree_constant (retval[0]); + + tree_simple_assignment_expression tmp_ass (ans_id, tmp); + + tmp_ass.eval (print); + + delete ans_id; // XXX FIXME XXX + } + } + else + retval = ans->eval (print, nargout, args, nargin); + } + } + + return retval; +} + +/* + * User defined functions. + */ +tree_function::tree_function (void) +{ + call_depth = 0; + param_list = (tree_parameter_list *) NULL; + ret_list = (tree_parameter_list *) NULL; + sym_tab = (symbol_table *) NULL; + cmd_list = NULL_TREE; + file_name = (char *) NULL; + fcn_name = (char *) NULL; + t_parsed = 0; + system_fcn_file = 0; + varargs_ok = 0; + num_named_args = 0; + args_passed = NULL_TREE_CONST; + num_args_passed = 0; + curr_arg_number = 0; +} + +tree_function::tree_function (tree *cl, symbol_table *st) +{ + call_depth = 0; + param_list = (tree_parameter_list *) NULL; + ret_list = (tree_parameter_list *) NULL; + sym_tab = st; + cmd_list = cl; + file_name = (char *) NULL; + fcn_name = (char *) NULL; + t_parsed = 0; + system_fcn_file = 0; + varargs_ok = 0; + num_named_args = 0; + args_passed = NULL_TREE_CONST; + num_args_passed = 0; + curr_arg_number = 0; +} + +tree_function::~tree_function (void) +{ +} + +tree_function * +tree_function::define (tree *t) +{ + cmd_list = t; + return this; +} + +tree_function * +tree_function::define_param_list (tree_parameter_list *t) +{ + param_list = t; + varargs_ok = (param_list != (tree_parameter_list *) NULL + && param_list->takes_varargs ()); + + if (varargs_ok) + num_named_args = param_list->length (); + + return this; +} + +tree_function * +tree_function::define_ret_list (tree_parameter_list *t) +{ + ret_list = t; + return this; +} + +void +tree_function::stash_fcn_file_name (char *s) +{ + delete [] file_name; + file_name = strsave (s); +} + +void +tree_function::stash_fcn_file_time (time_t t) +{ + t_parsed = t; +} + +char * +tree_function::fcn_file_name (void) +{ + return file_name; +} + +time_t +tree_function::time_parsed (void) +{ + return t_parsed; +} + +void +tree_function::mark_as_system_fcn_file (void) +{ + if (file_name != (char *) NULL) + { +// 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. + + char *oct_lib = octave_lib_dir (); + int len = strlen (oct_lib); + + char *ff_name = fcn_file_in_path (file_name); + + if (strncmp (oct_lib, ff_name, len) == 0) + system_fcn_file = 1; + + delete [] ff_name; + } + else + system_fcn_file = 0; +} + +int +tree_function::is_system_fcn_file (void) const +{ + return system_fcn_file; +} + +int +tree_function::takes_varargs (void) const +{ + return varargs_ok; +} + +void +tree_function::octave_va_start (void) +{ + curr_arg_number = num_named_args + 1; +} + +tree_constant +tree_function::octave_va_arg (void) +{ + tree_constant retval; + + if (curr_arg_number < num_args_passed) + { + retval = args_passed[curr_arg_number]; + curr_arg_number++; + } + else + ::error ("error getting arg number %d -- only %d provided", + curr_arg_number, num_args_passed-1); + + return retval; +} + +void +tree_function::stash_function_name (char *s) +{ + delete [] fcn_name; + fcn_name = strsave (s); +} + +char * +tree_function::function_name (void) +{ + return fcn_name; +} + +tree_constant +tree_function::eval (int print) +{ + tree_constant retval; + + if (error_state || cmd_list == NULL_TREE) + return retval; + + tree_constant *tmp = eval (print, 1); + + if (! error_state && tmp != NULL_TREE_CONST) + retval = tmp[0]; + delete [] tmp; + + return retval; +} + +// For unwind protect. + +static void +pop_symbol_table_context (void *table) +{ + symbol_table *tmp = (symbol_table *) table; + tmp->pop_context (); +} + +static void +clear_symbol_table (void *table) +{ + symbol_table *tmp = (symbol_table *) table; + tmp->clear (); +} + +tree_constant * +tree_function::eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0) +{ + tree_constant *retval = NULL_TREE_CONST; + + if (error_state) + return retval; + + if (cmd_list == NULL_TREE) + return retval; + + begin_unwind_frame ("func_eval"); + + unwind_protect_int (call_depth); + call_depth++; + + if (call_depth > 1) + { + sym_tab->push_context (); + add_unwind_protect (pop_symbol_table_context, (void *) sym_tab); + } + +// Force symbols to be undefined again when this function exits. + + add_unwind_protect (clear_symbol_table, (void *) 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; + + unwind_protect_ptr (args_passed); + args_passed = args; + + unwind_protect_int (num_args_passed); + num_args_passed = nargin; + + unwind_protect_int (num_named_args); + unwind_protect_int (curr_arg_number); + + if (param_list != (tree_parameter_list *) NULL) + { + param_list->define_from_arg_vector (args, nargin); + if (error_state) + goto abort; + } + +// The following code is in a separate scope to avoid warnings from +// G++ about `goto abort' crossing the initialization of some +// variables. + + { + bind_nargin_and_nargout (sym_tab, nargin, nargout); + +// Evaluate the commands that make up the function. Always turn on +// printing for commands inside functions. Maybe this should be +// toggled by a user-leval variable? + + int pf = ! user_pref.silent_functions; + tree_constant last_computed_value = cmd_list->eval (pf); + + if (returning) + returning = 0; + + if (error_state) + { + traceback_error (); + goto abort; + } + +// Copy return values out. + + if (ret_list != (tree_parameter_list *) NULL) + { + retval = ret_list->convert_to_const_vector (); + } + else if (user_pref.return_last_computed_value) + { + retval = new tree_constant [2]; + retval[0] = last_computed_value; + retval[1] = tree_constant (); + } + } + + abort: + run_unwind_frame ("func_eval"); + + return retval; +} + +int +tree_function::max_expected_args (void) +{ + if (param_list != NULL_TREE) + { + if (param_list->takes_varargs ()) + return -1; + else + return param_list->length () + 1; + } + else + return 1; +} + +void +tree_function::traceback_error (void) +{ + if (error_state >= 0) + error_state = -1; + + if (fcn_name != (char *) NULL) + { + if (file_name != (char *) NULL) + ::error ("called from `%s' in file `%s'", fcn_name, file_name); + else + ::error ("called from `%s'", fcn_name); + } + else + { + if (file_name != (char *) NULL) + ::error ("called from file `%s'", file_name); + else + ::error ("called from `?unknown?'"); + } +} + +/* + * Expressions. + */ +tree_expression::tree_expression (void) +{ + etype = tree::unknown; +} + +tree_expression::~tree_expression (void) +{ +} + +tree_constant +tree_expression::eval (int print) +{ + panic ("invalid evaluation of generic expression"); + return tree_constant (); +} + +/* + * Prefix expressions. + */ +tree_prefix_expression::tree_prefix_expression (int l = -1, int c = -1) +{ + id = (tree_identifier *) NULL; + etype = unknown; + line_num = l; + column_num = c; +} + +tree_prefix_expression::tree_prefix_expression (tree_identifier *t, + tree::expression_type et, + int l = -1, int c = -1) +{ + id = t; + etype = et; + line_num = l; + column_num = c; +} + +tree_prefix_expression::~tree_prefix_expression (void) +{ + delete id; +} + +tree_constant +tree_prefix_expression::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + if (id != (tree_identifier *) NULL) + { + id->bump_value (etype); + retval = id->eval (print); + if (error_state) + { + retval = tree_constant (); + if (error_state) + eval_error (); + } + } + return retval; +} + +void +tree_prefix_expression::eval_error (void) +{ + if (error_state > 0) + { + char *op; + switch (etype) + { + case tree::increment: op = "++"; break; + case tree::decrement: op = "--"; break; + default: op = "unknown"; break; + } + + ::error ("evaluating prefix operator `%s' near line %d, column %d", + op, line (), column ()); + } +} + +int +tree_prefix_expression::is_prefix_expression (void) const +{ + return 1; +} + +/* + * Postfix expressions. + */ +tree_postfix_expression::tree_postfix_expression (int l = -1, int c = -1) +{ + id = (tree_identifier *) NULL; + etype = unknown; + line_num = l; + column_num = c; +} + +tree_postfix_expression::tree_postfix_expression (tree_identifier *t, + tree::expression_type et, + int l = -1, int c = -1) +{ + id = t; + etype = et; + line_num = l; + column_num = c; +} + +tree_postfix_expression::~tree_postfix_expression (void) +{ + delete id; +} + +tree_constant +tree_postfix_expression::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + if (id != (tree_identifier *) NULL) + { + retval = id->eval (print); + id->bump_value (etype); + if (error_state) + { + retval = tree_constant (); + if (error_state) + eval_error (); + } + } + return retval; +} + +void +tree_postfix_expression::eval_error (void) +{ + if (error_state > 0) + { + char *op; + switch (etype) + { + case tree::increment: op = "++"; break; + case tree::decrement: op = "--"; break; + default: op = "unknown"; break; + } + + ::error ("evaluating postfix operator `%s' near line %d, column %d", + op, line (), column ()); + } +} + +/* + * Unary expressions. + */ +tree_unary_expression::tree_unary_expression (int l = -1, int c = -1) +{ + etype = tree::unknown; + op = (tree_expression *) NULL; + line_num = l; + column_num = c; +} + +tree_unary_expression::tree_unary_expression (tree_expression *a, + tree::expression_type t, + int l = -1, int c = -1) +{ + etype = t; + op = a; + line_num = l; + column_num = c; +} + +tree_unary_expression::~tree_unary_expression (void) +{ + delete op; +} + +tree_constant +tree_unary_expression::eval (int print) +{ + if (error_state) + return tree_constant (); + + tree_constant ans; + + switch (etype) + { + case tree::not: + case tree::uminus: + case tree::hermitian: + case tree::transpose: + if (op != (tree_expression *) NULL) + { + tree_constant u = op->eval (0); + if (error_state) + eval_error (); + else if (u.is_defined ()) + { + ans = do_unary_op (u, etype); + if (error_state) + { + ans = tree_constant (); + if (error_state) + eval_error (); + } + } + } + break; + default: + ::error ("unary operator %d not implemented", etype); + break; + } + + return ans; +} + +void +tree_unary_expression::eval_error (void) +{ + if (error_state > 0) + { + char *op; + switch (etype) + { + case tree::not: op = "!"; break; + case tree::uminus: op = "-"; break; + case tree::hermitian: op = "'"; break; + case tree::transpose: op = ".'"; break; + default: op = "unknown"; break; + } + + ::error ("evaluating unary operator `%s' near line %d, column %d", + op, line (), column ()); + } +} + +/* + * Binary expressions. + */ +tree_binary_expression::tree_binary_expression (int l = -1, int c = -1) +{ + etype = tree::unknown; + op1 = (tree_expression *) NULL; + op2 = (tree_expression *) NULL; + line_num = l; + column_num = c; +} + +tree_binary_expression::tree_binary_expression (tree_expression *a, + tree_expression *b, + tree::expression_type t, + int l = -1, int c = -1) +{ + etype = t; + op1 = a; + op2 = b; + line_num = l; + column_num = c; +} + +tree_binary_expression::~tree_binary_expression (void) +{ + delete op1; + delete op2; +} + +tree_constant +tree_binary_expression::eval (int print) +{ + if (error_state) + return tree_constant (); + + tree_constant ans; + switch (etype) + { + case tree::add: + case tree::subtract: + case tree::multiply: + case tree::el_mul: + case tree::divide: + case tree::el_div: + case tree::leftdiv: + case tree::el_leftdiv: + case tree::power: + case tree::elem_pow: + case tree::cmp_lt: + case tree::cmp_le: + case tree::cmp_eq: + case tree::cmp_ge: + case tree::cmp_gt: + case tree::cmp_ne: + case tree::and: + case tree::or: + if (op1 != (tree_expression *) NULL) + { + tree_constant a = op1->eval (0); + if (error_state) + eval_error (); + else if (a.is_defined () && op2 != (tree_expression *) NULL) + { + tree_constant b = op2->eval (0); + if (error_state) + eval_error (); + else if (b.is_defined ()) + { + ans = do_binary_op (a, b, etype); + if (error_state) + { + ans = tree_constant (); + if (error_state) + eval_error (); + } + } + } + } + break; + case tree::and_and: + case tree::or_or: + { + int result = 0; + if (op1 != NULL_TREE) + { + tree_constant a = op1->eval (0); + if (error_state) + { + eval_error (); + break; + } + + int a_true = a.is_true (); + if (error_state) + { + eval_error (); + break; + } + + if (a_true) + { + if (etype == tree::or_or) + { + result = 1; + goto done; + } + } + else + { + if (etype == tree::and_and) + { + result = 0; + goto done; + } + } + + if (op2 != NULL_TREE) + { + tree_constant b = op2->eval (0); + if (error_state) + { + eval_error (); + break; + } + + result = b.is_true (); + if (error_state) + { + eval_error (); + break; + } + } + } + done: + ans = tree_constant ((double) result); + } + break; + default: + ::error ("binary operator %d not implemented", etype); + break; + } + + return ans; +} + +void +tree_binary_expression::eval_error (void) +{ + if (error_state > 0) + { + char *op; + switch (etype) + { + case tree::add: op = "+"; break; + case tree::subtract: op = "-"; break; + case tree::multiply: op = "*"; break; + case tree::el_mul: op = ".*"; break; + case tree::divide: op = "/"; break; + case tree::el_div: op = "./"; break; + case tree::leftdiv: op = "\\"; break; + case tree::el_leftdiv: op = ".\\"; break; + case tree::power: op = "^"; break; + case tree::elem_pow: op = ".^"; break; + case tree::cmp_lt: op = "<"; break; + case tree::cmp_le: op = "<="; break; + case tree::cmp_eq: op = "=="; break; + case tree::cmp_ge: op = ">="; break; + case tree::cmp_gt: op = ">"; break; + case tree::cmp_ne: op = "!="; break; + case tree::and_and: op = "&&"; break; + case tree::or_or: op = "||"; break; + case tree::and: op = "&"; break; + case tree::or: op = "|"; break; + default: op = "unknown"; break; + } + + ::error ("evaluating binary operator `%s' near line %d, column %d", + op, line (), column ()); + } +} + +/* + * Assignment expressions. + */ +tree_assignment_expression::tree_assignment_expression (void) +{ + in_parens = 0; + etype = tree::assignment; +} + +tree_assignment_expression::~tree_assignment_expression (void) +{ +} + +tree_constant +tree_assignment_expression::eval (int print) +{ + panic ("invalid evaluation of generic expression"); + return tree_constant (); +} + +int +tree_assignment_expression::is_assignment_expression (void) const +{ + return 1; +} + +/* + * Simple assignment expressions. + */ +tree_simple_assignment_expression::tree_simple_assignment_expression + (int l = -1, int c = -1) +{ + etype = tree::assignment; + lhs = (tree_identifier *) NULL; + index = (tree_argument_list *) NULL; + rhs = (tree_expression *) NULL; + line_num = l; + column_num = c; +} + +tree_simple_assignment_expression::tree_simple_assignment_expression + (tree_identifier *i, tree_expression *r, int l = -1, int c = -1) +{ + etype = tree::assignment; + lhs = i; + index = (tree_argument_list *) NULL; + rhs = r; + line_num = l; + column_num = c; +} + +tree_simple_assignment_expression::tree_simple_assignment_expression + (tree_index_expression *idx_expr, tree_expression *r, int l = -1, int c = -1) +{ + etype = tree::assignment; + lhs = idx_expr->ident (); + index = idx_expr->arg_list (); + rhs = r; + line_num = l; + column_num = c; +} + +tree_simple_assignment_expression::~tree_simple_assignment_expression (void) +{ +// delete lhs; +// delete index; + delete rhs; +} + +tree_constant +tree_simple_assignment_expression::eval (int print) +{ + assert (etype == tree::assignment); + + tree_constant ans; + tree_constant retval; + + if (error_state) + return retval; + + if (rhs != (tree_expression *) NULL) + { + tree_constant rhs_val = rhs->eval (0); + if (error_state) + { + if (error_state) + eval_error (); + } + else if (index == NULL_TREE) + { + ans = lhs->assign (rhs_val); + if (error_state) + eval_error (); + } + else + { +// Extract the arguments into a simple vector. + int nargs = 0; + tree_constant *args = index->convert_to_const_vector (nargs); + + if (error_state) + eval_error (); + else if (nargs > 1) + { + ans = lhs->assign (rhs_val, args, nargs); + if (error_state) + eval_error (); + } + + delete [] args; + } + } + + if (! error_state && ans.is_defined ()) + { + int pad_after = 0; + if (print && user_pref.print_answer_id_name) + { + if (print_as_scalar (ans)) + { + ostrstream output_buf; + output_buf << lhs->name () << " = " << ends; + maybe_page_output (output_buf); + } + else + { + pad_after = 1; + ostrstream output_buf; + output_buf << lhs->name () << " =\n\n" << ends; + maybe_page_output (output_buf); + } + } + + retval = ans.eval (print); + + if (print && pad_after) + { + ostrstream output_buf; + output_buf << "\n" << ends; + maybe_page_output (output_buf); + } + } + + return retval; +} + +void +tree_simple_assignment_expression::eval_error (void) +{ + if (error_state > 0) + { + int l = line (); + int c = column (); + if (l != -1 && c != -1) + ::error ("evaluating assignment expression near line %d, column %d", + l, c); +// else +// error ("evaluating assignment expression"); + } +} + +/* + * Multi-valued assignmnt expressions. + */ +tree_multi_assignment_expression::tree_multi_assignment_expression + (int l = -1, int c = -1) +{ + etype = tree::multi_assignment; + lhs = (tree_return_list *) NULL; + rhs = (tree_expression *) NULL; + line_num = l; + column_num = c; +} + +tree_multi_assignment_expression::tree_multi_assignment_expression + (tree_return_list *lst, tree_expression *r, int l = -1, int c = -1) +{ + etype = tree::multi_assignment; + lhs = lst; + rhs = r; + line_num = l; + column_num = c; +} + +tree_multi_assignment_expression::~tree_multi_assignment_expression (void) +{ + delete lhs; + delete rhs; +} + +tree_constant +tree_multi_assignment_expression::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + tree_constant *result = eval (print, 1); + + if (result != NULL_TREE_CONST) + { + retval = result[0]; + delete [] result; + } + + return retval; +} + +tree_constant * +tree_multi_assignment_expression::eval (int print, int nargout, + const tree_constant *args = + NULL_TREE_CONST, + int nargin = 0) +{ + assert (etype == tree::multi_assignment); + + if (error_state || rhs == (tree_expression *) NULL) + return NULL_TREE_CONST; + + nargout = lhs->length (); + tree_constant *results = rhs->eval (0, nargout); + + if (error_state) + eval_error (); + + int ma_line = line (); + int ma_column = column (); + + if (results != NULL_TREE_CONST) + { + tree_return_list *elem; + int i = 0; + int pad_after = 0; + int last_was_scalar_type = 0; + for (elem = lhs; elem != (tree_return_list *) NULL; + elem = elem->next_elem ()) + { + tree_index_expression *lhs_expr = elem->idx_expr (); + if (i < nargout) + { + if (results[i].is_undefined ()) + { + tree_simple_assignment_expression tmp_expr + (lhs_expr, NULL_TREE_CONST, ma_line, ma_column); + + results[i] = tmp_expr.eval (0); // Should stay undefined! + + if (error_state) + break; + + if (last_was_scalar_type && i == 1) + pad_after = 0; + + break; + } + else + { + tree_constant *tmp = new tree_constant (results[i]); + + tree_simple_assignment_expression tmp_expr + (lhs_expr, tmp, ma_line, ma_column); + + results[i] = tmp_expr.eval (0); // May change + + if (error_state) + break; + + if (print && pad_after) + { + ostrstream output_buf; + output_buf << "\n" << '\0'; + maybe_page_output (output_buf); + } + + if (print && user_pref.print_answer_id_name) + { + tree_identifier *tmp_id = lhs_expr->ident (); + char *tmp_nm = tmp_id->name (); + + if (print_as_scalar (results[i])) + { + ostrstream output_buf; + output_buf << tmp_nm << " = " << '\0'; + maybe_page_output (output_buf); + last_was_scalar_type = 1; + } + else + { + ostrstream output_buf; + output_buf << tmp_nm << " =\n\n" << '\0'; + maybe_page_output (output_buf); + last_was_scalar_type = 0; + } + } + results[i].eval (print); + pad_after++; + } + i++; + } + else + { + tree_simple_assignment_expression tmp_expr + (lhs_expr, NULL_TREE_CONST, ma_line, ma_column); + + tmp_expr.eval (0); + + if (error_state) + break; + + if (last_was_scalar_type && i == 1) + pad_after = 0; + + break; + } + } + + if (print && pad_after) + { + ostrstream output_buf; + output_buf << "\n" << '\0'; + maybe_page_output (output_buf); + } + } + + return results; +} + +void +tree_multi_assignment_expression::eval_error (void) +{ + if (error_state > 0) + ::error ("evaluating assignment expression near line %d, column %d", + line (), column ()); +} + +/* + * Colon expressions. + */ +tree_colon_expression::tree_colon_expression (int l = -1, int c = -1) +{ + etype = tree::colon; + op1 = (tree_expression *) NULL; + op2 = (tree_expression *) NULL; + op3 = (tree_expression *) NULL; + line_num = l; + column_num = c; +} + +tree_colon_expression::tree_colon_expression (tree_expression *a, tree_expression *b, + int l = -1, int c = -1) +{ + etype = tree::colon; + op1 = a; // base + op2 = b; // limit + op3 = (tree_expression *) NULL; // increment if not empty. + line_num = l; + column_num = c; +} + +tree_colon_expression::~tree_colon_expression (void) +{ + delete op1; + delete op2; + delete op3; +} + +tree_colon_expression * +tree_colon_expression::chain (tree_expression *t) +{ + tree_colon_expression *retval = (tree_colon_expression *) NULL; + if (op1 == NULL_TREE || op3 != NULL_TREE) + ::error ("invalid colon expression"); + else + { + op3 = op2; // Stupid syntax. + op2 = t; + + retval = this; + } + return retval; +} + +tree_constant +tree_colon_expression::eval (int print) +{ + tree_constant retval; + + if (error_state || op1 == NULL_TREE || op2 == NULL_TREE) + return retval; + + tree_constant tmp; + + tmp = op1->eval (0); + + if (tmp.is_undefined ()) + { + eval_error ("invalid null value in colon expression"); + return retval; + } + + tmp = tmp.make_numeric (); + if (tmp.const_type () != tree_constant_rep::scalar_constant + && tmp.const_type () != tree_constant_rep::complex_scalar_constant) + { + eval_error ("base for colon expression must be a scalar"); + return retval; + } + double base = tmp.double_value (); + + tmp = op2->eval (0); + + if (tmp.is_undefined ()) + { + eval_error ("invalid null value in colon expression"); + return retval; + } + + tmp = tmp.make_numeric (); + if (tmp.const_type () != tree_constant_rep::scalar_constant + && tmp.const_type () != tree_constant_rep::complex_scalar_constant) + { + eval_error ("limit for colon expression must be a scalar"); + return retval; + } + double limit = tmp.double_value (); + + double inc = 1.0; + if (op3 != NULL_TREE) + { + tmp = op3->eval (0); + + if (tmp.is_undefined ()) + { + eval_error ("invalid null value in colon expression"); + return retval; + } + + tmp = tmp.make_numeric (); + if (tmp.const_type () != tree_constant_rep::scalar_constant + && tmp.const_type () != tree_constant_rep::complex_scalar_constant) + { + eval_error ("increment for colon expression must be a scalar"); + return retval; + } + else + inc = tmp.double_value (); + } + + retval = tree_constant (base, limit, inc); + + if (error_state) + { + if (error_state) + eval_error ("evaluating colon expression"); + return tree_constant (); + } + + return retval; +} + +void +tree_colon_expression::eval_error (const char *s) +{ + if (error_state > 0) + ::error ("%s near line %d column %d", s, line (), column ()); +} + +/* + * Index expressions. + */ +tree_index_expression::tree_index_expression (int l = -1, int c = -1) +{ + id = (tree_identifier *) NULL; + list = (tree_argument_list *) NULL; + line_num = l; + column_num = c; +} + +tree_index_expression::tree_index_expression (tree_identifier *i, + tree_argument_list *lst, + int l = -1, int c = -1) +{ + id = i; + list = lst; + line_num = l; + column_num = c; +} + +tree_index_expression::tree_index_expression (tree_identifier *i, + int l = -1, int c = -1) +{ + id = i; + list = (tree_argument_list *) NULL; + line_num = l; + column_num = c; +} + +tree_index_expression::~tree_index_expression (void) +{ + delete id; + delete list; +} + +int +tree_index_expression::is_index_expression (void) const +{ + return 1; +} + +tree_identifier * +tree_index_expression::ident (void) +{ + return id; +} + +tree_argument_list * +tree_index_expression::arg_list (void) +{ + return list; +} + +void +tree_index_expression::mark_for_possible_ans_assign (void) +{ + id->mark_for_possible_ans_assign (); +} + + +tree_constant +tree_index_expression::eval (int print) +{ + tree_constant retval; + + if (error_state) + return retval; + + if (list == (tree_argument_list *) NULL) + { + retval = id->eval (print); + if (error_state) + eval_error (); + } + else + { +// Extract the arguments into a simple vector. + int nargin = 0; + tree_constant *args = list->convert_to_const_vector (nargin); +// Don't pass null arguments. + if (error_state) + eval_error (); + else if (nargin > 1 && all_args_defined (args, nargin)) + { + tree_constant *tmp = id->eval (print, 1, args, nargin); + + if (error_state) + eval_error (); + + if (tmp != NULL_TREE_CONST) + retval = tmp[0]; + + delete [] tmp; + } + delete [] args; + } + return retval; +} + +tree_constant * +tree_index_expression::eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0) +{ + tree_constant *retval = NULL_TREE_CONST; + + if (error_state) + return retval; + + if (list == (tree_argument_list *) NULL) + { + retval = id->eval (print, nargout); + if (error_state) + eval_error (); + } + else + { +// Extract the arguments into a simple vector. + int nargin = 0; + tree_constant *args = list->convert_to_const_vector (nargin); +// Don't pass null arguments. + if (error_state) + eval_error (); + else if (nargin > 1 && all_args_defined (args, nargin)) + { + retval = id->eval (print, nargout, args, nargin); + if (error_state) + eval_error (); + } + + delete [] args; + } + return retval; +} + +void +tree_index_expression::eval_error (void) +{ + if (error_state > 0) + { + int l = line (); + int c = column (); + char *fmt; + if (l != -1 && c != -1) + { + if (list != (tree_argument_list *) NULL) + fmt = "evaluating index expression near line %d, column %d"; + else + fmt = "evaluating expression near line %d, column %d"; + + ::error (fmt, l, c); + } + else + { + if (list != (tree_argument_list *) NULL) + ::error ("evaluating index expression"); + else + ::error ("evaluating expression"); + } + } +} + +/* + * Argument lists. + */ +tree_argument_list::tree_argument_list (void) +{ + arg = NULL_TREE; + next = (tree_argument_list *) NULL; +} + +tree_argument_list::tree_argument_list (tree *t) +{ + arg = t; + next = (tree_argument_list *) NULL; +} + +tree_argument_list::~tree_argument_list (void) +{ + delete arg; + delete next; +} + +tree_argument_list * +tree_argument_list::chain (tree *t) +{ + tree_argument_list *tmp = new tree_argument_list (t); + tmp->next = this; + return tmp; +} + +tree_argument_list * +tree_argument_list::reverse (void) +{ + tree_argument_list *list = this; + tree_argument_list *next; + tree_argument_list *prev = (tree_argument_list *) NULL; + + while (list != (tree_argument_list *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +int +tree_argument_list::length (void) +{ + tree_argument_list *list = this; + int len = 0; + while (list != (tree_argument_list *) NULL) + { + len++; + list = list->next; + } + return len; +} + +tree_argument_list * +tree_argument_list::next_elem (void) +{ + return next; +} + +/* + * Convert a linked list of trees to a vector of pointers to trees, + * evaluating them along the way. + */ +tree_constant * +tree_argument_list::convert_to_const_vector (int& len) +{ + len = length () + 1; + + tree_constant *args = new tree_constant [len]; + +// args[0] may eventually hold something useful, like the function +// name. + tree_argument_list *tmp_list = this; + for (int k = 1; k < len; k++) + { + if (tmp_list != (tree_argument_list *) NULL) + { + args[k] = tmp_list->eval (0); + if (error_state) + { + ::error ("evaluating argument list element number %d", k); + break; + } + tmp_list = tmp_list->next; + } + else + { + args[k] = tree_constant (); + break; + } + } + return args; +} + +tree_constant +tree_argument_list::eval (int print) +{ + if (error_state || arg == NULL_TREE) + return tree_constant (); + else + return arg->eval (print); +} + +/* + * Parameter lists. + */ +tree_parameter_list::tree_parameter_list (void) +{ + marked_for_varargs = 0; + param = (tree_identifier *) NULL; + next = (tree_parameter_list *) NULL; +} + +tree_parameter_list::tree_parameter_list (tree_identifier *t) +{ + marked_for_varargs = 0; + param = t; + next = (tree_parameter_list *) NULL; +} + +tree_parameter_list::~tree_parameter_list (void) +{ + delete param; + delete next; +} + +tree_parameter_list * +tree_parameter_list::chain (tree_identifier *t) +{ + tree_parameter_list *tmp = new tree_parameter_list (t); + tmp->next = this; + return tmp; +} + +tree_parameter_list * +tree_parameter_list::reverse (void) +{ + tree_parameter_list *list = this; + tree_parameter_list *next; + tree_parameter_list *prev = (tree_parameter_list *) NULL; + + while (list != (tree_parameter_list *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +int +tree_parameter_list::length (void) +{ + tree_parameter_list *list = this; + int len = 0; + while (list != (tree_parameter_list *) NULL) + { + len++; + list = list->next; + } + return len; +} + +char * +tree_parameter_list::name (void) const +{ + return param->name (); +} + +void +tree_parameter_list::mark_as_formal_parameters (void) +{ + param->mark_as_formal_parameter (); + if (next != (tree_parameter_list *) NULL) + next->mark_as_formal_parameters (); +} + +void +tree_parameter_list::mark_varargs (void) +{ + marked_for_varargs = 1; +} + +int +tree_parameter_list::takes_varargs (void) const +{ + return marked_for_varargs; +} + +tree_identifier * +tree_parameter_list::define (tree_constant *t) +{ + return param->define (t); +} + +void +tree_parameter_list::define_from_arg_vector (const tree_constant *args, + int nargin) +{ + if (args == NULL_TREE_CONST) + return; + + int expected_nargin = length () + 1; + + tree_parameter_list *ptr = this; + + for (int i = 1; i < expected_nargin; i++) + { + tree_constant *tmp = NULL_TREE_CONST; + + if (i < nargin) + { + if (args[i].is_defined () + && (args[i].const_type () == tree_constant_rep::magic_colon)) + { + ::error ("invalid use of colon in function argument list"); + return; + } + tmp = new tree_constant (args[i]); + } + + ptr->define (tmp); + ptr = ptr->next; + } +} + +// XXX FIXME XXX -- need a way to prevent this from setting +// error_state and printing an error message if the elements are not +// defined. +tree_constant * +tree_parameter_list::convert_to_const_vector (void) +{ + int nout = length (); + + tree_constant *retval = new tree_constant [nout+1]; + + int i = 0; + + tree_parameter_list *elem = this; + + for ( ; elem != (tree_parameter_list *) NULL; elem = elem->next) + retval[i++] = elem->eval (0); + + retval [nout] = tree_constant (); + + return retval; +} + +tree_parameter_list * +tree_parameter_list::next_elem (void) +{ + return next; +} + +tree_constant +tree_parameter_list::eval (int print) +{ + if (error_state || param == NULL_TREE) + return tree_constant (); + else + return param->eval (print); +} + +/* + * Return lists. + */ +tree_return_list::tree_return_list (void) +{ + retval = (tree_index_expression *) NULL; + next = (tree_return_list *) NULL; +} + +tree_return_list::tree_return_list (tree_identifier *t) +{ + retval = new tree_index_expression (t); + next = (tree_return_list *) NULL; +} + +tree_return_list::tree_return_list (tree_index_expression *t) +{ + retval = t; + next = (tree_return_list *) NULL; +} + +tree_return_list::~tree_return_list (void) +{ + delete retval; + delete next; +} + +tree_return_list * +tree_return_list::chain (tree_identifier *t) +{ + tree_return_list *tmp = new tree_return_list (t); + tmp->next = this; + return tmp; +} + +tree_return_list * +tree_return_list::chain (tree_index_expression *t) +{ + tree_return_list *tmp = new tree_return_list (t); + tmp->next = this; + return tmp; +} + +tree_return_list * +tree_return_list::reverse (void) +{ + tree_return_list *list = this; + tree_return_list *next; + tree_return_list *prev = (tree_return_list *) NULL; + + while (list != (tree_return_list *) NULL) + { + next = list->next; + list->next = prev; + prev = list; + list = next; + } + return prev; +} + +int +tree_return_list::length (void) +{ + tree_return_list *list = this; + int len = 0; + while (list != (tree_return_list *) NULL) + { + len++; + list = list->next; + } + return len; +} + +tree_index_expression * +tree_return_list::idx_expr (void) +{ + return retval; +} + +tree_return_list * +tree_return_list::next_elem (void) +{ + return next; +} + +tree_constant +tree_return_list::eval (int print) +{ + panic ("invalid evaluation of return list"); + return tree_constant (); +} + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; page-delimiter: "^/\\*" *** +;;; End: *** +*/ diff --git a/src/pt-exp-base.h b/src/pt-exp-base.h new file mode 100644 --- /dev/null +++ b/src/pt-exp-base.h @@ -0,0 +1,652 @@ +// Tree classes. -*- C++ -*- +/* + +Copyright (C) 1992, 1993, 1994 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, 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#if !defined (octave_tree_expr_h) +#define octave_tree_expr_h 1 + +#if defined (__GNUG__) +#pragma interface +#endif + +#include + +#include "builtins.h" +#include "error.h" + +class tree_constant; +class symbol_record; +class symbol_table; + +#ifndef TREE_FCN_TYPEDEFS +#define TREE_FCN_TYPEDEFS 1 + +typedef tree_constant* (*Text_fcn)(int, char **, int); +typedef tree_constant* (*General_fcn)(const tree_constant *, int, int); + +#endif + +#ifndef NULL_TREE +#define NULL_TREE (tree *) NULL +#endif + +#ifndef NULL_TREE_CONST +#define NULL_TREE_CONST (tree_constant *) NULL +#endif + +class tree_matrix; +class tree_builtin; +class tree_identifier; +class tree_function; +class tree_expression; +class tree_prefix_expression; +class tree_postfix_expression; +class tree_unary_expression; +class tree_binary_expression; +class tree_assignment_expression; +class tree_simple_assignment_expression; +class tree_multi_assignment_expression; +class tree_colon_expression; +class tree_index_expression; +class tree_argument_list; +class tree_parameter_list; +class tree_return_list; + +/* + * A base class for expressions. + */ +class +tree_expression : public tree +{ +public: + tree_expression (void); + + ~tree_expression (void); + + tree_constant eval (int print); + + virtual int is_identifier (void) const + { return 0; } + + virtual int is_index_expression (void) const + { return 0; } + + virtual int is_assignment_expression (void) const + { return 0; } + + virtual int is_prefix_expression (void) const + { return 0; } + + virtual void mark_for_possible_ans_assign (void) + { panic_impossible (); } + + virtual tree_constant *eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0) + { panic_impossible (); return NULL_TREE_CONST; } + +protected: + expression_type etype; +}; + +/* + * General matrices. This allows us to construct matrices from + * other matrices, variables, and functions. + */ +class +tree_matrix : public tree_expression +{ +public: + tree_matrix (void); + tree_matrix (tree_expression *e, tree::matrix_dir d); + + ~tree_matrix (void); + + tree_matrix *chain (tree_expression *e, tree::matrix_dir d); + tree_matrix *reverse (void); + int length (void); + + tree_return_list *to_return_list (void); + + tree_constant eval (int print); + +private: + tree::matrix_dir dir; // Direction to the next element. + tree_expression *element; + tree_matrix *next; +}; + +/* + * Prefix expressions. + */ +class +tree_prefix_expression : public tree_expression +{ + public: + tree_prefix_expression (int l = -1, int c = -1); + tree_prefix_expression (tree_identifier *t, tree::expression_type et, + int l = -1, int c = -1); + + ~tree_prefix_expression (void); + + tree_constant eval (int print); + + void eval_error (void); + + int is_prefix_expression (void) const; + + private: + tree_identifier *id; +}; + +/* + * Postfix expressions. + */ +class +tree_postfix_expression : public tree_expression +{ + public: + tree_postfix_expression (int l = -1, int c = -1); + tree_postfix_expression (tree_identifier *t, tree::expression_type et, + int l = -1, int c = -1); + + ~tree_postfix_expression (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_identifier *id; +}; + +/* + * Unary expressions. + */ +class +tree_unary_expression : public tree_expression +{ + public: + tree_unary_expression (int l = -1, int c = -1); + tree_unary_expression (tree_expression *a, tree::expression_type t, + int l = -1, int c = -1); + + ~tree_unary_expression (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_expression *op; +}; + +/* + * Binary expressions. + */ +class +tree_binary_expression : public tree_expression +{ + public: + tree_binary_expression (int l = -1, int c = -1); + tree_binary_expression (tree_expression *a, tree_expression *b, + tree::expression_type t, int l = -1, int c = -1); + + ~tree_binary_expression (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_expression *op1; + tree_expression *op2; +}; + +/* + * Assignment expressions. + */ +class +tree_assignment_expression : public tree_expression +{ +public: + int in_parens; + + tree_assignment_expression (void); + + ~tree_assignment_expression (void); + + tree_constant eval (int print); + + int is_assignment_expression (void) const; +}; + +/* + * Simple assignment expressions. + */ +class +tree_simple_assignment_expression : public tree_assignment_expression +{ + public: + tree_simple_assignment_expression (int l = -1, int c = -1); + tree_simple_assignment_expression (tree_identifier *i, + tree_expression *r, + int l = -1, int c = -1); + tree_simple_assignment_expression (tree_index_expression *idx_expr, + tree_expression *r, int l = -1, int c = -1); + + ~tree_simple_assignment_expression (void); + + tree_constant eval (int print); + + void eval_error (void); + + private: + tree_identifier *lhs; + tree_argument_list *index; + tree_expression *rhs; +}; + +/* + * Multi-valued assignment expressions. + */ +class +tree_multi_assignment_expression : public tree_assignment_expression +{ + public: + tree_multi_assignment_expression (int l = -1, int c = -1); + tree_multi_assignment_expression (tree_return_list *lst, + tree_expression *r, + int l = -1, int c = -1); + + ~tree_multi_assignment_expression (void); + + tree_constant eval (int print); + + tree_constant *eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0); + + void eval_error (void); + + private: + tree_return_list *lhs; + tree_expression *rhs; +}; + +/* + * Colon expressions. + */ +class +tree_colon_expression : public tree_expression +{ + public: + tree_colon_expression (int l = -1, int c = -1); + tree_colon_expression (tree_expression *a, tree_expression *b, + int l = -1, int c = -1); + + ~tree_colon_expression (void); + + tree_colon_expression *chain (tree_expression *t); + + tree_constant eval (int print); + + void eval_error (const char *s); + + private: + tree_expression *op1; + tree_expression *op2; + tree_expression *op3; +}; + +/* + * Index expressions. + */ +class +tree_index_expression : public tree_expression +{ + public: + tree_index_expression (int l = -1, int c = -1); + tree_index_expression (tree_identifier *i, int l = -1, int c = -1); + tree_index_expression (tree_identifier *i, tree_argument_list *lst, + int l = -1, int c = -1); + + ~tree_index_expression (void); + + int is_index_expression (void) const; + + tree_identifier *ident (void); + + tree_argument_list *arg_list (void); + + void mark_for_possible_ans_assign (void); + + tree_constant eval (int print); + + tree_constant *eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0); + + void eval_error (void); + + private: + tree_identifier *id; + tree_argument_list *list; +}; + +/* + * A base class for objects that can be evaluated with argument lists. + */ +class +tree_fvc : public tree_expression +{ +public: + virtual int is_constant (void) const + { return 0; } + +// virtual int is_builtin (void) const +// { return 0; } + + virtual tree_constant assign (tree_constant& t, tree_constant *args, + int nargs); + + virtual char *name (void) const + { panic_impossible (); return (char *) NULL; } + + virtual void bump_value (tree::expression_type) + { panic_impossible (); } + + virtual int max_expected_args (void) + { panic_impossible (); return 0; } + + virtual char *fcn_file_name (void) + { return (char *) NULL; } + + virtual time_t time_parsed (void) + { panic_impossible (); return 0; } + + virtual int is_system_fcn_file (void) const + { return 0; } + + virtual int save (ostream& os, int mark_as_global = 0, + int precision = 17) + { panic_impossible (); return 0; } +}; + +/* + * Builtin functions. + */ +class +tree_builtin : public tree_fvc +{ +public: + tree_builtin (const char *nm = (char *) NULL); + + tree_builtin (int i_max, int o_max, Mapper_fcn& m_fcn, + const char *nm = (char *) NULL); + + tree_builtin (int i_max, int o_max, Text_fcn t_fcn, + const char *nm = (char *) NULL); + + tree_builtin (int i_max, int o_max, General_fcn t_fcn, + const char *nm = (char *) NULL); + + ~tree_builtin (void); + +// int is_builtin (void) const; + + tree_constant eval (int print); + + tree_constant *eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0); + + char *name (void) const; + + int max_expected_args (void); + +private: + int nargin_max; + int nargout_max; + Mapper_fcn mapper_fcn; + Text_fcn text_fcn; + General_fcn general_fcn; + char *my_name; +}; + +/* + * Symbols from the symbol table. + */ +class +tree_identifier : public tree_fvc +{ + friend class tree_index_expression; + +public: + tree_identifier (int l = -1, int c = -1); + tree_identifier (symbol_record *s, int l = -1, int c = -1); + + ~tree_identifier (void); + + int is_identifier (void) const; + + char *name (void) const; + void rename (const char *n); + + tree_identifier *define (tree_constant *t); + tree_identifier *define (tree_function *t); + + void document (char *s); + + tree_constant assign (tree_constant& t); + tree_constant assign (tree_constant& t, tree_constant *args, int nargs); + + void bump_value (tree::expression_type); + + int parse_fcn_file (int exec_script = 1); + int parse_fcn_file (char *ff, int exec_script = 1); + void parse_fcn_file (FILE *ffile, char *ff); + + tree_fvc *do_lookup (int& script_file_executed); + + void mark_as_formal_parameter (void); + + void mark_for_possible_ans_assign (void); + + tree_constant eval (int print); + + tree_constant *eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0); + + void eval_undefined_error (void); + +private: + symbol_record *sym; + int maybe_do_ans_assign; +}; + +/* + * User defined functions. + */ +class +tree_function : public tree_fvc +{ +public: + tree_function (void); + tree_function (tree *cl, symbol_table *st); + + ~tree_function (void); + + tree_function *define (tree *t); + tree_function *define_param_list (tree_parameter_list *t); + tree_function *define_ret_list (tree_parameter_list *t); + + void stash_fcn_file_name (char * s); + void stash_fcn_file_time (time_t t); + + char *fcn_file_name (void); + time_t time_parsed (void); + + void mark_as_system_fcn_file (void); + int is_system_fcn_file (void) const; + + int takes_varargs (void) const; + void octave_va_start (void); + tree_constant octave_va_arg (void); + + void stash_function_name (char *s); + char *function_name (void); + + tree_constant eval (int print); + + tree_constant *eval (int print, int nargout, + const tree_constant *args = NULL_TREE_CONST, + int nargin = 0); + + int max_expected_args (void); + + void traceback_error (void); + +private: + int call_depth; + tree_parameter_list *param_list; + tree_parameter_list *ret_list; + symbol_table *sym_tab; + tree *cmd_list; + char *file_name; + char *fcn_name; + time_t t_parsed; + int system_fcn_file; + int varargs_ok; + int num_named_args; + const tree_constant *args_passed; + int num_args_passed; + int curr_arg_number; +}; + +/* + * Argument lists. + */ +class +tree_argument_list : public tree +{ + public: + tree_argument_list (void); + tree_argument_list (tree *t); + + ~tree_argument_list (void); + + tree_argument_list *chain (tree *t); + tree_argument_list *reverse (void); + int length (void); + + tree_argument_list *next_elem (void); + + tree_constant *convert_to_const_vector (int& nargs); + + tree_constant eval (int print); + + private: + tree *arg; + tree_argument_list *next; +}; + +/* + * Parameter lists. Almost like argument lists, except that the + * elements are only supposed to be identifiers, never constants or + * expressions. + */ +class +tree_parameter_list : public tree +{ + public: + tree_parameter_list (void); + tree_parameter_list (tree_identifier *t); + + ~tree_parameter_list (void); + + tree_parameter_list *chain (tree_identifier *t); + tree_parameter_list *reverse (void); + int length (void); + + char *name (void) const; + + void mark_as_formal_parameters (void); + + void mark_varargs (void); + int takes_varargs (void) const; + + tree_identifier *define (tree_constant *t); + + void define_from_arg_vector (const tree_constant *args, int nargin); + + tree_constant *convert_to_const_vector (void); + + tree_parameter_list *next_elem (void); + + tree_constant eval (int print); + + private: + int marked_for_varargs; + tree_identifier *param; + tree_parameter_list *next; +}; + +/* + * Return lists. Almost like parameter lists, except that the + * elements may also be index expressions. + */ +class +tree_return_list : public tree +{ + public: + tree_return_list (void); + tree_return_list (tree_identifier *t); + tree_return_list (tree_index_expression *t); + + ~tree_return_list (void); + + tree_return_list *chain (tree_identifier *t); + tree_return_list *chain (tree_index_expression *t); + tree_return_list *reverse (void); + int length (void); + + tree_index_expression *idx_expr (void); + + tree_return_list *next_elem (void); + + tree_constant eval (int print); + + private: + tree_index_expression *retval; + tree_return_list *next; +}; + +#endif + +/* +;;; Local Variables: *** +;;; mode: C++ *** +;;; page-delimiter: "^/\\*" *** +;;; End: *** +*/