view src/pt-exp-base.cc @ 508:ef71e51a2280

[project @ 1994-07-10 02:06:07 by jwe]
author jwe
date Sun, 10 Jul 1994 02:07:03 +0000
parents 0f388340e607
children 7ea224e713cd
line wrap: on
line source

// 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 <sys/types.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <iostream.h>
#include <strstream.h>
#include <limits.h>
#include <ctype.h>
#include <stdio.h>

#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 <readline/readline.h>
}

// 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 (const Octave_object& args)
{
  int nargin = args.length ();

  while (--nargin > 0)
    {
      if (args(nargin).is_undefined ())
	return 0;
    }
  return 1;
}

/*
 * Are any of the arguments `:'?
 */
static int
any_arg_is_magic_colon (const Octave_object& args)
{
  int nargin = args.length ();

  while (--nargin > 0)
    {
      if (args(nargin).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, const Octave_object& args)
{
  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);
      Octave_object tmp = (*text_fcn) (1, argv, 1);
      if (tmp.length () > 0)
	retval = tmp(0);
      delete [] argv;
    }
  else if (general_fcn != (General_fcn) NULL)
    {
      Octave_object args (1);
      args(0) = tree_constant (my_name);
      Octave_object tmp = (*general_fcn) (args, 1);
      if (tmp.length () > 0)
	retval = tmp(0);
    }
  else // Assume mapper function
    ::error ("%s: argument expected", my_name);

  return retval;
}

Octave_object
tree_builtin::eval (int print, int nargout, const Octave_object& args)
{
  Octave_object retval;

  if (error_state)
    return retval;

  int nargin = args.length ();

  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))
	::error ("invalid use of colon in function argument list");
      else
	retval = (*general_fcn) (args, nargout);
    }
  else
    {
      if (nargin > nargin_max)
	::error ("%s: too many arguments", my_name);
      else if (nargin > 0 && args.length () > 0 && args(1).is_defined ())
	{
	  tree_constant tmp = args(1).mapper (mapper_fcn, 0);
	  retval.resize (1);
	  retval(0) = tmp;
	}	
    }

  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, const Octave_object& args)
{
  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);
	}
      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);
	  if (retval.is_defined ())
	    sym->define (tmp);
	}
    }

  return retval;
}

int
tree_identifier::is_defined (void)
{
  return (sym != (symbol_record *) NULL && sym->is_defined ());
}

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;

	  int nargin = (ans->is_constant ()) ? 0 : 1;
	  Octave_object tmp_args (nargin);
	  Octave_object tmp = ans->eval (0, nargout, tmp_args);

	  if (tmp.length () > 0)
	    retval = tmp(0);
	}
    }

  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;
}

Octave_object
tree_identifier::eval (int print, int nargout, const Octave_object& args)
{
  Octave_object 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
	{
	  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);

	      if (retval.length () > 0 && 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);
	}
    }

  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;
  num_named_args = 0;
  num_args_passed = 0;
  curr_va_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;
  num_named_args = 0;
  num_args_passed = 0;
  curr_va_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;

  if (param_list != (tree_parameter_list *) NULL)
    {
      int len = param_list->length ();
      int va_only = param_list->varargs_only ();
      num_named_args = va_only ? len - 1 : len;
      curr_va_arg_number = num_named_args;
    }

  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 (param_list != (tree_parameter_list *) NULL
	  && param_list->takes_varargs ());
}

void
tree_function::octave_va_start (void)
{
  curr_va_arg_number = num_named_args;
}

tree_constant
tree_function::octave_va_arg (void)
{
  tree_constant retval;

  if (curr_va_arg_number < num_args_passed)
    retval = args_passed (++curr_va_arg_number);
  else
    ::error ("error getting arg number %d -- only %d provided",
	     curr_va_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;

  Octave_object tmp_args (1);
  Octave_object tmp = eval (print, 1, tmp_args);

  if (! error_state && tmp.length () > 0)
    retval = tmp(0);

  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 ();
}

Octave_object
tree_function::eval (int print, int nargout, const Octave_object& args)
{
  Octave_object retval;

  if (error_state)
    return retval;

  if (cmd_list == NULL_TREE)
    return retval;

  int nargin = args.length ();

  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_va_arg_number);

  if (param_list != (tree_parameter_list *) NULL
      && ! param_list->varargs_only ())
    {
      param_list->define_from_arg_vector (args);
      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.resize (1);
	retval(0) = last_computed_value;
      }
  }

 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 ();
}

Octave_object
tree_expression::eval (int print, int nargout, const Octave_object& args)
{
  panic_impossible ();
  return Octave_object ();
}

/*
 * 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.
	  Octave_object args = index->convert_to_const_vector ();

	  int nargin = args.length ();

	  if (error_state)
	    eval_error ();
	  else if (nargin > 1)
	    {
	      ans = lhs->assign (rhs_val, args);
	      if (error_state)
		eval_error ();
	    }
	}
    }

  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;

  Octave_object tmp_args;
  Octave_object result = eval (print, 1, tmp_args);

  if (result.length () > 0)
    retval = result(0);

  return retval;
}

Octave_object
tree_multi_assignment_expression::eval (int print, int nargout,
					const Octave_object& args)
{
  assert (etype == tree::multi_assignment);

  if (error_state || rhs == (tree_expression *) NULL)
    return Octave_object ();

  nargout = lhs->length ();
  Octave_object tmp_args;
  Octave_object results = rhs->eval (0, nargout, tmp_args);

  if (error_state)
    eval_error ();

  int ma_line = line ();
  int ma_column = column ();

  if (results.length () > 0)
    {
      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)
	    {
// XXX FIXME? XXX -- this is apparently the way Matlab works, but
// maybe we should have the option of skipping the assignment instead.

	      tree_constant *tmp = NULL_TREE_CONST;
	      if (results(i).is_undefined ())
		{
		  Matrix m;
		  tmp = new tree_constant (m);
		}
	      else
		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.
      Octave_object args = list->convert_to_const_vector ();
// Don't pass null arguments.
      int nargin = args.length ();
      if (error_state)
	eval_error ();
      else if (nargin > 1 && all_args_defined (args))
	{
	  Octave_object tmp = id->eval (print, 1, args);

	  if (error_state)
	    eval_error ();

	  if (tmp.length () > 0)
	    retval = tmp(0);
	}
    }
  return retval;
}

Octave_object
tree_index_expression::eval (int print, int nargout, const Octave_object& args)
{
  Octave_object retval;

  if (error_state)
    return retval;

  if (list == (tree_argument_list *) NULL)
    {
      Octave_object tmp_args;
      retval = id->eval (print, nargout, tmp_args);
      if (error_state)
	eval_error ();
    }
  else
    {
// Extract the arguments into a simple vector.
      Octave_object args = list->convert_to_const_vector ();
// Don't pass null arguments.
      if (error_state)
	eval_error ();
      else if (args.length () > 1 && all_args_defined (args))
	{
	  retval = id->eval (print, nargout, args);
	  if (error_state)
	    eval_error ();
	}
    }
  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.
 */
Octave_object
tree_argument_list::convert_to_const_vector (void)
{
  int len = length () + 1;

  Octave_object args (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;
}

void
tree_parameter_list::mark_varargs_only (void)
{
  marked_for_varargs = -1;
}

int
tree_parameter_list::varargs_only (void)
{
  return (marked_for_varargs < 0);
}

tree_identifier *
tree_parameter_list::define (tree_constant *t)
{
  return param->define (t);
}

void
tree_parameter_list::define_from_arg_vector (const Octave_object& args)
{
  if (args.length () <= 0)
    return;

  int nargin = args.length ();

  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;
    }
}

Octave_object
tree_parameter_list::convert_to_const_vector (void)
{
  int nout = length ();

  Octave_object retval (nout);

  int i = 0;

  tree_parameter_list *elem = this;

  for ( ; elem != (tree_parameter_list *) NULL;	elem = elem->next)
    {
      if (elem->is_defined ())
	retval(i) = elem->eval (0);
      i++;
    }

  return retval;
}

int
tree_parameter_list::is_defined (void)
{
  return (param != (tree_identifier *) NULL && param->is_defined ());
}

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: ***
*/