/*

Copyright (C) 1993-2016 John W. Eaton
Copyright (C) 2009 David Grundberg
Copyright (C) 2009-2010 VZLU Prague
Copyright (C) 2016 Oliver Heimlich

This file is part of Octave.

Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 3 of the License, or (at your
option) any later version.

Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
for more details.

You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING.  If not, see
<http://www.gnu.org/licenses/>.

*/

// Parser for Octave.

// C decarations.

%{

#define YYDEBUG 1

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include <cassert>
#include <cstdio>
#include <cstdlib>

#include <iostream>
#include <map>
#include <sstream>

#include "Matrix.h"
#include "cmd-edit.h"
#include "cmd-hist.h"
#include "file-ops.h"
#include "file-stat.h"
#include "oct-env.h"
#include "oct-time.h"
#include "quit.h"

#include "Cell.h"
#include "call-stack.h"
#include "comment-list.h"
#include "defaults.h"
#include "defun.h"
#include "dirfns.h"
#include "dynamic-ld.h"
#include "error.h"
#include "input.h"
#include "interpreter.h"
#include "lex.h"
#include "load-path.h"
#include "oct-hist.h"
#include "oct-map.h"
#include "ov-classdef.h"
#include "ov-fcn-handle.h"
#include "ov-usr-fcn.h"
#include "ov-null-mat.h"
#include "pager.h"
#include "parse.h"
#include "pt-all.h"
#include "pt-eval.h"
#include "pt-funcall.h"
#include "symtab.h"
#include "token.h"
#include "unwind-prot.h"
#include "utils.h"
#include "variables.h"

// oct-parse.h must be included after pt-all.h
#include <oct-parse.h>

extern int octave_lex (YYSTYPE *, void *);

// Global access to currently active lexer.
// FIXME: to be removed after more parser+lexer refactoring.
octave::base_lexer *LEXER = 0;

// TRUE means we printed messages about reading startup files.
bool reading_startup_message_printed = false;

// List of autoloads (function -> file mapping).
static std::map<std::string, std::string> autoload_map;

// Forward declarations for some functions defined at the bottom of
// the file.

static void yyerror (octave::base_parser& parser, const char *s);

#define lexer parser.lexer
#define scanner lexer.scanner

#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
// Disable this warning for code that is generated by Bison, including
// grammar rules.  Push the current state so we can restore the warning
// state prior to functions we define at the bottom of the file.
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wold-style-cast"
#endif

%}

// Bison declarations.

// The grammar currently has 14 shift/reduce conflicts.  Ensure that
// we notice if that number changes.

%expect 14

%define api.prefix {octave_}

// We are using the pure parser interface and the reentrant lexer
// interface but the Octave parser and lexer are NOT properly
// reentrant because both still use many global variables.  It should be
// safe to create a parser object and call it while anotehr parser
// object is active (to parse a callback function while the main
// interactive parser is waiting for input, for example) if you take
// care to properly save and restore (typically with an unwind_protect
// object) relevant global values before and after the nested call.

%define api.pure
%define api.push-pull both
%parse-param { octave::base_parser& parser }
%lex-param { void *scanner }

%union
{
  int dummy_type;

  // The type of the basic tokens returned by the lexer.
  token *tok_val;

  // Comment strings that we need to deal with mid-rule.
  octave_comment_list *comment_type;

  // Types for the nonterminals we generate.
  char punct_type;
  tree *tree_type;
  tree_matrix *tree_matrix_type;
  tree_cell *tree_cell_type;
  tree_expression *tree_expression_type;
  tree_constant *tree_constant_type;
  tree_fcn_handle *tree_fcn_handle_type;
  tree_funcall *tree_funcall_type;
  tree_function_def *tree_function_def_type;
  tree_anon_fcn_handle *tree_anon_fcn_handle_type;
  tree_identifier *tree_identifier_type;
  tree_index_expression *tree_index_expression_type;
  tree_colon_expression *tree_colon_expression_type;
  tree_argument_list *tree_argument_list_type;
  tree_parameter_list *tree_parameter_list_type;
  tree_command *tree_command_type;
  tree_if_command *tree_if_command_type;
  tree_if_clause *tree_if_clause_type;
  tree_if_command_list *tree_if_command_list_type;
  tree_switch_command *tree_switch_command_type;
  tree_switch_case *tree_switch_case_type;
  tree_switch_case_list *tree_switch_case_list_type;
  tree_decl_elt *tree_decl_elt_type;
  tree_decl_init_list *tree_decl_init_list_type;
  tree_decl_command *tree_decl_command_type;
  tree_statement *tree_statement_type;
  tree_statement_list *tree_statement_list_type;
  octave_user_function *octave_user_function_type;

  tree_classdef *tree_classdef_type;
  tree_classdef_attribute* tree_classdef_attribute_type;
  tree_classdef_attribute_list* tree_classdef_attribute_list_type;
  tree_classdef_superclass* tree_classdef_superclass_type;
  tree_classdef_superclass_list* tree_classdef_superclass_list_type;
  tree_classdef_body* tree_classdef_body_type;
  tree_classdef_property* tree_classdef_property_type;
  tree_classdef_property_list* tree_classdef_property_list_type;
  tree_classdef_properties_block* tree_classdef_properties_block_type;
  tree_classdef_methods_list* tree_classdef_methods_list_type;
  tree_classdef_methods_block* tree_classdef_methods_block_type;
  tree_classdef_event* tree_classdef_event_type;
  tree_classdef_events_list* tree_classdef_events_list_type;
  tree_classdef_events_block* tree_classdef_events_block_type;
  tree_classdef_enum* tree_classdef_enum_type;
  tree_classdef_enum_list* tree_classdef_enum_list_type;
  tree_classdef_enum_block* tree_classdef_enum_block_type;
}

// Tokens with line and column information.
%token <tok_val> '=' ':' '-' '+' '*' '/'
%token <tok_val> ADD_EQ SUB_EQ MUL_EQ DIV_EQ LEFTDIV_EQ POW_EQ
%token <tok_val> EMUL_EQ EDIV_EQ ELEFTDIV_EQ EPOW_EQ AND_EQ OR_EQ
%token <tok_val> EXPR_AND_AND EXPR_OR_OR
%token <tok_val> EXPR_AND EXPR_OR EXPR_NOT
%token <tok_val> EXPR_LT EXPR_LE EXPR_EQ EXPR_NE EXPR_GE EXPR_GT
%token <tok_val> LEFTDIV EMUL EDIV ELEFTDIV EPLUS EMINUS
%token <tok_val> HERMITIAN TRANSPOSE
%token <tok_val> PLUS_PLUS MINUS_MINUS POW EPOW
%token <tok_val> NUM IMAG_NUM
%token <tok_val> STRUCT_ELT
%token <tok_val> NAME
%token <tok_val> END
%token <tok_val> DQ_STRING SQ_STRING
%token <tok_val> FOR PARFOR WHILE DO UNTIL
%token <tok_val> IF ELSEIF ELSE
%token <tok_val> SWITCH CASE OTHERWISE
%token <tok_val> BREAK CONTINUE FUNC_RET
%token <tok_val> UNWIND CLEANUP
%token <tok_val> TRY CATCH
%token <tok_val> GLOBAL PERSISTENT
%token <tok_val> FCN_HANDLE
%token <tok_val> CLASSDEF
%token <tok_val> PROPERTIES METHODS EVENTS ENUMERATION
%token <tok_val> METAQUERY
%token <tok_val> SUPERCLASSREF
%token <tok_val> FQ_IDENT
%token <tok_val> GET SET
%token <tok_val> FCN
%token <tok_val> LEXICAL_ERROR

// Other tokens.
%token<dummy_type> END_OF_INPUT
%token<dummy_type> INPUT_FILE
// %token VARARGIN VARARGOUT

%token<dummy_type> '(' ')' '[' ']' '{' '}' '.' ',' ';' '@' '\n'

// Nonterminals we construct.
%type <dummy_type> indirect_ref_op decl_param_init push_fcn_symtab
%type <dummy_type> param_list_beg param_list_end stmt_begin parse_error
%type <comment_type> stash_comment
%type <tok_val> function_beg classdef_beg
%type <punct_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep
%type <tree_type> input
%type <tree_constant_type> string constant magic_colon
%type <tree_anon_fcn_handle_type> anon_fcn_handle
%type <tree_fcn_handle_type> fcn_handle
%type <tree_matrix_type> matrix_rows
%type <tree_cell_type> cell_rows
%type <tree_expression_type> matrix cell
%type <tree_expression_type> primary_expr oper_expr power_expr
%type <tree_expression_type> simple_expr colon_expr assign_expr expression
%type <tree_identifier_type> identifier fcn_name magic_tilde
%type <tree_funcall_type> superclass_identifier meta_identifier
%type <octave_user_function_type> function1 function2
%type <tree_index_expression_type> word_list_cmd
%type <tree_colon_expression_type> colon_expr1
%type <tree_argument_list_type> arg_list word_list assign_lhs
%type <tree_argument_list_type> cell_or_matrix_row
%type <tree_parameter_list_type> param_list param_list1 param_list2
%type <tree_parameter_list_type> return_list return_list1
%type <tree_command_type> command select_command loop_command
%type <tree_command_type> jump_command except_command
%type <tree_function_def_type> function
%type <tree_classdef_type> classdef
%type <tree_command_type> file
%type <tree_if_command_type> if_command
%type <tree_if_clause_type> elseif_clause else_clause
%type <tree_if_command_list_type> if_cmd_list1 if_cmd_list
%type <tree_switch_command_type> switch_command
%type <tree_switch_case_type> switch_case default_case
%type <tree_switch_case_list_type> case_list1 case_list
%type <tree_decl_elt_type> decl2 param_list_elt
%type <tree_decl_init_list_type> decl1
%type <tree_decl_command_type> declaration
%type <tree_statement_type> statement function_end
%type <tree_statement_list_type> simple_list simple_list1 list list1
%type <tree_statement_list_type> opt_list
%type <tree_classdef_attribute_type> attr
%type <tree_classdef_attribute_list_type> attr_list opt_attr_list
%type <tree_classdef_superclass_type> superclass
%type <tree_classdef_superclass_list_type> superclass_list opt_superclass_list
%type <tree_classdef_body_type> class_body
%type <tree_classdef_property_type> class_property
%type <tree_classdef_property_list_type> property_list
%type <tree_classdef_properties_block_type> properties_block
%type <tree_classdef_methods_list_type> methods_list
%type <tree_classdef_methods_block_type> methods_block
%type <tree_classdef_event_type> class_event
%type <tree_classdef_events_list_type> events_list
%type <tree_classdef_events_block_type> events_block
%type <tree_classdef_enum_type> class_enum
%type <tree_classdef_enum_list_type> enum_list
%type <tree_classdef_enum_block_type> enum_block
%type <tree_function_def_type> method_decl method
%type <octave_user_function_type> method_decl1

// Precedence and associativity.
%right '=' ADD_EQ SUB_EQ MUL_EQ DIV_EQ LEFTDIV_EQ POW_EQ EMUL_EQ EDIV_EQ ELEFTDIV_EQ EPOW_EQ OR_EQ AND_EQ
%left EXPR_OR_OR
%left EXPR_AND_AND
%left EXPR_OR
%left EXPR_AND
%left EXPR_LT EXPR_LE EXPR_EQ EXPR_NE EXPR_GE EXPR_GT
%left ':'
%left '-' '+' EPLUS EMINUS
%left '*' '/' LEFTDIV EMUL EDIV ELEFTDIV
%right UNARY EXPR_NOT
%left POW EPOW HERMITIAN TRANSPOSE
%right PLUS_PLUS MINUS_MINUS
%left '(' '.' '{'

// How to clean up if there is a parse error.  We handle deleting tokens
// and comments seperately and separators are just characters.  The
// remaining items are dynamically allocated parse tree objects that
// must be deleted.  Use the wildcard case (<*>) to detect unhandled
// cases (for example, a new semantic type is added but not handled
// here).

%destructor { } <tok_val>
%destructor { } <punct_type>
%destructor { } <comment_type>
%destructor { } <>

%destructor { delete $$; } <tree_type>
%destructor { delete $$; } <tree_matrix_type>
%destructor { delete $$; } <tree_cell_type>
%destructor { delete $$; } <tree_expression_type>
%destructor { delete $$; } <tree_constant_type>
%destructor { delete $$; } <tree_fcn_handle_type>
%destructor { delete $$; } <tree_funcall_type>
%destructor { delete $$; } <tree_function_def_type>
%destructor { delete $$; } <tree_anon_fcn_handle_type>
%destructor { delete $$; } <tree_identifier_type>
%destructor { delete $$; } <tree_index_expression_type>
%destructor { delete $$; } <tree_colon_expression_type>
%destructor { delete $$; } <tree_argument_list_type>
%destructor { delete $$; } <tree_parameter_list_type>
%destructor { delete $$; } <tree_command_type>
%destructor { delete $$; } <tree_if_command_type>
%destructor { delete $$; } <tree_if_clause_type>
%destructor { delete $$; } <tree_if_command_list_type>
%destructor { delete $$; } <tree_switch_command_type>
%destructor { delete $$; } <tree_switch_case_type>
%destructor { delete $$; } <tree_switch_case_list_type>
%destructor { delete $$; } <tree_decl_elt_type>
%destructor { delete $$; } <tree_decl_init_list_type>
%destructor { delete $$; } <tree_decl_command_type>
%destructor { delete $$; } <tree_statement_type>
%destructor { delete $$; } <tree_statement_list_type>
%destructor { delete $$; } <octave_user_function_type>

%destructor { delete $$; } <tree_classdef_type>
%destructor { delete $$; } <tree_classdef_attribute_type>
%destructor { delete $$; } <tree_classdef_attribute_list_type>
%destructor { delete $$; } <tree_classdef_superclass_type>
%destructor { delete $$; } <tree_classdef_superclass_list_type>
%destructor { delete $$; } <tree_classdef_body_type>
%destructor { delete $$; } <tree_classdef_property_type>
%destructor { delete $$; } <tree_classdef_property_list_type>
%destructor { delete $$; } <tree_classdef_properties_block_type>
%destructor { delete $$; } <tree_classdef_methods_list_type>
%destructor { delete $$; } <tree_classdef_methods_block_type>
%destructor { delete $$; } <tree_classdef_event_type>
%destructor { delete $$; } <tree_classdef_events_list_type>
%destructor { delete $$; } <tree_classdef_events_block_type>
%destructor { delete $$; } <tree_classdef_enum_type>
%destructor { delete $$; } <tree_classdef_enum_list_type>
%destructor { delete $$; } <tree_classdef_enum_block_type>

// Defining a generic destructor generates a warning if destructors are
// already explicitly declared for all types.
//
// %destructor {
//    warning_with_id
//      ("Octave:parser-destructor",
//       "possible memory leak in cleanup following parse error");
// } <*>

// Where to start.
%start input

%%

// ==============================
// Statements and statement lists
// ==============================

input           : simple_list '\n'
                  {
                    $$ = 0;
                    parser.stmt_list = $1;
                    YYACCEPT;
                  }
                | simple_list END_OF_INPUT
                  {
                    $$ = 0;
                    lexer.end_of_input = true;
                    parser.stmt_list = $1;
                    YYACCEPT;
                  }
                | parse_error
                  {
                    $$ = 0;
                    YYABORT;
                  }
                ;

simple_list     : opt_sep_no_nl
                  {
                    YYUSE ($1);

                    $$ = 0;
                  }
                | simple_list1 opt_sep_no_nl
                  { $$ = parser.set_stmt_print_flag ($1, $2, false); }
                ;

simple_list1    : statement
                  { $$ = parser.make_statement_list ($1); }
                | simple_list1 sep_no_nl statement
                  { $$ = parser.append_statement_list ($1, $2, $3, false); }
                ;

opt_list        : // empty
                  { $$ = new tree_statement_list (); }
                | list
                  { $$ = $1; }
                ;

list            : list1 opt_sep
                  { $$ = parser.set_stmt_print_flag ($1, $2, true); }
                ;

list1           : statement
                  { $$ = parser.make_statement_list ($1); }
                | list1 sep statement
                  { $$ = parser.append_statement_list ($1, $2, $3, true); }
                ;

statement       : expression
                  { $$ = parser.make_statement ($1); }
                | command
                  { $$ = parser.make_statement ($1); }
                | word_list_cmd
                  { $$ = parser.make_statement ($1); }
                ;

// =================
// Word-list command
// =================

// These are not really like expressions since they can't appear on
// the RHS of an assignment.  But they are also not like commands (IF,
// WHILE, etc.

word_list_cmd   : identifier word_list
                  {
                    $$ = parser.make_index_expression ($1, $2, '(');
                    if (! $$)
                      {
                        // make_index_expression deleted $1 and $2.
                        YYABORT;
                      }
                  }
                ;

word_list       : string
                  { $$ = new tree_argument_list ($1); }
                | word_list string
                  {
                    $1->append ($2);
                    $$ = $1;
                  }
                ;

// ===========
// Expressions
// ===========

identifier      : NAME
                  {
                    symbol_table::symbol_record *sr = $1->sym_rec ();
                    $$ = new tree_identifier (*sr, $1->line (), $1->column ());
                  }
                ;

superclass_identifier
                : SUPERCLASSREF
                  {
                    std::string method_nm = $1->superclass_method_name ();
                    std::string class_nm = $1->superclass_class_name ();

                    $$ = parser.make_superclass_ref (method_nm, class_nm);
                  }
                ;

meta_identifier : METAQUERY
                  {
                    std::string class_nm = $1->text ();

                    $$ = parser.make_meta_class_query (class_nm);
                  }
                ;

string          : DQ_STRING
                  { $$ = parser.make_constant (DQ_STRING, $1); }
                | SQ_STRING
                  { $$ = parser.make_constant (SQ_STRING, $1); }
                ;

constant        : NUM
                  { $$ = parser.make_constant (NUM, $1); }
                | IMAG_NUM
                  { $$ = parser.make_constant (IMAG_NUM, $1); }
                | string
                  { $$ = $1; }
                ;

matrix          : '[' matrix_rows ']'
                  { $$ = parser.finish_matrix ($2); }
                ;

matrix_rows     : cell_or_matrix_row
                  { $$ = $1 ? new tree_matrix ($1) : 0; }
                | matrix_rows ';' cell_or_matrix_row
                  {
                    if ($1)
                      {
                        if ($3)
                          $1->append ($3);

                        $$ = $1;
                      }
                    else
                      $$ = $3 ? new tree_matrix ($3) : 0;
                  }
                ;

cell            : '{' cell_rows '}'
                  { $$ = parser.finish_cell ($2); }
                ;

cell_rows       : cell_or_matrix_row
                  { $$ = $1 ? new tree_cell ($1) : 0; }
                | cell_rows ';' cell_or_matrix_row
                  {
                    if ($1)
                      {
                        if ($3)
                          $1->append ($3);

                        $$ = $1;
                      }
                    else
                      $$ = $3 ? new tree_cell ($3) : 0;
                  }
                ;

// tree_argument_list objects can't be empty or have leading or trailing
// commas, but those are all allowed in matrix and cell array rows.

cell_or_matrix_row
                : // empty
                  { $$ = 0; }
                | ','
                  { $$ = 0; }
                | arg_list
                  { $$ = $1; }
                | arg_list ','
                  { $$ = $1; }
                | ',' arg_list
                  { $$ = $2; }
                | ',' arg_list ','
                  { $$ = $2; }
                ;

fcn_handle      : '@' FCN_HANDLE
                  {
                    $$ = parser.make_fcn_handle ($2);
                    lexer.looking_at_function_handle--;
                  }
                ;

anon_fcn_handle : '@' param_list stmt_begin statement
                  {
                    $$ = parser.make_anon_fcn_handle ($2, $4);
                    lexer.nesting_level.remove ();
                  }
                ;

primary_expr    : identifier
                  { $$ = $1; }
                | constant
                  { $$ = $1; }
                | fcn_handle
                  { $$ = $1; }
                | matrix
                  {
                    lexer.looking_at_matrix_or_assign_lhs = false;
                    $$ = $1;
                  }
                | cell
                  { $$ = $1; }
                | meta_identifier
                  { $$ = $1; }
                | superclass_identifier
                  { $$ = $1; }
                | '(' expression ')'
                  { $$ = $2->mark_in_parens (); }
                ;

magic_colon     : ':'
                  {
                    YYUSE ($1);

                    octave_value tmp (octave_value::magic_colon_t);
                    $$ = new tree_constant (tmp);
                  }
                ;

magic_tilde     : EXPR_NOT
                  {
                    YYUSE ($1);

                    $$ = new tree_black_hole ();
                  }
                ;

arg_list        : expression
                  { $$ = new tree_argument_list ($1); }
                | magic_colon
                  { $$ = new tree_argument_list ($1); }
                | magic_tilde
                  { $$ = new tree_argument_list ($1); }
                | arg_list ',' magic_colon
                  {
                    $1->append ($3);
                    $$ = $1;
                  }
                | arg_list ',' magic_tilde
                  {
                    $1->append ($3);
                    $$ = $1;
                  }
                | arg_list ',' expression
                  {
                    $1->append ($3);
                    $$ = $1;
                  }
                ;

indirect_ref_op : '.'
                  {
                    $$ = 0;
                    lexer.looking_at_indirect_ref = true;
                  }
                ;

oper_expr       : primary_expr
                  { $$ = $1; }
                | oper_expr PLUS_PLUS
                  { $$ = parser.make_postfix_op (PLUS_PLUS, $1, $2); }
                | oper_expr MINUS_MINUS
                  { $$ = parser.make_postfix_op (MINUS_MINUS, $1, $2); }
                | oper_expr '(' ')'
                  {
                    $$ = parser.make_index_expression ($1, 0, '(');
                    if (! $$)
                      {
                        // make_index_expression deleted $1.
                        YYABORT;
                      }
                  }
                | oper_expr '(' arg_list ')'
                  {
                    $$ = parser.make_index_expression ($1, $3, '(');
                    if (! $$)
                      {
                        // make_index_expression deleted $1 and $3.
                        YYABORT;
                      }
                  }
                | oper_expr '{' '}'
                  {
                    $$ = parser.make_index_expression ($1, 0, '{');
                    if (! $$)
                      {
                        // make_index_expression deleted $1.
                        YYABORT;
                      }
                  }
                | oper_expr '{' arg_list '}'
                  {
                    $$ = parser.make_index_expression ($1, $3, '{');
                    if (! $$)
                      {
                        // make_index_expression deleted $1 and $3.
                        YYABORT;
                      }
                  }
                | oper_expr HERMITIAN
                  { $$ = parser.make_postfix_op (HERMITIAN, $1, $2); }
                | oper_expr TRANSPOSE
                  { $$ = parser.make_postfix_op (TRANSPOSE, $1, $2); }
                | oper_expr indirect_ref_op STRUCT_ELT
                  { $$ = parser.make_indirect_ref ($1, $3->text ()); }
                | oper_expr indirect_ref_op '(' expression ')'
                  { $$ = parser.make_indirect_ref ($1, $4); }
                | PLUS_PLUS oper_expr %prec UNARY
                  { $$ = parser.make_prefix_op (PLUS_PLUS, $2, $1); }
                | MINUS_MINUS oper_expr %prec UNARY
                  { $$ = parser.make_prefix_op (MINUS_MINUS, $2, $1); }
                | EXPR_NOT oper_expr %prec UNARY
                  { $$ = parser.make_prefix_op (EXPR_NOT, $2, $1); }
                | '+' oper_expr %prec UNARY
                  { $$ = parser.make_prefix_op ('+', $2, $1); }
                | '-' oper_expr %prec UNARY
                  { $$ = parser.make_prefix_op ('-', $2, $1); }
                | oper_expr POW power_expr
                  { $$ = parser.make_binary_op (POW, $1, $2, $3); }
                | oper_expr EPOW power_expr
                  { $$ = parser.make_binary_op (EPOW, $1, $2, $3); }
                | oper_expr '+' oper_expr
                  { $$ = parser.make_binary_op ('+', $1, $2, $3); }
                | oper_expr '-' oper_expr
                  { $$ = parser.make_binary_op ('-', $1, $2, $3); }
                | oper_expr '*' oper_expr
                  { $$ = parser.make_binary_op ('*', $1, $2, $3); }
                | oper_expr '/' oper_expr
                  { $$ = parser.make_binary_op ('/', $1, $2, $3); }
                | oper_expr EPLUS oper_expr
                  { $$ = parser.make_binary_op ('+', $1, $2, $3); }
                | oper_expr EMINUS oper_expr
                  { $$ = parser.make_binary_op ('-', $1, $2, $3); }
                | oper_expr EMUL oper_expr
                  { $$ = parser.make_binary_op (EMUL, $1, $2, $3); }
                | oper_expr EDIV oper_expr
                  { $$ = parser.make_binary_op (EDIV, $1, $2, $3); }
                | oper_expr LEFTDIV oper_expr
                  { $$ = parser.make_binary_op (LEFTDIV, $1, $2, $3); }
                | oper_expr ELEFTDIV oper_expr
                  { $$ = parser.make_binary_op (ELEFTDIV, $1, $2, $3); }
                ;

power_expr      : primary_expr
                  { $$ = $1; }
                | power_expr PLUS_PLUS
                  { $$ = parser.make_postfix_op (PLUS_PLUS, $1, $2); }
                | power_expr MINUS_MINUS
                  { $$ = parser.make_postfix_op (MINUS_MINUS, $1, $2); }
                | power_expr '(' ')'
                  {
                    $$ = parser.make_index_expression ($1, 0, '(');
                    if (! $$)
                      {
                        // make_index_expression deleted $1.
                        YYABORT;
                      }
                  }
                | power_expr '(' arg_list ')'
                  {
                    $$ = parser.make_index_expression ($1, $3, '(');
                    if (! $$)
                      {
                        // make_index_expression deleted $1 and $3.
                        YYABORT;
                      }
                  }
                | power_expr '{' '}'
                  {
                    $$ = parser.make_index_expression ($1, 0, '{');
                    if (! $$)
                      {
                        // make_index_expression deleted $1.
                        YYABORT;
                      }
                  }
                | power_expr '{' arg_list '}'
                  {
                    $$ = parser.make_index_expression ($1, $3, '{');
                    if (! $$)
                      {
                        // make_index_expression deleted $1 and $3.
                        YYABORT;
                      }
                  }
                | power_expr indirect_ref_op STRUCT_ELT
                  { $$ = parser.make_indirect_ref ($1, $3->text ()); }
                | power_expr indirect_ref_op '(' expression ')'
                  { $$ = parser.make_indirect_ref ($1, $4); }
                | PLUS_PLUS power_expr %prec POW
                  { $$ = parser.make_prefix_op (PLUS_PLUS, $2, $1); }
                | MINUS_MINUS power_expr %prec POW
                  { $$ = parser.make_prefix_op (MINUS_MINUS, $2, $1); }
                | EXPR_NOT power_expr %prec POW
                  { $$ = parser.make_prefix_op (EXPR_NOT, $2, $1); }
                | '+' power_expr %prec POW
                  { $$ = parser.make_prefix_op ('+', $2, $1); }
                | '-' power_expr %prec POW
                  { $$ = parser.make_prefix_op ('-', $2, $1); }
                ;

colon_expr      : colon_expr1
                  { $$ = parser.finish_colon_expression ($1); }
                ;

colon_expr1     : oper_expr
                  { $$ = new tree_colon_expression ($1); }
                | colon_expr1 ':' oper_expr
                  {
                    YYUSE ($2);

                    if (! ($$ = $1->append ($3)))
                      {
                        delete $1;
                        delete $3;
                        YYABORT;
                      }
                  }
                ;

simple_expr     : colon_expr
                  { $$ = $1; }
                | simple_expr EXPR_LT simple_expr
                  { $$ = parser.make_binary_op (EXPR_LT, $1, $2, $3); }
                | simple_expr EXPR_LE simple_expr
                  { $$ = parser.make_binary_op (EXPR_LE, $1, $2, $3); }
                | simple_expr EXPR_EQ simple_expr
                  { $$ = parser.make_binary_op (EXPR_EQ, $1, $2, $3); }
                | simple_expr EXPR_GE simple_expr
                  { $$ = parser.make_binary_op (EXPR_GE, $1, $2, $3); }
                | simple_expr EXPR_GT simple_expr
                  { $$ = parser.make_binary_op (EXPR_GT, $1, $2, $3); }
                | simple_expr EXPR_NE simple_expr
                  { $$ = parser.make_binary_op (EXPR_NE, $1, $2, $3); }
                | simple_expr EXPR_AND simple_expr
                  { $$ = parser.make_binary_op (EXPR_AND, $1, $2, $3); }
                | simple_expr EXPR_OR simple_expr
                  { $$ = parser.make_binary_op (EXPR_OR, $1, $2, $3); }
                | simple_expr EXPR_AND_AND simple_expr
                  { $$ = parser.make_boolean_op (EXPR_AND_AND, $1, $2, $3); }
                | simple_expr EXPR_OR_OR simple_expr
                  { $$ = parser.make_boolean_op (EXPR_OR_OR, $1, $2, $3); }
                ;

assign_lhs      : simple_expr
                  {
                    $$ = parser.validate_matrix_for_assignment ($1);

                    if ($$)
                      { lexer.looking_at_matrix_or_assign_lhs = false; }
                    else
                      {
                        // validate_matrix_for_assignment deleted $1.
                        YYABORT;
                      }
                  }
                ;

assign_expr     : assign_lhs '=' expression
                  { $$ = parser.make_assign_op ('=', $1, $2, $3); }
                | assign_lhs ADD_EQ expression
                  { $$ = parser.make_assign_op (ADD_EQ, $1, $2, $3); }
                | assign_lhs SUB_EQ expression
                  { $$ = parser.make_assign_op (SUB_EQ, $1, $2, $3); }
                | assign_lhs MUL_EQ expression
                  { $$ = parser.make_assign_op (MUL_EQ, $1, $2, $3); }
                | assign_lhs DIV_EQ expression
                  { $$ = parser.make_assign_op (DIV_EQ, $1, $2, $3); }
                | assign_lhs LEFTDIV_EQ expression
                  { $$ = parser.make_assign_op (LEFTDIV_EQ, $1, $2, $3); }
                | assign_lhs POW_EQ expression
                  { $$ = parser.make_assign_op (POW_EQ, $1, $2, $3); }
                | assign_lhs EMUL_EQ expression
                  { $$ = parser.make_assign_op (EMUL_EQ, $1, $2, $3); }
                | assign_lhs EDIV_EQ expression
                  { $$ = parser.make_assign_op (EDIV_EQ, $1, $2, $3); }
                | assign_lhs ELEFTDIV_EQ expression
                  { $$ = parser.make_assign_op (ELEFTDIV_EQ, $1, $2, $3); }
                | assign_lhs EPOW_EQ expression
                  { $$ = parser.make_assign_op (EPOW_EQ, $1, $2, $3); }
                | assign_lhs AND_EQ expression
                  { $$ = parser.make_assign_op (AND_EQ, $1, $2, $3); }
                | assign_lhs OR_EQ expression
                  { $$ = parser.make_assign_op (OR_EQ, $1, $2, $3); }
                ;

expression      : simple_expr
                  {
                    if ($1 && ($1->is_matrix () || $1->is_cell ()))
                      {
                        if (parser.validate_array_list ($1))
                          $$ = $1;
                        else
                          {
                            delete $1;
                            YYABORT;
                          }
                      }
                    else
                      $$ = $1;
                  }
                | assign_expr
                  {
                    if (! $1)
                      YYABORT;

                    $$ = $1;
                  }
                | anon_fcn_handle
                  { $$ = $1; }
                ;

// ================================================
// Commands, declarations, and function definitions
// ================================================

command         : declaration
                  { $$ = $1; }
                | select_command
                  { $$ = $1; }
                | loop_command
                  { $$ = $1; }
                | jump_command
                  { $$ = $1; }
                | except_command
                  { $$ = $1; }
                | function
                  { $$ = $1; }
                | file
                  { $$ = $1; }
                ;

// =====================
// Declaration statemnts
// =====================

declaration     : GLOBAL decl1
                  {
                    $$ = parser.make_decl_command (GLOBAL, $1, $2);
                    lexer.looking_at_decl_list = false;
                  }
                | PERSISTENT decl1
                  {
                    $$ = parser.make_decl_command (PERSISTENT, $1, $2);
                    lexer.looking_at_decl_list = false;
                  }
                ;

decl1           : decl2
                  { $$ = new tree_decl_init_list ($1); }
                | decl1 decl2
                  {
                    $1->append ($2);
                    $$ = $1;
                  }
                ;

decl_param_init : // empty
                  {
                    $$ = 0;
                    lexer.looking_at_initializer_expression = true;
                  }

decl2           : identifier
                  { $$ = new tree_decl_elt ($1); }
                | identifier '=' decl_param_init expression
                  {
                    YYUSE ($2);

                    lexer.looking_at_initializer_expression = false;
                    $$ = new tree_decl_elt ($1, $4);
                  }
                ;

// ====================
// Selection statements
// ====================

select_command  : if_command
                  { $$ = $1; }
                | switch_command
                  { $$ = $1; }
                ;

// ============
// If statement
// ============

if_command      : IF stash_comment if_cmd_list END
                  {
                    if (! ($$ = parser.finish_if_command ($1, $3, $4, $2)))
                      {
                        // finish_if_command deleted $3.
                        YYABORT;
                      }
                  }
                ;

if_cmd_list     : if_cmd_list1
                  { $$ = $1; }
                | if_cmd_list1 else_clause
                  {
                    $1->append ($2);
                    $$ = $1;
                  }
                ;

if_cmd_list1    : expression stmt_begin opt_sep opt_list
                  {
                    YYUSE ($3);

                    $1->mark_braindead_shortcircuit ();

                    $$ = parser.start_if_command ($1, $4);
                  }
                | if_cmd_list1 elseif_clause
                  {
                    $1->append ($2);
                    $$ = $1;
                  }
                ;

elseif_clause   : ELSEIF stash_comment opt_sep expression stmt_begin opt_sep opt_list
                  {
                    YYUSE ($3);
                    YYUSE ($6);

                    $4->mark_braindead_shortcircuit ();

                    $$ = parser.make_elseif_clause ($1, $4, $7, $2);
                  }
                ;

else_clause     : ELSE stash_comment opt_sep opt_list
                  {
                    YYUSE ($1);
                    YYUSE ($3);

                    $$ = new tree_if_clause ($4, $2);
                  }
                ;

// ================
// Switch statement
// ================

switch_command  : SWITCH stash_comment expression opt_sep case_list END
                  {
                    YYUSE ($4);

                    if (! ($$ = parser.finish_switch_command ($1, $3, $5, $6, $2)))
                      {
                        // finish_switch_command deleted $3 adn $5.
                        YYABORT;
                      }
                  }
                ;

case_list       : // empty
                  { $$ = new tree_switch_case_list (); }
                | default_case
                  { $$ = new tree_switch_case_list ($1); }
                | case_list1
                  { $$ = $1; }
                | case_list1 default_case
                  {
                    $1->append ($2);
                    $$ = $1;
                  }
                ;

case_list1      : switch_case
                  { $$ = new tree_switch_case_list ($1); }
                | case_list1 switch_case
                  {
                    $1->append ($2);
                    $$ = $1;
                  }
                ;

switch_case     : CASE stash_comment opt_sep expression stmt_begin opt_sep opt_list
                  {
                    YYUSE ($3);
                    YYUSE ($6);

                    $$ = parser.make_switch_case ($1, $4, $7, $2);
                  }
                ;

default_case    : OTHERWISE stash_comment opt_sep opt_list
                  {
                    YYUSE ($1);
                    YYUSE ($3);

                    $$ = new tree_switch_case ($4, $2);
                  }
                ;

// =======
// Looping
// =======

loop_command    : WHILE stash_comment expression stmt_begin opt_sep opt_list END
                  {
                    YYUSE ($5);

                    $3->mark_braindead_shortcircuit ();

                    if (! ($$ = parser.make_while_command ($1, $3, $6, $7, $2)))
                      {
                        // make_while_command deleted $3 and $6.
                        YYABORT;
                      }
                  }
                | DO stash_comment opt_sep opt_list UNTIL expression
                  {
                    YYUSE ($1);
                    YYUSE ($3);

                    $$ = parser.make_do_until_command ($5, $4, $6, $2);
                  }
                | FOR stash_comment assign_lhs '=' expression stmt_begin opt_sep opt_list END
                  {
                    YYUSE ($4);
                    YYUSE ($7);

                    if (! ($$ = parser.make_for_command (FOR, $1, $3, $5, 0,
                                                         $8, $9, $2)))
                      {
                        // make_for_command deleted $3, $5, and $8.
                        YYABORT;
                      }
                  }
                | FOR stash_comment '(' assign_lhs '=' expression ')' opt_sep opt_list END
                  {
                    YYUSE ($5);
                    YYUSE ($8);

                    if (! ($$ = parser.make_for_command (FOR, $1, $4, $6, 0,
                                                         $9, $10, $2)))
                      {
                        // make_for_command deleted $4, $6, and $9.
                        YYABORT;
                      }
                  }
                | PARFOR stash_comment assign_lhs '=' expression stmt_begin opt_sep opt_list END
                  {
                    YYUSE ($4);
                    YYUSE ($7);

                    if (! ($$ = parser.make_for_command (PARFOR, $1, $3, $5,
                                                         0, $8, $9, $2)))
                      {
                        // make_for_command deleted $3, $5, and $8.
                        YYABORT;
                      }
                  }
                | PARFOR stash_comment '(' assign_lhs '=' expression ',' expression ')' opt_sep opt_list END
                  {
                    YYUSE ($5);
                    YYUSE ($10);

                    if (! ($$ = parser.make_for_command (PARFOR, $1, $4, $6,
                                                         $8, $11, $12, $2)))
                      {
                        // make_for_command deleted $4, $6, $8, and $11.
                        YYABORT;
                      }
                  }
                ;

// =======
// Jumping
// =======

jump_command    : BREAK
                  { $$ = parser.make_break_command ($1); }
                | CONTINUE
                  { $$ = parser.make_continue_command ($1); }
                | FUNC_RET
                  { $$ = parser.make_return_command ($1); }
                ;

// ==========
// Exceptions
// ==========

except_command  : UNWIND stash_comment opt_sep opt_list CLEANUP
                  stash_comment opt_sep opt_list END
                  {
                    YYUSE ($3);
                    YYUSE ($5);
                    YYUSE ($7);

                    if (! ($$ = parser.make_unwind_command ($1, $4, $8, $9, $2, $6)))
                      {
                        // make_unwind_command deleted $4 and $8.
                        YYABORT;
                      }
                  }
                | TRY stash_comment opt_sep opt_list CATCH stash_comment
                  opt_sep opt_list END
                  {
                    YYUSE ($3);
                    YYUSE ($5);
                    YYUSE ($7);

                    if (! ($$ = parser.make_try_command ($1, $4, $7, $8, $9, $2, $6)))
                      {
                        // make_try_command deleted $4 and $8.
                        YYABORT;
                      }
                  }
                | TRY stash_comment opt_sep opt_list END
                  {
                    YYUSE ($3);

                    if (! ($$ = parser.make_try_command ($1, $4, 0, 0, $5, $2, 0)))
                      {
                        // make_try_command deleted $4.
                        YYABORT;
                      }
                  }
                ;

// ===========================================
// Some 'subroutines' for function definitions
// ===========================================

push_fcn_symtab : // empty
                  {
                    $$ = 0;

                    parser.curr_fcn_depth++;

                    if (parser.max_fcn_depth < parser.curr_fcn_depth)
                      parser.max_fcn_depth = parser.curr_fcn_depth;

                    lexer.symtab_context.push (symbol_table::alloc_scope ());

                    parser.function_scopes.push_back
                     (lexer.symtab_context.curr_scope ());

                    if (! lexer.reading_script_file
                        && parser.curr_fcn_depth == 1
                        && ! parser.parsing_subfunctions)
                      parser.primary_fcn_scope
                        = lexer.symtab_context.curr_scope ();

                    if (lexer.reading_script_file
                        && parser.curr_fcn_depth > 1)
                      {
                        parser.bison_error ("nested functions not implemented in this context");
                        YYABORT;
                      }
                  }
                ;

// ===========================
// List of function parameters
// ===========================

param_list_beg  : '('
                  {
                    $$ = 0;
                    lexer.looking_at_parameter_list = true;

                    if (lexer.looking_at_function_handle)
                      {
                        lexer.symtab_context.push (symbol_table::alloc_scope ());
                        lexer.looking_at_function_handle--;
                        lexer.looking_at_anon_fcn_args = true;
                      }
                  }
                ;

param_list_end  : ')'
                  {
                    $$ = 0;
                    lexer.looking_at_parameter_list = false;
                    lexer.looking_for_object_index = false;
                  }
                ;

param_list      : param_list_beg param_list1 param_list_end
                  {
                    if ($2)
                      lexer.mark_as_variables ($2->variable_names ());

                    $$ = $2;
                  }
                | param_list_beg error
                  {
                    $$ = 0;
                    parser.bison_error ("invalid parameter list");
                    YYABORT;
                  }
                ;

param_list1     : // empty
                  { $$ = 0; }
                | param_list2
                  {
                    $1->mark_as_formal_parameters ();
                    if ($1->validate (tree_parameter_list::in))
                      {
                        lexer.mark_as_variables ($1->variable_names ());
                        $$ = $1;
                      }
                    else
                      {
                        delete $1;
                        YYABORT;
                      }
                  }
                ;

param_list2     : param_list_elt
                  { $$ = new tree_parameter_list ($1); }
                | param_list2 ',' param_list_elt
                  {
                    $1->append ($3);
                    $$ = $1;
                  }
                ;

param_list_elt  : decl2
                  { $$ = $1; }
                | magic_tilde
                  { $$ = new tree_decl_elt ($1); }
                ;

// ===================================
// List of function return value names
// ===================================

return_list     : '[' ']'
                  {
                    lexer.looking_at_return_list = false;

                    $$ = new tree_parameter_list ();
                  }
                | identifier
                  {
                    lexer.looking_at_return_list = false;

                    tree_parameter_list *tmp = new tree_parameter_list ($1);

                    // Even though this parameter list can contain only
                    // a single identifier, we still need to validate it
                    // to check for varargin or varargout.

                    if (tmp->validate (tree_parameter_list::out))
                      $$ = tmp;
                    else
                      {
                        delete tmp;
                        YYABORT;
                      }
                  }
                | '[' return_list1 ']'
                  {
                    lexer.looking_at_return_list = false;

                    // Check for duplicate parameter names, varargin,
                    // or varargout.

                    if ($2->validate (tree_parameter_list::out))
                      $$ = $2;
                    else
                      {
                        delete $2;
                        YYABORT;
                      }
                  }
                ;

return_list1    : identifier
                  { $$ = new tree_parameter_list (new tree_decl_elt ($1)); }
                | return_list1 ',' identifier
                  {
                    $1->append (new tree_decl_elt ($3));
                    $$ = $1;
                  }
                ;

// =======================
// Script or function file
// =======================

file            : INPUT_FILE opt_nl opt_list END_OF_INPUT
                  {
                    YYUSE ($2);

                    if (lexer.reading_fcn_file)
                      {
                        // Delete the dummy statement_list we created
                        // after parsing the function.  Any function
                        // definitions found in the file have already
                        // been stored in the symbol table or in
                        // base_parser::primary_fcn_ptr.

                        delete $3;
                      }
                    else
                      {
                        tree_statement *end_of_script
                          = parser.make_end ("endscript", true,
                                             lexer.input_line_number,
                                             lexer.current_input_column);

                        parser.make_script ($3, end_of_script);
                      }

                    $$ = 0;
                  }
                | INPUT_FILE opt_nl classdef opt_sep END_OF_INPUT
                  {
                    YYUSE ($2);
                    YYUSE ($4);

                    if (lexer.reading_classdef_file)
                      parser.classdef_object = $3;

                    $$ = 0;
                  }
                ;

// ===================
// Function definition
// ===================

function_beg    : push_fcn_symtab FCN
                  {
                    $$ = $2;
                    if (lexer.reading_classdef_file
                        || lexer.parsing_classdef)
                      lexer.maybe_classdef_get_set_method = true;
                  }
                ;

function        : function_beg stash_comment function1
                  {
                    $$ = parser.finish_function (0, $3, $2, $1->line (),
                                                 $1->column ());
                    parser.recover_from_parsing_function ();
                  }
                | function_beg stash_comment return_list '=' function1
                  {
                    YYUSE ($4);

                    $$ = parser.finish_function ($3, $5, $2, $1->line (),
                                                 $1->column ());
                    parser.recover_from_parsing_function ();
                  }
                ;

fcn_name        : identifier
                  {
                    std::string id_name = $1->name ();

                    lexer.parsed_function_name.top () = true;
                    lexer.maybe_classdef_get_set_method = false;

                    $$ = $1;
                  }
                | GET '.' identifier
                  {
                    YYUSE ($1);

                    lexer.parsed_function_name.top () = true;
                    lexer.maybe_classdef_get_set_method = false;
                    lexer.parsing_classdef_get_method = true;
                    $$ = $3;
                  }
                | SET '.' identifier
                  {
                    YYUSE ($1);

                    lexer.parsed_function_name.top () = true;
                    lexer.maybe_classdef_get_set_method = false;
                    lexer.parsing_classdef_set_method = true;
                    $$ = $3;
                  }
                ;

function1       : fcn_name function2
                  {
                    std::string fname = $1->name ();

                    delete $1;

                    if (lexer.parsing_classdef_get_method)
                      fname.insert (0, "get.");
                    else if (lexer.parsing_classdef_set_method)
                      fname.insert (0, "set.");

                    lexer.parsing_classdef_get_method = false;
                    lexer.parsing_classdef_set_method = false;

                    $$ = parser.frob_function (fname, $2);
                  }
                ;

function2       : param_list opt_sep opt_list function_end
                  {
                    YYUSE ($2);

                    $$ = parser.start_function ($1, $3, $4);
                  }
                | opt_sep opt_list function_end
                  {
                    YYUSE ($1);

                    $$ = parser.start_function (0, $2, $3);
                  }
                ;

function_end    : END
                  {
                    parser.endfunction_found = true;

                    if (parser.end_token_ok ($1, token::function_end))
                      $$ = parser.make_end ("endfunction", false,
                                            $1->line (), $1->column ());
                    else
                      {
                        parser.end_token_error ($1, token::function_end);
                        YYABORT;
                      }
                  }
                | END_OF_INPUT
                  {
// A lot of tests are based on the assumption that this is OK
//                  if (lexer.reading_script_file)
//                    {
//                      parser.bison_error ("function body open at end of script");
//                      YYABORT;
//                    }

                    if (parser.endfunction_found)
                      {
                        parser.bison_error ("inconsistent function endings -- "
                                 "if one function is explicitly ended, "
                                 "so must all the others");
                        YYABORT;
                      }

                    if (! (lexer.reading_fcn_file || lexer.reading_script_file
                           || lexer.input_from_eval_string ()))
                      {
                        parser.bison_error ("function body open at end of input");
                        YYABORT;
                      }

                    if (lexer.reading_classdef_file)
                      {
                        parser.bison_error ("classdef body open at end of input");
                        YYABORT;
                      }

                    $$ = parser.make_end ("endfunction", true,
                                          lexer.input_line_number,
                                          lexer.current_input_column);
                  }
                ;

// ========
// Classdef
// ========

classdef_beg    : CLASSDEF
                  {
                    if (! lexer.reading_classdef_file)
                      {
                        parser.bison_error ("classdef must appear inside a file containing only a class definition");
                        YYABORT;
                      }

                    lexer.parsing_classdef = true;
                    $$ = $1;
                  }
                ;

classdef        : classdef_beg stash_comment opt_attr_list identifier opt_superclass_list opt_sep class_body opt_sep END
                  {
                    YYUSE ($6);
                    YYUSE ($8);

                    lexer.parsing_classdef = false;

                    if (! ($$ = parser.make_classdef ($1, $3, $4, $5, $7, $9, $2)))
                      {
                        // make_classdef deleted $3, $4, $5, and $7.
                        YYABORT;
                      }
                  }
                | classdef_beg stash_comment opt_attr_list identifier opt_superclass_list opt_sep END
                  {
                    YYUSE ($6);

                    lexer.parsing_classdef = false;

                    if (! ($$ = parser.make_classdef ($1, $3, $4, $5, 0, $7, $2)))
                      {
                        // make_classdef deleted $3, $4, and $5.
                        YYABORT;
                      }
                  }
                ;

opt_attr_list   : // empty
                  { $$ = 0; }
                | '(' attr_list ')'
                  { $$ = $2; }
                ;

attr_list       : attr
                  { $$ = new tree_classdef_attribute_list ($1); }
                | attr_list ',' attr
                  {
                    $1->append ($3);
                    $$ = $1;
                  }
                ;

attr            : identifier
                  { $$ = new tree_classdef_attribute ($1); }
                | identifier '=' decl_param_init expression
                  {
                    YYUSE ($2);

                    lexer.looking_at_initializer_expression = false;
                    $$ = new tree_classdef_attribute ($1, $4);
                  }
                | EXPR_NOT identifier
                  {
                    YYUSE ($1);

                    $$ = new tree_classdef_attribute ($2, false);
                  }
                ;

opt_superclass_list
                : // empty
                  { $$ = 0; }
                | superclass_list
                  { $$ = $1; }
                ;

superclass_list : EXPR_LT
                  {
                    YYUSE ($1);

                    lexer.enable_fq_identifier ();
                  }
                  superclass
                  { $$ = new tree_classdef_superclass_list ($3); }
                | superclass_list EXPR_AND
                  {
                    YYUSE ($2);

                    lexer.enable_fq_identifier ();
                  }
                  superclass
                  {
                    $1->append ($4);
                    $$ = $1;
                  }
                ;

superclass      : FQ_IDENT
                  { $$ = new tree_classdef_superclass ($1->text ()); }
                ;

class_body      : properties_block
                  { $$ = new tree_classdef_body ($1); }
                | methods_block
                  { $$ = new tree_classdef_body ($1); }
                | events_block
                  { $$ = new tree_classdef_body ($1); }
                | enum_block
                  { $$ = new tree_classdef_body ($1); }
                | class_body opt_sep properties_block
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                | class_body opt_sep methods_block
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                | class_body opt_sep events_block
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                | class_body opt_sep enum_block
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                ;

properties_block
                : PROPERTIES stash_comment opt_attr_list opt_sep property_list opt_sep END
                  {
                    YYUSE ($4);
                    YYUSE ($6);

                    if (! ($$ = parser.make_classdef_properties_block
                           ($1, $3, $5, $7, $2)))
                      {
                        // make_classdef_properties_block delete $3 and $5.
                        YYABORT;
                      }
                  }
                | PROPERTIES stash_comment opt_attr_list opt_sep END
                  {
                    YYUSE ($4);

                    if (! ($$ = parser.make_classdef_properties_block
                           ($1, $3, 0, $5, $2)))
                      {
                        // make_classdef_properties_block delete $3.
                        YYABORT;
                      }
                  }
                ;

property_list
                : class_property
                  { $$ = new tree_classdef_property_list ($1); }
                | property_list opt_sep class_property
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                ;

class_property  : identifier
                  { $$ = new tree_classdef_property ($1); }
                | identifier '=' decl_param_init expression ';'
                  {
                    YYUSE ($2);

                    lexer.looking_at_initializer_expression = false;
                    $$ = new tree_classdef_property ($1, $4);
                  }
                ;

methods_block   : METHODS stash_comment opt_attr_list opt_sep methods_list opt_sep END
                  {
                    YYUSE ($4);
                    YYUSE ($6);

                    if (! ($$ = parser.make_classdef_methods_block
                           ($1, $3, $5, $7, $2)))
                      {
                        // make_classdef_methods_block deleted $3 and $5.
                        YYABORT;
                      }
                  }
                | METHODS stash_comment opt_attr_list opt_sep END
                  {
                    YYUSE ($4);

                    if (! ($$ = parser.make_classdef_methods_block
                           ($1, $3, 0, $5, $2)))
                      {
                        // make_classdef_methods_block deleted $3.
                        YYABORT;
                      }
                  }
                ;
                ;

method_decl1    : identifier
                  {
                    if (! ($$ = parser.start_classdef_external_method ($1, 0)))
                      YYABORT;
                  }
                | identifier param_list
                  {
                    if (! ($$ = parser.start_classdef_external_method ($1, $2)))
                      YYABORT;
                  }
                ;

method_decl     : stash_comment method_decl1
                  { $$ = parser.finish_classdef_external_method ($2, 0, $1); }
                | stash_comment return_list '='
                  {
                    YYUSE ($3);

                    lexer.defining_func++;
                    lexer.parsed_function_name.push (false);
                  }
                  method_decl1
                  {
                    lexer.defining_func--;
                    lexer.parsed_function_name.pop ();
                    $$ = parser.finish_classdef_external_method ($5, $2, $1);
                  }
                ;

method          : method_decl
                  { $$ = $1; }
                | function
                  { $$ = $1; }
                ;

methods_list    : method
                  {
                    octave_value fcn;
                    if ($1)
                      fcn = $1->function ();
                    delete $1;
                    $$ = new tree_classdef_methods_list (fcn);
                  }
                | methods_list opt_sep method
                  {
                    YYUSE ($2);

                    octave_value fcn;
                    if ($3)
                      fcn = $3->function ();
                    delete $3;

                    $1->append (fcn);
                    $$ = $1;
                  }
                ;

events_block    : EVENTS stash_comment opt_attr_list opt_sep events_list opt_sep END
                  {
                    YYUSE ($4);
                    YYUSE ($6);

                    if (! ($$ = parser.make_classdef_events_block
                           ($1, $3, $5, $7, $2)))
                      {
                        // make_classdef_events_block deleted $3 and $5.
                        YYABORT;
                      }
                  }
                | EVENTS stash_comment opt_attr_list opt_sep END
                  {
                    YYUSE ($4);

                    if (! ($$ = parser.make_classdef_events_block
                           ($1, $3, 0, $5, $2)))
                      {
                        // make_classdef_events_block deleted $3.
                        YYABORT;
                      }
                  }
                ;

events_list     : class_event
                  { $$ = new tree_classdef_events_list ($1); }
                | events_list opt_sep class_event
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                ;

class_event     : identifier
                  { $$ = new tree_classdef_event ($1); }
                ;

enum_block      : ENUMERATION stash_comment opt_attr_list opt_sep enum_list opt_sep END
                  {
                    YYUSE ($4);
                    YYUSE ($6);

                    if (! ($$ = parser.make_classdef_enum_block
                           ($1, $3, $5, $7, $2)))
                      {
                        // make_classdef_enum_block deleted $3 and $5.
                        YYABORT;
                      }
                  }
                | ENUMERATION stash_comment opt_attr_list opt_sep END
                  {
                    YYUSE ($4);

                    if (! ($$ = parser.make_classdef_enum_block
                           ($1, $3, 0, $5, $2)))
                      {
                        // make_classdef_enum_block deleted $3.
                        YYABORT;
                      }
                  }
                ;

enum_list       : class_enum
                  { $$ = new tree_classdef_enum_list ($1); }
                | enum_list opt_sep class_enum
                  {
                    YYUSE ($2);

                    $1->append ($3);
                    $$ = $1;
                  }
                ;

class_enum      : identifier '(' expression ')'
                  { $$ = new tree_classdef_enum ($1, $3); }
                ;

// =============
// Miscellaneous
// =============

stmt_begin      : // empty
                  {
                    $$ = 0;
                    lexer.at_beginning_of_statement = true;
                  }
                ;

stash_comment   : // empty
                  { $$ = lexer.get_comment (); }
                ;

parse_error     : LEXICAL_ERROR
                  {
                    $$ = 0;
                    std::string msg = $1->text ();
                    parser.bison_error (msg.c_str ());
                  }
                | error
                  { $$ = 0; }
                ;

sep_no_nl       : ','
                  { $$ = ','; }
                | ';'
                  { $$ = ';'; }
                | sep_no_nl ','
                  { $$ = $1; }
                | sep_no_nl ';'
                  { $$ = $1; }
                ;

opt_sep_no_nl   : // empty
                  { $$ = 0; }
                | sep_no_nl
                  { $$ = $1; }
                ;

opt_nl          : // empty
                  { $$ = 0; }
                | nl
                  { $$ = $1; }
                ;

nl              : '\n'
                  { $$ = '\n'; }
                | nl '\n'
                  { $$ = $1; }
                ;

sep             : ','
                  { $$ = ','; }
                | ';'
                  { $$ = ';'; }
                | '\n'
                  { $$ = '\n'; }
                | sep ','
                  { $$ = $1; }
                | sep ';'
                  { $$ = $1; }
                | sep '\n'
                  { $$ = $1; }
                ;

opt_sep         : // empty
                  { $$ = 0; }
                | sep
                  { $$ = $1; }
                ;

%%

#if defined (HAVE_PRAGMA_GCC_DIAGNOSTIC)
// Restore prevailing warning state for remainder of the file.
#pragma GCC diagnostic pop
#endif

// Generic error messages.

#undef lexer

static void
yyerror (octave::base_parser& parser, const char *s)
{
  parser.bison_error (s);
}

namespace octave
{
  base_parser::base_parser (base_lexer& lxr)
    : endfunction_found (false), autoloading (false),
      fcn_file_from_relative_lookup (false), parsing_subfunctions (false),
      max_fcn_depth (0), curr_fcn_depth (0), primary_fcn_scope (-1),
      curr_class_name (), curr_package_name (), function_scopes (),
      primary_fcn_ptr (0), subfunction_names (), classdef_object (0),
      stmt_list (0), lexer (lxr), parser_state (yypstate_new ())
  { }

  base_parser::~base_parser (void)
  {
    delete stmt_list;

    delete &lexer;

    // FIXME: Deleting the internal Bison parser state structure does
    // not clean up any partial parse trees in the event of an interrupt or
    // error.  It's not clear how to safely do that with the C language
    // parser that Bison generates.  The C++ language parser that Bison
    // generates would do it for us automatically whenever an exception
    // is thrown while parsing input, but there is currently no C++
    // interface for a push parser.

    yypstate_delete (static_cast<yypstate *> (parser_state));
  }

  void
  base_parser::reset (void)
  {
    endfunction_found = false;
    autoloading = false;
    fcn_file_from_relative_lookup = false;
    parsing_subfunctions = false;
    max_fcn_depth = 0;
    curr_fcn_depth = 0;
    primary_fcn_scope = -1;
    curr_class_name = "";
    curr_package_name = "";
    function_scopes.clear ();
    primary_fcn_ptr  = 0;
    subfunction_names.clear ();

    delete stmt_list;
    stmt_list = 0;

    lexer.reset ();

    yypstate_delete (static_cast<yypstate *> (parser_state));
    parser_state = yypstate_new ();
  }
}

// Error mesages for mismatched end tokens.

static std::string
end_token_as_string (token::end_tok_type ettype)
{
  std::string retval = "<unknown>";

  switch (ettype)
    {
    case token::simple_end:
      retval = "end";
      break;

    case token::classdef_end:
      retval = "endclassdef";
      break;

    case token::enumeration_end:
      retval = "endenumeration";
      break;

    case token::events_end:
      retval = "endevents";
      break;

    case token::for_end:
      retval = "endfor";
      break;

    case token::function_end:
      retval = "endfunction";
      break;

    case token::if_end:
      retval = "endif";
      break;

    case token::methods_end:
      retval = "endmethods";
      break;

    case token::parfor_end:
      retval = "endparfor";
      break;

    case token::properties_end:
      retval = "endproperties";
      break;

    case token::switch_end:
      retval = "endswitch";
      break;

    case token::try_catch_end:
      retval = "end_try_catch";
      break;

    case token::unwind_protect_end:
      retval = "end_unwind_protect";
      break;

    case token::while_end:
      retval = "endwhile";
      break;

    default:
      panic_impossible ();
      break;
    }

  return retval;
}

namespace octave
{
  void
  base_parser::end_token_error (token *tok, token::end_tok_type expected)
  {
    std::string msg = ("'" + end_token_as_string (expected)
                       + "' command matched by '"
                       + end_token_as_string (tok->ettype ()) + "'");

    bison_error (msg, tok->line (), tok->column ());
  }

  // Check to see that end tokens are properly matched.

  bool
  base_parser::end_token_ok (token *tok, token::end_tok_type expected)
  {
    token::end_tok_type ettype = tok->ettype ();

    return ettype == expected || ettype == token::simple_end;
  }

  // Maybe print a warning if an assignment expression is used as the
  // test in a logical expression.

  void
  base_parser::maybe_warn_assign_as_truth_value (tree_expression *expr)
  {
    if (expr->is_assignment_expression ()
        && expr->paren_count () < 2)
      {
        if (lexer.fcn_file_full_name.empty ())
          warning_with_id
            ("Octave:assign-as-truth-value",
             "suggest parenthesis around assignment used as truth value");
        else
          warning_with_id
            ("Octave:assign-as-truth-value",
             "suggest parenthesis around assignment used as truth value near line %d, column %d in file '%s'",
             expr->line (), expr->column (), lexer.fcn_file_full_name.c_str ());
      }
  }

  // Maybe print a warning about switch labels that aren't constants.

  void
  base_parser::maybe_warn_variable_switch_label (tree_expression *expr)
  {
    if (! expr->is_constant ())
      {
        if (lexer.fcn_file_full_name.empty ())
          warning_with_id ("Octave:variable-switch-label",
                           "variable switch label");
        else
          warning_with_id
            ("Octave:variable-switch-label",
             "variable switch label near line %d, column %d in file '%s'",
             expr->line (), expr->column (), lexer.fcn_file_full_name.c_str ());
      }
  }

  // Finish building a range.

  tree_expression *
  base_parser::finish_colon_expression (tree_colon_expression *e)
  {
    tree_expression *retval = e;

    octave::unwind_protect frame;

    frame.protect_var (discard_error_messages);
    frame.protect_var (discard_warning_messages);

    discard_error_messages = true;
    discard_warning_messages = true;

    tree_expression *base = e->base ();
    tree_expression *limit = e->limit ();
    tree_expression *incr = e->increment ();

    if (base)
      {
        if (limit)
          {
            if (base->is_constant () && limit->is_constant ()
                && (! incr || (incr && incr->is_constant ())))
              {
                try
                  {
                    octave_value tmp = e->rvalue1 ();

                    tree_constant *tc_retval
                      = new tree_constant (tmp, base->line (), base->column ());

                    std::ostringstream buf;

                    tree_print_code tpc (buf);

                    e->accept (tpc);

                    tc_retval->stash_original_text (buf.str ());

                    delete e;

                    retval = tc_retval;
                  }
                catch (const octave::execution_exception&)
                  {
                    recover_from_exception ();
                  }
              }
          }
        else
          {
            e->preserve_base ();
            delete e;

            retval = base;
          }
      }

    return retval;
  }

  // Make a constant.

  tree_constant *
  base_parser::make_constant (int op, token *tok_val)
  {
    int l = tok_val->line ();
    int c = tok_val->column ();

    tree_constant *retval = 0;

    switch (op)
      {
      case NUM:
        {
          octave_value tmp (tok_val->number ());
          retval = new tree_constant (tmp, l, c);
          retval->stash_original_text (tok_val->text_rep ());
        }
        break;

      case IMAG_NUM:
        {
          octave_value tmp (Complex (0.0, tok_val->number ()));
          retval = new tree_constant (tmp, l, c);
          retval->stash_original_text (tok_val->text_rep ());
        }
        break;

      case DQ_STRING:
      case SQ_STRING:
        {
          std::string txt = tok_val->text ();

          char delim = op == DQ_STRING ? '"' : '\'';
          octave_value tmp (txt, delim);

          if (txt.empty ())
            {
              if (op == DQ_STRING)
                tmp = octave_null_str::instance;
              else
                tmp = octave_null_sq_str::instance;
            }

          retval = new tree_constant (tmp, l, c);

          if (op == DQ_STRING)
            txt = undo_string_escapes (txt);

          // FIXME: maybe this should also be handled by
          // tok_val->text_rep () for character strings?
          retval->stash_original_text (delim + txt + delim);
        }
        break;

      default:
        panic_impossible ();
        break;
      }

    return retval;
  }

  // Make a function handle.

  tree_fcn_handle *
  base_parser::make_fcn_handle (token *tok_val)
  {
    int l = tok_val->line ();
    int c = tok_val->column ();

    tree_fcn_handle *retval = new tree_fcn_handle (tok_val->text (), l, c);

    return retval;
  }

  // Make an anonymous function handle.

  tree_anon_fcn_handle *
  base_parser::make_anon_fcn_handle (tree_parameter_list *param_list,
                                     tree_statement *stmt)
  {
    // FIXME: need to get these from the location of the @ symbol.
    int l = lexer.input_line_number;
    int c = lexer.current_input_column;

    tree_parameter_list *ret_list = 0;

    symbol_table::scope_id fcn_scope = lexer.symtab_context.curr_scope ();

    lexer.symtab_context.pop ();

    stmt->set_print_flag (false);

    tree_statement_list *body = new tree_statement_list (stmt);

    body->mark_as_anon_function_body ();

    tree_anon_fcn_handle *retval
      = new tree_anon_fcn_handle (param_list, ret_list, body, fcn_scope, l, c);
    // FIXME: Stash the filename.  This does not work and produces
    // errors when executed.
    //retval->stash_file_name (lexer.fcn_file_name);

    return retval;
  }

  // Build a binary expression.

  tree_expression *
  base_parser::make_binary_op (int op, tree_expression *op1,
                               token *tok_val, tree_expression *op2)
  {
    octave_value::binary_op t = octave_value::unknown_binary_op;

    switch (op)
      {
      case POW:
        t = octave_value::op_pow;
        break;

      case EPOW:
        t = octave_value::op_el_pow;
        break;

      case '+':
        t = octave_value::op_add;
        break;

      case '-':
        t = octave_value::op_sub;
        break;

      case '*':
        t = octave_value::op_mul;
        break;

      case '/':
        t = octave_value::op_div;
        break;

      case EMUL:
        t = octave_value::op_el_mul;
        break;

      case EDIV:
        t = octave_value::op_el_div;
        break;

      case LEFTDIV:
        t = octave_value::op_ldiv;
        break;

      case ELEFTDIV:
        t = octave_value::op_el_ldiv;
        break;

      case EXPR_LT:
        t = octave_value::op_lt;
        break;

      case EXPR_LE:
        t = octave_value::op_le;
        break;

      case EXPR_EQ:
        t = octave_value::op_eq;
        break;

      case EXPR_GE:
        t = octave_value::op_ge;
        break;

      case EXPR_GT:
        t = octave_value::op_gt;
        break;

      case EXPR_NE:
        t = octave_value::op_ne;
        break;

      case EXPR_AND:
        t = octave_value::op_el_and;
        break;

      case EXPR_OR:
        t = octave_value::op_el_or;
        break;

      default:
        panic_impossible ();
        break;
      }

    int l = tok_val->line ();
    int c = tok_val->column ();

    return maybe_compound_binary_expression (op1, op2, l, c, t);
  }

  // Build a boolean expression.

  tree_expression *
  base_parser::make_boolean_op (int op, tree_expression *op1,
                                token *tok_val, tree_expression *op2)
  {
    tree_boolean_expression::type t;

    switch (op)
      {
      case EXPR_AND_AND:
        t = tree_boolean_expression::bool_and;
        break;

      case EXPR_OR_OR:
        t = tree_boolean_expression::bool_or;
        break;

      default:
        panic_impossible ();
        break;
      }

    int l = tok_val->line ();
    int c = tok_val->column ();

    return new tree_boolean_expression (op1, op2, l, c, t);
  }

  // Build a prefix expression.

  tree_expression *
  base_parser::make_prefix_op (int op, tree_expression *op1, token *tok_val)
  {
    octave_value::unary_op t = octave_value::unknown_unary_op;

    switch (op)
      {
      case EXPR_NOT:
        t = octave_value::op_not;
        break;

      case '+':
        t = octave_value::op_uplus;
        break;

      case '-':
        t = octave_value::op_uminus;
        break;

      case PLUS_PLUS:
        t = octave_value::op_incr;
        break;

      case MINUS_MINUS:
        t = octave_value::op_decr;
        break;

      default:
        panic_impossible ();
        break;
      }

    int l = tok_val->line ();
    int c = tok_val->column ();

    return new tree_prefix_expression (op1, l, c, t);
  }

  // Build a postfix expression.

  tree_expression *
  base_parser::make_postfix_op (int op, tree_expression *op1, token *tok_val)
  {
    octave_value::unary_op t = octave_value::unknown_unary_op;

    switch (op)
      {
      case HERMITIAN:
        t = octave_value::op_hermitian;
        break;

      case TRANSPOSE:
        t = octave_value::op_transpose;
        break;

      case PLUS_PLUS:
        t = octave_value::op_incr;
        break;

      case MINUS_MINUS:
        t = octave_value::op_decr;
        break;

      default:
        panic_impossible ();
        break;
      }

    int l = tok_val->line ();
    int c = tok_val->column ();

    return new tree_postfix_expression (op1, l, c, t);
  }

  // Build an unwind-protect command.

  tree_command *
  base_parser::make_unwind_command (token *unwind_tok,
                                    tree_statement_list *body,
                                    tree_statement_list *cleanup_stmts,
                                    token *end_tok,
                                    octave_comment_list *lc,
                                    octave_comment_list *mc)
  {
    tree_command *retval = 0;

    if (end_token_ok (end_tok, token::unwind_protect_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = unwind_tok->line ();
        int c = unwind_tok->column ();

        retval = new tree_unwind_protect_command (body, cleanup_stmts,
                                                  lc, mc, tc, l, c);
      }
    else
      {
        delete body;
        delete cleanup_stmts;

        end_token_error (end_tok, token::unwind_protect_end);
      }

    return retval;
  }

  // Build a try-catch command.

  tree_command *
  base_parser::make_try_command (token *try_tok,
                                 tree_statement_list *body,
                                 char catch_sep,
                                 tree_statement_list *cleanup_stmts,
                                 token *end_tok,
                                 octave_comment_list *lc,
                                 octave_comment_list *mc)
  {
    tree_command *retval = 0;

    if (end_token_ok (end_tok, token::try_catch_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = try_tok->line ();
        int c = try_tok->column ();

        tree_identifier *id = 0;

        if (! catch_sep && cleanup_stmts && ! cleanup_stmts->empty ())
          {
            tree_statement *stmt = cleanup_stmts->front ();

            if (stmt)
              {
                tree_expression *expr = stmt->expression ();

                if (expr && expr->is_identifier ())
                  {
                    id = dynamic_cast<tree_identifier *> (expr);

                    cleanup_stmts->pop_front ();

                    stmt->set_expression (0);
                    delete stmt;
                  }
              }
          }

        retval = new tree_try_catch_command (body, cleanup_stmts, id,
                                             lc, mc, tc, l, c);
      }
    else
      {
        delete body;
        delete cleanup_stmts;

        end_token_error (end_tok, token::try_catch_end);
      }

    return retval;
  }

  // Build a while command.

  tree_command *
  base_parser::make_while_command (token *while_tok,
                                   tree_expression *expr,
                                   tree_statement_list *body,
                                   token *end_tok,
                                   octave_comment_list *lc)
  {
    tree_command *retval = 0;

    maybe_warn_assign_as_truth_value (expr);

    if (end_token_ok (end_tok, token::while_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        lexer.looping--;

        int l = while_tok->line ();
        int c = while_tok->column ();

        retval = new tree_while_command (expr, body, lc, tc, l, c);
      }
    else
      {
        delete expr;
        delete body;

        end_token_error (end_tok, token::while_end);
      }

    return retval;
  }

  // Build a do-until command.

  tree_command *
  base_parser::make_do_until_command (token *until_tok,
                                      tree_statement_list *body,
                                      tree_expression *expr,
                                      octave_comment_list *lc)
  {
    maybe_warn_assign_as_truth_value (expr);

    octave_comment_list *tc = lexer.comment_buf.get_comment ();

    lexer.looping--;

    int l = until_tok->line ();
    int c = until_tok->column ();

    return new tree_do_until_command (expr, body, lc, tc, l, c);
  }

  // Build a for command.

  tree_command *
  base_parser::make_for_command (int tok_id, token *for_tok,
                                 tree_argument_list *lhs,
                                 tree_expression *expr,
                                 tree_expression *maxproc,
                                 tree_statement_list *body,
                                 token *end_tok,
                                 octave_comment_list *lc)
  {
    tree_command *retval = 0;

    bool parfor = tok_id == PARFOR;

    if (end_token_ok (end_tok, parfor ? token::parfor_end : token::for_end))
      {
        expr->mark_as_for_cmd_expr ();

        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        lexer.looping--;

        int l = for_tok->line ();
        int c = for_tok->column ();

        if (lhs->length () == 1)
          {
            tree_expression *tmp = lhs->remove_front ();

            retval = new tree_simple_for_command (parfor, tmp, expr, maxproc,
                                                  body, lc, tc, l, c);

            delete lhs;
          }
        else
          {
            if (parfor)
              {
                delete lhs;
                delete expr;
                delete maxproc;
                delete body;

                bison_error ("invalid syntax for parfor statement");
              }
            else
              retval = new tree_complex_for_command (lhs, expr, body,
                                                     lc, tc, l, c);
          }
      }
    else
      {
        delete lhs;
        delete expr;
        delete maxproc;
        delete body;

        end_token_error (end_tok, parfor ? token::parfor_end : token::for_end);
      }

    return retval;
  }

  // Build a break command.

  tree_command *
  base_parser::make_break_command (token *break_tok)
  {
    int l = break_tok->line ();
    int c = break_tok->column ();

    return new tree_break_command (l, c);
  }

  // Build a continue command.

  tree_command *
  base_parser::make_continue_command (token *continue_tok)
  {
    int l = continue_tok->line ();
    int c = continue_tok->column ();

    return new tree_continue_command (l, c);
  }

  // Build a return command.

  tree_command *
  base_parser::make_return_command (token *return_tok)
  {
    int l = return_tok->line ();
    int c = return_tok->column ();

    return new tree_return_command (l, c);
  }

  // Start an if command.

  tree_if_command_list *
  base_parser::start_if_command (tree_expression *expr,
                                 tree_statement_list *list)
  {
    maybe_warn_assign_as_truth_value (expr);

    tree_if_clause *t = new tree_if_clause (expr, list);

    return new tree_if_command_list (t);
  }

  // Finish an if command.

  tree_if_command *
  base_parser::finish_if_command (token *if_tok,
                                  tree_if_command_list *list,
                                  token *end_tok,
                                  octave_comment_list *lc)
  {
    tree_if_command *retval = 0;

    if (end_token_ok (end_tok, token::if_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = if_tok->line ();
        int c = if_tok->column ();

        if (list && ! list->empty ())
          {
            tree_if_clause *elt = list->front ();

            if (elt)
              {
                elt->line (l);
                elt->column (c);
              }
          }

        retval = new tree_if_command (list, lc, tc, l, c);
      }
    else
      {
        delete list;

        end_token_error (end_tok, token::if_end);
      }

    return retval;
  }

  // Build an elseif clause.

  tree_if_clause *
  base_parser::make_elseif_clause (token *elseif_tok,
                                   tree_expression *expr,
                                   tree_statement_list *list,
                                   octave_comment_list *lc)
  {
    maybe_warn_assign_as_truth_value (expr);

    int l = elseif_tok->line ();
    int c = elseif_tok->column ();

    return new tree_if_clause (expr, list, lc, l, c);
  }

  // Finish a switch command.

  tree_switch_command *
  base_parser::finish_switch_command (token *switch_tok,
                                      tree_expression *expr,
                                      tree_switch_case_list *list,
                                      token *end_tok,
                                      octave_comment_list *lc)
  {
    tree_switch_command *retval = 0;

    if (end_token_ok (end_tok, token::switch_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = switch_tok->line ();
        int c = switch_tok->column ();

        if (list && ! list->empty ())
          {
            tree_switch_case *elt = list->front ();

            if (elt)
              {
                elt->line (l);
                elt->column (c);
              }
          }

        retval = new tree_switch_command (expr, list, lc, tc, l, c);
      }
    else
      {
        delete expr;
        delete list;

        end_token_error (end_tok, token::switch_end);
      }

    return retval;
  }

  // Build a switch case.

  tree_switch_case *
  base_parser::make_switch_case (token *case_tok,
                                 tree_expression *expr,
                                 tree_statement_list *list,
                                 octave_comment_list *lc)
  {
    maybe_warn_variable_switch_label (expr);

    int l = case_tok->line ();
    int c = case_tok->column ();

    return new tree_switch_case (expr, list, lc, l, c);
  }

  // Build an assignment to a variable.

  tree_expression *
  base_parser::make_assign_op (int op, tree_argument_list *lhs,
                               token *eq_tok, tree_expression *rhs)
  {
    octave_value::assign_op t = octave_value::unknown_assign_op;

    switch (op)
      {
      case '=':
        t = octave_value::op_asn_eq;
        break;

      case ADD_EQ:
        t = octave_value::op_add_eq;
        break;

      case SUB_EQ:
        t = octave_value::op_sub_eq;
        break;

      case MUL_EQ:
        t = octave_value::op_mul_eq;
        break;

      case DIV_EQ:
        t = octave_value::op_div_eq;
        break;

      case LEFTDIV_EQ:
        t = octave_value::op_ldiv_eq;
        break;

      case POW_EQ:
        t = octave_value::op_pow_eq;
        break;

      case EMUL_EQ:
        t = octave_value::op_el_mul_eq;
        break;

      case EDIV_EQ:
        t = octave_value::op_el_div_eq;
        break;

      case ELEFTDIV_EQ:
        t = octave_value::op_el_ldiv_eq;
        break;

      case EPOW_EQ:
        t = octave_value::op_el_pow_eq;
        break;

      case AND_EQ:
        t = octave_value::op_el_and_eq;
        break;

      case OR_EQ:
        t = octave_value::op_el_or_eq;
        break;

      default:
        panic_impossible ();
        break;
      }

    int l = eq_tok->line ();
    int c = eq_tok->column ();

    if (! lhs->is_simple_assign_lhs () && t != octave_value::op_asn_eq)
      {
        // Multiple assignments like [x,y] OP= rhs are only valid for
        // '=', not '+=', etc.

        delete lhs;
        delete rhs;

        bison_error ("computed multiple assignment not allowed", l, c);

        return 0;
      }

    if (lhs->is_simple_assign_lhs ())
      {
        // We are looking at a simple assignment statement like x = rhs;

        tree_expression *tmp = lhs->remove_front ();

        if ((tmp->is_identifier () || tmp->is_index_expression ())
            && is_keyword (tmp->name ()))
          {
            std::string kw = tmp->name ();

            delete tmp;
            delete lhs;
            delete rhs;

            bison_error ("invalid assignment to keyword \"" + kw + "\"", l, c);

            return 0;
          }

        delete lhs;

        return new tree_simple_assignment (tmp, rhs, false, l, c, t);
      }
    else
      {
        std::list<std::string> names = lhs->variable_names ();

        for (std::list<std::string>::const_iterator it = names.begin ();
             it != names.end (); it++)
          {
            std::string kw = *it;

            if (is_keyword (kw))
              {
                delete lhs;
                delete rhs;

                bison_error ("invalid assignment to keyword \"" + kw + "\"",
                             l, c);

                return 0;
              }
          }

        return new tree_multi_assignment (lhs, rhs, false, l, c);
      }
  }

  // Define a script.

  void
  base_parser::make_script (tree_statement_list *cmds,
                            tree_statement *end_script)
  {
    if (! cmds)
      cmds = new tree_statement_list ();

    cmds->append (end_script);

    octave_user_script *script
      = new octave_user_script (lexer.fcn_file_full_name,
                                lexer.fcn_file_name,
                                cmds, lexer.help_text);

    lexer.help_text = "";

    octave::sys::time now;

    script->stash_fcn_file_time (now);

    primary_fcn_ptr = script;
  }

  // Begin defining a function.

  octave_user_function *
  base_parser::start_function (tree_parameter_list *param_list,
                               tree_statement_list *body,
                               tree_statement *end_fcn_stmt)
  {
    // We'll fill in the return list later.

    if (! body)
      body = new tree_statement_list ();

    body->append (end_fcn_stmt);

    octave_user_function *fcn
      = new octave_user_function (lexer.symtab_context.curr_scope (),
                                  param_list, 0, body);

    if (fcn)
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        fcn->stash_trailing_comment (tc);
        fcn->stash_fcn_end_location (end_fcn_stmt->line (),
                                     end_fcn_stmt->column ());
      }

    return fcn;
  }

  tree_statement *
  base_parser::make_end (const std::string& type, bool eof, int l, int c)
  {
    return make_statement (new tree_no_op_command (type, eof, l, c));
  }

  // Do most of the work for defining a function.

  octave_user_function *
  base_parser::frob_function (const std::string& fname,
                              octave_user_function *fcn)
  {
    std::string id_name = fname;

    // If input is coming from a file, issue a warning if the name of
    // the file does not match the name of the function stated in the
    // file.  Matlab doesn't provide a diagnostic (it ignores the stated
    // name).
    if (! autoloading && lexer.reading_fcn_file
        && curr_fcn_depth == 1 && ! parsing_subfunctions)
      {
        // FIXME: should lexer.fcn_file_name already be
        // preprocessed when we get here?  It seems to only be a
        // problem with relative filenames.

        std::string nm = lexer.fcn_file_name;

        size_t pos = nm.find_last_of (octave::sys::file_ops::dir_sep_chars ());

        if (pos != std::string::npos)
          nm = lexer.fcn_file_name.substr (pos+1);

        if (nm != id_name)
          {
            warning_with_id
              ("Octave:function-name-clash",
               "function name '%s' does not agree with function filename '%s'",
               id_name.c_str (), lexer.fcn_file_full_name.c_str ());

            id_name = nm;
          }
      }

    if (lexer.reading_fcn_file || lexer.reading_classdef_file || autoloading)
      {
        octave::sys::time now;

        fcn->stash_fcn_file_name (lexer.fcn_file_full_name);
        fcn->stash_fcn_file_time (now);
        fcn->mark_as_system_fcn_file ();

        if (fcn_file_from_relative_lookup)
          fcn->mark_relative ();

        if (curr_fcn_depth > 1 || parsing_subfunctions)
          {
            fcn->stash_parent_fcn_name (lexer.fcn_file_name);

            if (curr_fcn_depth > 1)
              fcn->stash_parent_fcn_scope (function_scopes[function_scopes.size ()-2]);
            else
              fcn->stash_parent_fcn_scope (primary_fcn_scope);
          }

        if (lexer.parsing_class_method)
          {
            if (curr_class_name == id_name)
              fcn->mark_as_class_constructor ();
            else
              fcn->mark_as_class_method ();

            fcn->stash_dispatch_class (curr_class_name);
          }

        std::string nm = fcn->fcn_file_name ();

        octave::sys::file_stat fs (nm);

        if (fs && fs.is_newer (now))
          warning_with_id ("Octave:future-time-stamp",
                           "time stamp for '%s' is in the future", nm.c_str ());
      }
    else if (! input_from_tmp_history_file
             && ! lexer.force_script
             && lexer.reading_script_file
             && lexer.fcn_file_name == id_name)
      {
        warning ("function '%s' defined within script file '%s'",
                 id_name.c_str (), lexer.fcn_file_full_name.c_str ());
      }

    fcn->stash_function_name (id_name);

    if (! lexer.help_text.empty () && curr_fcn_depth == 1
        && ! parsing_subfunctions)
      {
        fcn->document (lexer.help_text);

        lexer.help_text = "";
      }

    if (lexer.reading_fcn_file && curr_fcn_depth == 1
        && ! parsing_subfunctions)
      primary_fcn_ptr = fcn;

    return fcn;
  }

  tree_function_def *
  base_parser::finish_function (tree_parameter_list *ret_list,
                                octave_user_function *fcn,
                                octave_comment_list *lc,
                                int l, int c)
  {
    tree_function_def *retval = 0;

    if (ret_list)
      ret_list->mark_as_formal_parameters ();

    if (fcn)
      {
        std::string nm = fcn->name ();
        std::string file = fcn->fcn_file_name ();

        std::string tmp = nm;
        if (! file.empty ())
          tmp += ": " + file;

        symbol_table::cache_name (fcn->scope (), tmp);

        if (lc)
          fcn->stash_leading_comment (lc);

        fcn->define_ret_list (ret_list);

        if (curr_fcn_depth > 1 || parsing_subfunctions)
          {
            fcn->mark_as_subfunction ();
            fcn->stash_fcn_location (l, c);

            subfunction_names.push_back (nm);

            if (endfunction_found && function_scopes.size () > 1)
              {
                symbol_table::scope_id pscope
                  = function_scopes[function_scopes.size ()-2];

                symbol_table::install_nestfunction (nm, octave_value (fcn),
                                                    pscope);
              }
            else
              symbol_table::install_subfunction (nm, octave_value (fcn),
                                                 primary_fcn_scope);
          }

        if (curr_fcn_depth == 1 && fcn)
          symbol_table::update_nest (fcn->scope ());

        if (! lexer.reading_fcn_file && curr_fcn_depth == 1)
          {
            // We are either reading a script file or defining a function
            // at the command line, so this definition creates a
            // tree_function object that is placed in the parse tree.
            // Otherwise, it is just inserted in the symbol table,
            // either as a subfunction or nested function (see above),
            // or as the primary function for the file, via
            // primary_fcn_ptr (see also load_fcn_from_file,,
            // parse_fcn_file, and
            // symbol_table::fcn_info::fcn_info_rep::find_user_function).

            retval = new tree_function_def (fcn);
          }
      }

    return retval;
  }

  void
  base_parser::recover_from_parsing_function (void)
  {
    lexer.symtab_context.pop ();

    if (lexer.reading_fcn_file && curr_fcn_depth == 1
        && ! parsing_subfunctions)
      parsing_subfunctions = true;

    curr_fcn_depth--;
    function_scopes.pop_back ();

    lexer.defining_func--;
    lexer.parsed_function_name.pop ();
    lexer.looking_at_return_list = false;
    lexer.looking_at_parameter_list = false;
  }

  tree_funcall *
  base_parser::make_superclass_ref (const std::string& method_nm,
                                    const std::string& class_nm)
  {
    octave_value_list args;

    args(1) = class_nm;
    args(0) = method_nm;

    octave_value fcn
      = symbol_table::find_built_in_function ("__superclass_reference__");

    return new tree_funcall (fcn, args);
  }

  tree_funcall *
  base_parser::make_meta_class_query (const std::string& class_nm)
  {
    octave_value_list args;

    args(0) = class_nm;

    octave_value fcn
      = symbol_table::find_built_in_function ("__meta_class_query__");

    return new tree_funcall (fcn, args);
  }

  // A CLASSDEF block defines a class that has a constructor and other
  // methods, but it is not an executable command.  Parsing the block
  // makes some changes in the symbol table (inserting the constructor
  // and methods, and adding to the list of known objects) and creates
  // a parse tree containing meta information about the class.

  tree_classdef *
  base_parser::make_classdef (token *tok_val,
                              tree_classdef_attribute_list *a,
                              tree_identifier *id,
                              tree_classdef_superclass_list *sc,
                              tree_classdef_body *body, token *end_tok,
                              octave_comment_list *lc)
  {
    tree_classdef *retval = 0;

    std::string cls_name = id->name ();

    std::string nm = lexer.fcn_file_name;

    size_t pos = nm.find_last_of (octave::sys::file_ops::dir_sep_chars ());

    if (pos != std::string::npos)
      nm = lexer.fcn_file_name.substr (pos+1);

    if (nm != cls_name)
      {
        delete a;
        delete id;
        delete sc;
        delete body;

        bison_error ("invalid classdef definition, the class name must match the filename");

      }
    else
      {
        if (end_token_ok (end_tok, token::classdef_end))
          {
            octave_comment_list *tc = lexer.comment_buf.get_comment ();

            int l = tok_val->line ();
            int c = tok_val->column ();

            if (! body)
              body = new tree_classdef_body ();

            retval = new tree_classdef (a, id, sc, body, lc, tc,
                                        curr_package_name, l, c);
          }
        else
          {
            delete a;
            delete id;
            delete sc;
            delete body;

            end_token_error (end_tok, token::switch_end);
          }
      }

    return retval;
  }

  tree_classdef_properties_block *
  base_parser::make_classdef_properties_block (token *tok_val,
                                               tree_classdef_attribute_list *a,
                                               tree_classdef_property_list *plist,
                                               token *end_tok,
                                               octave_comment_list *lc)
  {
    tree_classdef_properties_block *retval = 0;

    if (end_token_ok (end_tok, token::properties_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = tok_val->line ();
        int c = tok_val->column ();

        if (! plist)
          plist = new tree_classdef_property_list ();

        retval = new tree_classdef_properties_block (a, plist, lc, tc, l, c);
      }
    else
      {
        delete a;
        delete plist;

        end_token_error (end_tok, token::properties_end);
      }

    return retval;
  }

  tree_classdef_methods_block *
  base_parser::make_classdef_methods_block (token *tok_val,
                                            tree_classdef_attribute_list *a,
                                            tree_classdef_methods_list *mlist,
                                            token *end_tok,
                                            octave_comment_list *lc)
  {
    tree_classdef_methods_block *retval = 0;

    if (end_token_ok (end_tok, token::methods_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = tok_val->line ();
        int c = tok_val->column ();

        if (! mlist)
          mlist = new tree_classdef_methods_list ();

        retval = new tree_classdef_methods_block (a, mlist, lc, tc, l, c);
      }
    else
      {
        delete a;
        delete mlist;

        end_token_error (end_tok, token::methods_end);
      }

    return retval;
  }

  tree_classdef_events_block *
  base_parser::make_classdef_events_block (token *tok_val,
                                           tree_classdef_attribute_list *a,
                                           tree_classdef_events_list *elist,
                                           token *end_tok,
                                           octave_comment_list *lc)
  {
    tree_classdef_events_block *retval = 0;

    if (end_token_ok (end_tok, token::events_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = tok_val->line ();
        int c = tok_val->column ();

        if (! elist)
          elist = new tree_classdef_events_list ();

        retval = new tree_classdef_events_block (a, elist, lc, tc, l, c);
      }
    else
      {
        delete a;
        delete elist;

        end_token_error (end_tok, token::events_end);
      }

    return retval;
  }

  tree_classdef_enum_block *
  base_parser::make_classdef_enum_block (token *tok_val,
                                         tree_classdef_attribute_list *a,
                                         tree_classdef_enum_list *elist,
                                         token *end_tok,
                                         octave_comment_list *lc)
  {
    tree_classdef_enum_block *retval = 0;

    if (end_token_ok (end_tok, token::enumeration_end))
      {
        octave_comment_list *tc = lexer.comment_buf.get_comment ();

        int l = tok_val->line ();
        int c = tok_val->column ();

        if (! elist)
          elist = new tree_classdef_enum_list ();

        retval = new tree_classdef_enum_block (a, elist, lc, tc, l, c);
      }
    else
      {
        delete a;
        delete elist;

        end_token_error (end_tok, token::enumeration_end);
      }

    return retval;
  }

  octave_user_function*
  base_parser::start_classdef_external_method (tree_identifier *id,
                                               tree_parameter_list *pl)
  {
    octave_user_function* retval = 0;

    // External methods are only allowed within @-folders. In this case,
    // curr_class_name will be non-empty.

    if (! curr_class_name.empty ())
      {

        std::string mname = id->name ();

        // Methods that cannot be declared outside the classdef file:
        // - methods with '.' character (e.g. property accessors)
        // - class constructor
        // - `delete'

        if (mname.find_first_of (".") == std::string::npos
            && mname != "delete"
            && mname != curr_class_name)
          {
            // Create a dummy function that is used until the real method
            // is loaded.

            retval = new octave_user_function (-1, pl);

            retval->stash_function_name (mname);

            int l = id->line ();
            int c = id->column ();

            retval->stash_fcn_location (l, c);
          }
        else
          bison_error ("invalid external method declaration, an external "
                       "method cannot be the class constructor, `delete' "
                       "or have a dot (.) character in its name");
      }
    else
      bison_error ("external methods are only allowed in @-folders");

    if (! retval)
      delete id;

    return retval;
  }

  tree_function_def *
  base_parser::finish_classdef_external_method (octave_user_function *fcn,
                                                tree_parameter_list *ret_list,
                                                octave_comment_list *cl)
  {
    if (ret_list)
      fcn->define_ret_list (ret_list);

    if (cl)
      fcn->stash_leading_comment (cl);

    int l = fcn->beginning_line ();
    int c = fcn->beginning_column ();

    return new tree_function_def (fcn, l, c);
  }

  // Make an index expression.

  tree_index_expression *
  base_parser::make_index_expression (tree_expression *expr,
                                      tree_argument_list *args,
                                      char type)
  {
    tree_index_expression *retval = 0;

    if (args && args->has_magic_tilde ())
      {
        delete expr;
        delete args;

        bison_error ("invalid use of empty argument (~) in index expression");
      }
    else
      {
        int l = expr->line ();
        int c = expr->column ();

        if (! expr->is_postfix_indexed ())
          expr->set_postfix_index (type);

        if (expr->is_index_expression ())
          {
            tree_index_expression *tmp =
              static_cast<tree_index_expression *> (expr);

            tmp->append (args, type);

            retval = tmp;
          }
        else
          retval = new tree_index_expression (expr, args, l, c, type);
      }

    return retval;
  }

  // Make an indirect reference expression.

  tree_index_expression *
  base_parser::make_indirect_ref (tree_expression *expr,
                                  const std::string& elt)
  {
    tree_index_expression *retval = 0;

    int l = expr->line ();
    int c = expr->column ();

    if (! expr->is_postfix_indexed ())
      expr->set_postfix_index ('.');

    if (expr->is_index_expression ())
      {
        tree_index_expression *tmp = static_cast<tree_index_expression *> (expr);

        tmp->append (elt);

        retval = tmp;
      }
    else
      retval = new tree_index_expression (expr, elt, l, c);

    lexer.looking_at_indirect_ref = false;

    return retval;
  }

  // Make an indirect reference expression with dynamic field name.

  tree_index_expression *
  base_parser::make_indirect_ref (tree_expression *expr,
                                  tree_expression *elt)
  {
    tree_index_expression *retval = 0;

    int l = expr->line ();
    int c = expr->column ();

    if (! expr->is_postfix_indexed ())
      expr->set_postfix_index ('.');

    if (expr->is_index_expression ())
      {
        tree_index_expression *tmp = static_cast<tree_index_expression *> (expr);

        tmp->append (elt);

        retval = tmp;
      }
    else
      retval = new tree_index_expression (expr, elt, l, c);

    lexer.looking_at_indirect_ref = false;

    return retval;
  }

  // Make a declaration command.

  tree_decl_command *
  base_parser::make_decl_command (int tok, token *tok_val,
                                  tree_decl_init_list *lst)
  {
    tree_decl_command *retval = 0;

    int l = tok_val->line ();
    int c = tok_val->column ();

    switch (tok)
      {
      case GLOBAL:
        retval = new tree_global_command (lst, l, c);
        break;

      case PERSISTENT:
        if (curr_fcn_depth > 0)
          retval = new tree_persistent_command (lst, l, c);
        else
          {
            if (lexer.reading_script_file)
              warning ("ignoring persistent declaration near line %d of file '%s'",
                       l, lexer.fcn_file_full_name.c_str ());
            else
              warning ("ignoring persistent declaration near line %d", l);
          }
        break;

      default:
        panic_impossible ();
        break;
      }

    return retval;
  }

  bool
  base_parser::validate_array_list (tree_expression *e)
  {
    bool retval = true;

    tree_array_list *al = dynamic_cast<tree_array_list *> (e);

    for (tree_array_list::iterator i = al->begin (); i != al->end (); i++)
      {
        tree_argument_list *row = *i;

        if (row && row->has_magic_tilde ())
          {
            retval = false;

            if (e->is_matrix ())
              bison_error ("invalid use of tilde (~) in matrix expression");
            else
              bison_error ("invalid use of tilde (~) in cell expression");

            break;
          }
      }

    return retval;
  }

  tree_argument_list *
  base_parser::validate_matrix_for_assignment (tree_expression *e)
  {
    tree_argument_list *retval = 0;

    if (e->is_constant ())
      {
        octave_value ov = e->rvalue1 ();

        delete e;

        if (ov.is_empty ())
          bison_error ("invalid empty left hand side of assignment");
        else
          bison_error ("invalid constant left hand side of assignment");
      }
    else
      {
        bool is_simple_assign = true;

        tree_argument_list *tmp = 0;

        if (e->is_matrix ())
          {
            tree_matrix *mat = dynamic_cast<tree_matrix *> (e);

            if (mat && mat->size () == 1)
              {
                tmp = mat->front ();
                mat->pop_front ();
                delete e;
                is_simple_assign = false;
              }
          }
        else
          tmp = new tree_argument_list (e);

        if (tmp && tmp->is_valid_lvalue_list ())
          {
            lexer.mark_as_variables (tmp->variable_names ());
            retval = tmp;
          }
        else
          {
            delete tmp;

            bison_error ("invalid left hand side of assignment");
          }

        if (retval && is_simple_assign)
          retval->mark_as_simple_assign_lhs ();
      }

    return retval;
  }

  // Finish building an array_list.

  tree_expression *
  base_parser::finish_array_list (tree_array_list *array_list)
  {
    tree_expression *retval = array_list;

    octave::unwind_protect frame;

    frame.protect_var (discard_error_messages);
    frame.protect_var (discard_warning_messages);

    discard_error_messages = true;
    discard_warning_messages = true;

    if (array_list->all_elements_are_constant ())
      {
        try
          {
            octave_value tmp = array_list->rvalue1 ();

            tree_constant *tc_retval
              = new tree_constant (tmp, array_list->line (),
                                   array_list->column ());

            std::ostringstream buf;

            tree_print_code tpc (buf);

            array_list->accept (tpc);

            tc_retval->stash_original_text (buf.str ());

            delete array_list;

            retval = tc_retval;
          }
        catch (const octave::execution_exception&)
          {
            recover_from_exception ();
          }
      }

    return retval;
  }

  // Finish building a matrix list.

  tree_expression *
  base_parser::finish_matrix (tree_matrix *m)
  {
    return (m
            ? finish_array_list (m)
            : new tree_constant (octave_null_matrix::instance));
  }

  // Finish building a cell list.

  tree_expression *
  base_parser::finish_cell (tree_cell *c)
  {
    return (c
            ? finish_array_list (c)
            : new tree_constant (octave_value (Cell ())));
  }

  void
  base_parser::maybe_warn_missing_semi (tree_statement_list *t)
  {
    if (curr_fcn_depth > 0)
      {
        tree_statement *tmp = t->back ();

        if (tmp->is_expression ())
          warning_with_id
            ("Octave:missing-semicolon",
             "missing semicolon near line %d, column %d in file '%s'",
             tmp->line (), tmp->column (), lexer.fcn_file_full_name.c_str ());
      }
  }

  tree_statement_list *
  base_parser::set_stmt_print_flag (tree_statement_list *list,
                                    char sep, bool warn_missing_semi)
  {
    tree_statement *tmp = list->back ();

    switch (sep)
      {
      case ';':
        tmp->set_print_flag (false);
        break;

      case 0:
      case ',':
      case '\n':
        tmp->set_print_flag (true);
        if (warn_missing_semi)
          maybe_warn_missing_semi (list);
        break;

      default:
        warning ("unrecognized separator type!");
        break;
      }

    // Even if a statement is null, we add it to the list then remove it
    // here so that the print flag is applied to the correct statement.

    if (tmp->is_null_statement ())
      {
        list->pop_back ();
        delete tmp;
      }

    return list;
  }

  // Finish building a statement.
  template <typename T>
  tree_statement *
  base_parser::make_statement (T *arg)
  {
    octave_comment_list *comment = lexer.get_comment ();

    return new tree_statement (arg, comment);
  }

  tree_statement_list *
  base_parser::make_statement_list (tree_statement *stmt)
  {
    return new tree_statement_list (stmt);
  }

  tree_statement_list *
  base_parser::append_statement_list (tree_statement_list *list,
                                      char sep, tree_statement *stmt,
                                      bool warn_missing_semi)
  {
    set_stmt_print_flag (list, sep, warn_missing_semi);

    list->append (stmt);

    return list;
  }

  void
  base_parser::bison_error (const std::string& str, int l, int c)
  {
    int err_line = l < 0 ? lexer.input_line_number : l;
    int err_col = c < 0 ? lexer.current_input_column - 1 : c;

    std::ostringstream output_buf;

    if (lexer.reading_fcn_file || lexer.reading_script_file
        || lexer.reading_classdef_file)
      output_buf << "parse error near line " << err_line
                 << " of file " << lexer.fcn_file_full_name;
    else
      output_buf << "parse error:";

    if (str != "parse error")
      output_buf << "\n\n  " << str;

    output_buf << "\n\n";

    std::string curr_line = lexer.current_input_line;

    if (! curr_line.empty ())
      {
        size_t len = curr_line.length ();

        if (curr_line[len-1] == '\n')
          curr_line.resize (len-1);

        // Print the line, maybe with a pointer near the error token.

        output_buf << ">>> " << curr_line << "\n";

        if (err_col == 0)
          err_col = len;

        for (int i = 0; i < err_col + 3; i++)
          output_buf << " ";

        output_buf << "^";
      }

    output_buf << "\n";

    parse_error_msg = output_buf.str ();
  }

  int
  parser::run (void)
  {
    int status = -1;

    yypstate *pstate = static_cast<yypstate *> (parser_state);

    try
      {
        status = octave_pull_parse (pstate, *this);
      }
    catch (octave::execution_exception& e)
      {
        std::string file = lexer.fcn_file_full_name;

        if (file.empty ())
          error (e, "parse error");
        else
          error (e, "parse error in %s", file.c_str ());
      }
    catch (octave::interrupt_exception &)
      {
        throw;
      }
    catch (...)
      {
        std::string file = lexer.fcn_file_full_name;

        if (file.empty ())
          error ("unexpected exception while parsing input");
        else
          error ("unexpected exception while parsing %s", file.c_str ());
      }

    if (status != 0)
      parse_error ("%s", parse_error_msg.c_str ());

    return status;
  }

  // Parse input from INPUT.  Pass TRUE for EOF if the end of INPUT should
  // finish the parse.

  int
  push_parser::run (const std::string& input, bool eof)
  {
    int status = -1;

    dynamic_cast<push_lexer&> (lexer).append_input (input, eof);

    do
      {
        YYSTYPE lval;

        int token = octave_lex (&lval, scanner);

        if (token < 0)
          {
            if (! eof && lexer.at_end_of_buffer ())
              {
                status = -1;
                break;
              }
          }

        yypstate *pstate = static_cast<yypstate *> (parser_state);

        try
          {
            status = octave_push_parse (pstate, token, &lval, *this);
          }
        catch (octave::execution_exception& e)
          {
            std::string file = lexer.fcn_file_full_name;

            if (file.empty ())
              error (e, "parse error");
            else
              error (e, "parse error in %s", file.c_str ());
          }
        catch (octave::interrupt_exception &)
          {
            throw;
          }
        catch (...)
          {
            std::string file = lexer.fcn_file_full_name;

            if (file.empty ())
              error ("unexpected exception while parsing input");
            else
              error ("unexpected exception while parsing %s", file.c_str ());
          }
      }
    while (status == YYPUSH_MORE);

    if (status != 0)
      parse_error ("%s", parse_error_msg.c_str ());

    return status;
  }
}

static void
safe_fclose (FILE *f)
{
  if (f)
    fclose (static_cast<FILE *> (f));
}

static octave_function *
parse_fcn_file (const std::string& full_file, const std::string& file,
                const std::string& dispatch_type,
                const std::string& package_name,
                bool require_file, bool force_script, bool autoload,
                bool relative_lookup, const std::string& warn_for)
{
  octave::unwind_protect frame;

  octave_function *fcn_ptr = 0;

  // Open function file and parse.

  FILE *in_stream = octave::command_editor::get_input_stream ();

  frame.add_fcn (octave::command_editor::set_input_stream, in_stream);

  frame.add_fcn (octave::command_history::ignore_entries,
                 octave::command_history::ignoring_entries ());

  octave::command_history::ignore_entries ();

  FILE *ffile = 0;

  if (! full_file.empty ())
    ffile = std::fopen (full_file.c_str (), "rb");

  if (ffile)
    {
      frame.add_fcn (safe_fclose, ffile);

      // octave::base_parser constructor sets this for us.
      frame.protect_var (LEXER);

      octave::parser parser (ffile);

      parser.curr_class_name = dispatch_type;
      parser.curr_package_name = package_name;
      parser.autoloading = autoload;
      parser.fcn_file_from_relative_lookup = relative_lookup;

      parser.lexer.force_script = force_script;
      parser.lexer.prep_for_file ();
      parser.lexer.parsing_class_method = ! dispatch_type.empty ();

      parser.lexer.fcn_file_name = file;
      parser.lexer.fcn_file_full_name = full_file;

      int status = parser.run ();

      fcn_ptr = parser.primary_fcn_ptr;

      if (status == 0)
        {
          if (parser.lexer.reading_classdef_file
              && parser.classdef_object)
            {
              // Convert parse tree for classdef object to
              // meta.class info (and stash it in the symbol
              // table?).  Return pointer to constructor?

              if (fcn_ptr)
                panic_impossible ();

              bool is_at_folder = ! dispatch_type.empty ();

              fcn_ptr =
                parser.classdef_object->make_meta_class (is_at_folder);

              delete (parser.classdef_object);

              parser.classdef_object = 0;
            }
          else if (fcn_ptr)
            {
              fcn_ptr->maybe_relocate_end ();

              if (parser.parsing_subfunctions)
                {
                  if (! parser.endfunction_found)
                    parser.subfunction_names.reverse ();

                  fcn_ptr->stash_subfunction_names (parser.subfunction_names);
                }
            }
        }
      else
        error ("parse error while reading file %s", full_file.c_str ());
    }
  else if (require_file)
    error ("no such file, '%s'", full_file.c_str ());
  else if (! warn_for.empty ())
    error ("%s: unable to open file '%s'", warn_for.c_str (),
           full_file.c_str ());

  return fcn_ptr;
}

std::string
get_help_from_file (const std::string& nm, bool& symbol_found,
                    std::string& full_file)
{
  std::string retval;

  full_file = fcn_file_in_path (nm);

  std::string file = full_file;

  size_t file_len = file.length ();

  if ((file_len > 4 && file.substr (file_len-4) == ".oct")
      || (file_len > 4 && file.substr (file_len-4) == ".mex")
      || (file_len > 2 && file.substr (file_len-2) == ".m"))
    {
      file = octave::sys::env::base_pathname (file);
      file = file.substr (0, file.find_last_of ('.'));

      size_t pos = file.find_last_of (octave::sys::file_ops::dir_sep_str ());
      if (pos != std::string::npos)
        file = file.substr (pos+1);
    }

  if (! file.empty ())
    {
      symbol_found = true;

      octave_function *fcn
        = parse_fcn_file (full_file, file, "", "", true, false, false, false,
                          "");

      if (fcn)
        {
          retval = fcn->doc_string ();

          delete fcn;
        }
    }

  return retval;
}

std::string
get_help_from_file (const std::string& nm, bool& symbol_found)
{
  std::string file;
  return get_help_from_file (nm, symbol_found, file);
}

std::string
lookup_autoload (const std::string& nm)
{
  std::string retval;

  typedef std::map<std::string, std::string>::const_iterator am_iter;

  am_iter p = autoload_map.find (nm);

  if (p != autoload_map.end ())
    retval = load_path::find_file (p->second);

  return retval;
}

string_vector
autoloaded_functions (void)
{
  string_vector names (autoload_map.size ());

  octave_idx_type i = 0;
  typedef std::map<std::string, std::string>::const_iterator am_iter;
  for (am_iter p = autoload_map.begin (); p != autoload_map.end (); p++)
    names[i++] = p->first;

  return names;
}

string_vector
reverse_lookup_autoload (const std::string& nm)
{
  string_vector names;

  typedef std::map<std::string, std::string>::const_iterator am_iter;
  for (am_iter p = autoload_map.begin (); p != autoload_map.end (); p++)
    if (nm == p->second)
      names.append (p->first);

  return names;
}

octave_function *
load_fcn_from_file (const std::string& file_name, const std::string& dir_name,
                    const std::string& dispatch_type,
                    const std::string& package_name,
                    const std::string& fcn_name, bool autoload)
{
  octave_function *retval = 0;

  octave::unwind_protect frame;

  std::string nm = file_name;

  size_t nm_len = nm.length ();

  std::string file;

  bool relative_lookup = false;

  file = nm;

  if ((nm_len > 4 && nm.substr (nm_len-4) == ".oct")
      || (nm_len > 4 && nm.substr (nm_len-4) == ".mex")
      || (nm_len > 2 && nm.substr (nm_len-2) == ".m"))
    {
      nm = octave::sys::env::base_pathname (file);
      nm = nm.substr (0, nm.find_last_of ('.'));

      size_t pos = nm.find_last_of (octave::sys::file_ops::dir_sep_str ());
      if (pos != std::string::npos)
        nm = nm.substr (pos+1);
    }

  relative_lookup = ! octave::sys::env::absolute_pathname (file);

  file = octave::sys::env::make_absolute (file);

  int len = file.length ();

  if (len > 4 && file.substr (len-4, len-1) == ".oct")
    {
      if (autoload && ! fcn_name.empty ())
        nm = fcn_name;

      retval = octave_dynamic_loader::load_oct (nm, file, relative_lookup);
    }
  else if (len > 4 && file.substr (len-4, len-1) == ".mex")
    {
      // Temporarily load m-file version of mex-file, if it exists,
      // to get the help-string to use.

      octave_function *tmpfcn = parse_fcn_file (file.substr (0, len - 2),
                                                nm, dispatch_type,
                                                package_name, false,
                                                autoload, autoload,
                                                relative_lookup, "");

      retval = octave_dynamic_loader::load_mex (nm, file, relative_lookup);

      if (tmpfcn)
        retval->document (tmpfcn->doc_string ());
      delete tmpfcn;
    }
  else if (len > 2)
    {
      retval = parse_fcn_file (file, nm, dispatch_type, package_name, true,
                               autoload, autoload, relative_lookup, "");
    }

  if (retval)
    {
      retval->stash_dir_name (dir_name);
      retval->stash_package_name (package_name);

      if (retval->is_user_function ())
        {
          symbol_table::scope_id id = retval->scope ();

          symbol_table::stash_dir_name_for_subfunctions (id, dir_name);
        }
    }

  return retval;
}

DEFUN (autoload, args, ,
       doc: /* -*- texinfo -*-
@deftypefn  {} {@var{autoload_map} =} autoload ()
@deftypefnx {} {} autoload (@var{function}, @var{file})
@deftypefnx {} {} autoload (@dots{}, "remove")
Define @var{function} to autoload from @var{file}.

The second argument, @var{file}, should be an absolute filename or a file
name in the same directory as the function or script from which the autoload
command was run.  @var{file} @emph{should not} depend on the Octave load
path.

Normally, calls to @code{autoload} appear in PKG_ADD script files that are
evaluated when a directory is added to Octave's load path.  To avoid having
to hardcode directory names in @var{file}, if @var{file} is in the same
directory as the PKG_ADD script then

@example
autoload ("foo", "bar.oct");
@end example

@noindent
will load the function @code{foo} from the file @code{bar.oct}.  The above
usage when @code{bar.oct} is not in the same directory, or usages such as

@example
autoload ("foo", file_in_loadpath ("bar.oct"))
@end example

@noindent
are strongly discouraged, as their behavior may be unpredictable.

With no arguments, return a structure containing the current autoload map.

If a third argument @qcode{"remove"} is given, the function is cleared and
not loaded anymore during the current Octave session.

@seealso{PKG_ADD}
@end deftypefn */)
{
  octave_value retval;

  int nargin = args.length ();

  if (nargin == 1 || nargin > 3)
    print_usage ();

  if (nargin == 0)
    {
      Cell func_names (dim_vector (autoload_map.size (), 1));
      Cell file_names (dim_vector (autoload_map.size (), 1));

      octave_idx_type i = 0;
      typedef std::map<std::string, std::string>::const_iterator am_iter;
      for (am_iter p = autoload_map.begin (); p != autoload_map.end (); p++)
        {
          func_names(i) = p->first;
          file_names(i) = p->second;

          i++;
        }

      octave_map m;

      m.assign ("function", func_names);
      m.assign ("file", file_names);

      retval = m;
    }
  else
    {
      string_vector argv = args.make_argv ("autoload");

      std::string nm = argv[2];

      if (! octave::sys::env::absolute_pathname (nm))
        {
          octave_user_code *fcn = octave_call_stack::caller_user_code ();

          bool found = false;

          if (fcn)
            {
              std::string fname = fcn->fcn_file_name ();

              if (! fname.empty ())
                {
                  fname = octave::sys::env::make_absolute (fname);
                  fname = fname.substr (0, fname.find_last_of (octave::sys::file_ops::dir_sep_str ()) + 1);

                  octave::sys::file_stat fs (fname + nm);

                  if (fs.exists ())
                    {
                      nm = fname + nm;
                      found = true;
                    }
                }
            }
          if (! found)
            warning_with_id ("Octave:autoload-relative-file-name",
                             "autoload: '%s' is not an absolute filename",
                             nm.c_str ());
        }
      if (nargin == 2)
        autoload_map[argv[1]] = nm;
      else if (nargin == 3)
        {
          if (argv[3] != "remove")
            error_with_id ("Octave:invalid-input-arg",
                           "autoload: third argument can only be 'remove'");

          // Remove function from symbol table and autoload map.
          symbol_table::clear_dld_function (argv[1]);
          autoload_map.erase (argv[1]);
        }
    }

  return retval;
}

void
source_file (const std::string& file_name, const std::string& context,
             bool verbose, bool require_file, const std::string& warn_for)
{
  // Map from absolute name of script file to recursion level.  We
  // use a map instead of simply placing a limit on recursion in the
  // source_file function so that two mutually recursive scripts
  // written as
  //
  //   foo1.m:
  //   ------
  //   foo2
  //
  //   foo2.m:
  //   ------
  //   foo1
  //
  // and called with
  //
  //   foo1
  //
  // (for example) will behave the same if they are written as
  //
  //   foo1.m:
  //   ------
  //   source ("foo2.m")
  //
  //   foo2.m:
  //   ------
  //   source ("foo1.m")
  //
  // and called with
  //
  //   source ("foo1.m")
  //
  // (for example).

  static std::map<std::string, int> source_call_depth;

  std::string file_full_name = octave::sys::file_ops::tilde_expand (file_name);

  file_full_name = octave::sys::env::make_absolute (file_full_name);

  octave::unwind_protect frame;

  if (source_call_depth.find (file_full_name) == source_call_depth.end ())
    source_call_depth[file_full_name] = -1;

  frame.protect_var (source_call_depth[file_full_name]);

  source_call_depth[file_full_name]++;

  if (source_call_depth[file_full_name] >= Vmax_recursion_depth)
    error ("max_recursion_depth exceeded");

  if (! context.empty ())
    {
      if (context == "caller")
        octave_call_stack::goto_caller_frame ();
      else if (context == "base")
        octave_call_stack::goto_base_frame ();
      else
        error ("source: context must be \"caller\" or \"base\"");

      frame.add_fcn (octave_call_stack::pop);
    }

  octave_function *fcn = 0;
  // Don't delete a function already in symbol_table
  bool delete_fcn = false;

  // Find symbol name that would be in symbol_table, if it were loaded.
  size_t dir_end = file_name.find_last_of (octave::sys::file_ops::dir_sep_chars ());
  dir_end = (dir_end == std::string::npos) ? 0 : dir_end + 1;

  size_t extension = file_name.find_last_of ('.');
  if (extension == std::string::npos)
    extension = file_name.length ();

  std::string symbol = file_name.substr (dir_end, extension - dir_end);
  std::string full_name = octave::sys::canonicalize_file_name (file_name);

  // Check if this file is already loaded (or in the path)
  octave_value loaded_sym = symbol_table::find (symbol);
  if (loaded_sym.is_function ())
    {
      fcn = loaded_sym.function_value ();
      if (fcn)
        {
          if (octave::sys::canonicalize_file_name (fcn->fcn_file_name ())
              != full_name)
            {
              fcn = 0;             // wrong file, so load it below
              delete_fcn = true;   // and delete it when done.
            }
        }
    }

  // If no symbol of this name, or the symbol is for a different file, load
  if (! fcn)
    {
      try
        {
          fcn = parse_fcn_file (file_full_name, file_name, "", "",
                                require_file, true, false, false, warn_for);
        }
      catch (octave::execution_exception& e)
        {
          error (e, "source: error sourcing file '%s'",
                 file_full_name.c_str ());
        }
    }

  // Return or error if we don't have a valid script
  if (! fcn)
    return;

  if (! fcn->is_user_code ())
    {
      if (delete_fcn)
        delete fcn;
      error ("source: %s is not a script", full_name.c_str ());
    }

  // Parameter checking is over.  Now run.
  octave_value_list args;

  if (verbose)
    {
      std::cout << "executing commands from " << full_name << " ... ";
      reading_startup_message_printed = true;
      std::cout.flush ();
    }

  fcn->do_multi_index_op (0, args);

  if (verbose)
    std::cout << "done." << std::endl;

  // Delete scripts not on the path, so they don't shadow ones that are.
  if (delete_fcn)
    delete fcn;
}

DEFUN (mfilename, args, ,
       doc: /* -*- texinfo -*-
@deftypefn  {} {} mfilename ()
@deftypefnx {} {} mfilename ("fullpath")
@deftypefnx {} {} mfilename ("fullpathext")
Return the name of the currently executing file.

When called from outside an m-file return the empty string.

Given the argument @qcode{"fullpath"}, include the directory part of the
filename, but not the extension.

Given the argument @qcode{"fullpathext"}, include the directory part of
the filename and the extension.
@end deftypefn */)
{
  octave_value retval;

  int nargin = args.length ();

  if (nargin > 1)
    print_usage ();

  std::string arg;

  if (nargin == 1)
    arg = args(0).xstring_value ("mfilename: argument must be a string");

  std::string fname;

  octave_user_code *fcn = octave_call_stack::caller_user_code ();

  if (fcn)
    {
      fname = fcn->fcn_file_name ();

      if (fname.empty ())
        fname = fcn->name ();
    }

  if (arg == "fullpathext")
    retval = fname;
  else
    {
      size_t dpos = fname.rfind (octave::sys::file_ops::dir_sep_char ());
      size_t epos = fname.rfind ('.');

      if (epos <= dpos)
        epos = std::string::npos;

      fname = (epos != std::string::npos) ? fname.substr (0, epos) : fname;

      if (arg == "fullpath")
        retval = fname;
      else
        retval = (dpos != std::string::npos) ? fname.substr (dpos+1) : fname;
    }

  return retval;
}

DEFUN (source, args, ,
       doc: /* -*- texinfo -*-
@deftypefn  {} {} source (@var{file})
@deftypefnx {} {} source (@var{file}, @var{context})
Parse and execute the contents of @var{file}.

Without specifying @var{context}, this is equivalent to executing commands
from a script file, but without requiring the file to be named
@file{@var{file}.m} or to be on the execution path.

Instead of the current context, the script may be executed in either the
context of the function that called the present function
(@qcode{"caller"}), or the top-level context (@qcode{"base"}).
@seealso{run}
@end deftypefn */)
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin < 1 || nargin > 2)
    print_usage ();

  std::string file_name = args(0).xstring_value ("source: FILE must be a string");

  std::string context;

  if (nargin == 2)
    context = args(1).xstring_value ("source: CONTEXT must be a string");

  source_file (file_name, context);

  return retval;
}

/*!
    Evaluate an Octave function (built-in or interpreted) and return
    the list of result values.

    @param name The name of the function to call.
    @param args The arguments to the function.
    @param nargout The number of output arguments expected.
    @return A list of output values.  The length of the list is not
      necessarily the same as @c nargout.

*/
octave_value_list
feval (const std::string& name, const octave_value_list& args, int nargout)
{
  octave_value_list retval;

  octave_value fcn = symbol_table::find_function (name, args);

  if (fcn.is_defined ())
    retval = fcn.do_multi_index_op (nargout, args);
  else
    error ("feval: function '%s' not found", name.c_str ());

  return retval;
}

octave_value_list
feval (octave_function *fcn, const octave_value_list& args, int nargout)
{
  octave_value_list retval;

  if (fcn)
    retval = fcn->do_multi_index_op (nargout, args);

  return retval;
}

static octave_value_list
get_feval_args (const octave_value_list& args)
{
  return args.slice (1, args.length () - 1, true);
}

/*!
    Evaluate an Octave function (built-in or interpreted) and return
    the list of result values.

    @param args The first element of @c args is the function to call.
      It may be the name of the function as a string, a function
      handle, or an inline function.  The remaining arguments are
      passed to the function.
    @param nargout The number of output arguments expected.
    @return A list of output values.  The length of the list is not
      necessarily the same as @c nargout.
*/
octave_value_list
feval (const octave_value_list& args, int nargout)
{
  octave_value_list retval;

  if (args.length () > 0)
    {
      octave_value f_arg = args(0);

      if (f_arg.is_string ())
        {
          std::string name = f_arg.string_value ();

          octave_value_list tmp_args = get_feval_args (args);

          retval = feval (name, tmp_args, nargout);
        }
      else if (f_arg.is_function_handle ()
               || f_arg.is_anonymous_function ()
               || f_arg.is_inline_function ())
        {
          const octave_value_list tmp_args = get_feval_args (args);

          retval = f_arg.do_multi_index_op (nargout, tmp_args);
        }
      else
        error ("feval: first argument must be a string, inline function, or a function handle");
    }

  return retval;
}

DEFUN (feval, args, nargout,
       doc: /* -*- texinfo -*-
@deftypefn {} {} feval (@var{name}, @dots{})
Evaluate the function named @var{name}.

Any arguments after the first are passed as inputs to the named function.
For example,

@example
@group
feval ("acos", -1)
     @result{} 3.1416
@end group
@end example

@noindent
calls the function @code{acos} with the argument @samp{-1}.

The function @code{feval} can also be used with function handles of any sort
(@pxref{Function Handles}).  Historically, @code{feval} was the only way to
call user-supplied functions in strings, but function handles are now
preferred due to the cleaner syntax they offer.  For example,

@example
@group
@var{f} = @@exp;
feval (@var{f}, 1)
    @result{} 2.7183
@var{f} (1)
    @result{} 2.7183
@end group
@end example

@noindent
are equivalent ways to call the function referred to by @var{f}.  If it
cannot be predicted beforehand whether @var{f} is a function handle,
function name in a string, or inline function then @code{feval} can be used
instead.
@end deftypefn */)
{
  if (args.length () == 0)
    print_usage ();

  return feval (args, nargout);
}

DEFUN (builtin, args, nargout,
       doc: /* -*- texinfo -*-
@deftypefn {} {[@dots{}] =} builtin (@var{f}, @dots{})
Call the base function @var{f} even if @var{f} is overloaded to another
function for the given type signature.

This is normally useful when doing object-oriented programming and there is
a requirement to call one of Octave's base functions rather than the
overloaded one of a new class.

A trivial example which redefines the @code{sin} function to be the
@code{cos} function shows how @code{builtin} works.

@example
@group
sin (0)
  @result{} 0
function y = sin (x), y = cos (x); endfunction
sin (0)
  @result{} 1
builtin ("sin", 0)
  @result{} 0
@end group
@end example
@end deftypefn */)
{
  octave_value_list retval;

  if (args.length () == 0)
    print_usage ();

  const std::string name (args(0).xstring_value ("builtin: function name (F) must be a string"));

  octave_value fcn = symbol_table::builtin_find (name);

  if (fcn.is_defined ())
    retval = feval (fcn.function_value (), args.splice (0, 1), nargout);
  else
    error ("builtin: lookup for symbol '%s' failed", name.c_str ());

  return retval;
}

octave_value_list
eval_string (const std::string& eval_str, bool silent,
             int& parse_status, int nargout)
{
  octave_value_list retval;

  octave::parser parser (eval_str);

  do
    {
      parser.reset ();

      parse_status = parser.run ();

      if (parse_status == 0)
        {
          if (parser.stmt_list)
            {
              tree_statement *stmt = 0;

              if (parser.stmt_list->length () == 1
                  && (stmt = parser.stmt_list->front ())
                  && stmt->is_expression ())
                {
                  tree_expression *expr = stmt->expression ();

                  if (silent)
                    expr->set_print_flag (false);

                  bool do_bind_ans = false;

                  if (expr->is_identifier ())
                    {
                      tree_identifier *id
                        = dynamic_cast<tree_identifier *> (expr);

                      do_bind_ans = (! id->is_variable ());
                    }
                  else
                    do_bind_ans = (! expr->is_assignment_expression ());

                  retval = expr->rvalue (nargout);

                  if (do_bind_ans && ! retval.empty ())
                    bind_ans (retval(0), expr->print_result ());

                  if (nargout == 0)
                    retval = octave_value_list ();
                }
              else if (nargout == 0)
                parser.stmt_list->accept (*octave::current_evaluator);
              else
                error ("eval: invalid use of statement list");

              if (tree_return_command::returning
                  || tree_break_command::breaking
                  || tree_continue_command::continuing)
                break;
            }
          else if (parser.lexer.end_of_input)
            break;
        }
    }
  while (parse_status == 0);

  return retval;
}

octave_value
eval_string (const std::string& eval_str, bool silent, int& parse_status)
{
  octave_value retval;

  octave_value_list tmp = eval_string (eval_str, silent, parse_status, 1);

  if (! tmp.empty ())
    retval = tmp(0);

  return retval;
}

static octave_value_list
eval_string (const octave_value& arg, bool silent, int& parse_status,
             int nargout)
{
  std::string s = arg.xstring_value ("eval: expecting std::string argument");

  return eval_string (s, silent, parse_status, nargout);
}

void
cleanup_statement_list (tree_statement_list **lst)
{
  if (*lst)
    {
      delete *lst;
      *lst = 0;
    }
}

DEFUN (eval, args, nargout,
       doc: /* -*- texinfo -*-
@deftypefn  {} {} eval (@var{try})
@deftypefnx {} {} eval (@var{try}, @var{catch})
Parse the string @var{try} and evaluate it as if it were an Octave
program.

If execution fails, evaluate the optional string @var{catch}.

The string @var{try} is evaluated in the current context, so any results
remain available after @code{eval} returns.

The following example creates the variable @var{A} with the approximate
value of 3.1416 in the current workspace.

@example
eval ("A = acos(-1);");
@end example

If an error occurs during the evaluation of @var{try} then the @var{catch}
string is evaluated, as the following example shows:

@example
@group
eval ('error ("This is a bad example");',
      'printf ("This error occurred:\n%s\n", lasterr ());');
     @print{} This error occurred:
        This is a bad example
@end group
@end example

Programming Note: if you are only using @code{eval} as an error-capturing
mechanism, rather than for the execution of arbitrary code strings,
Consider using try/catch blocks or unwind_protect/unwind_protect_cleanup
blocks instead.  These techniques have higher performance and don't
introduce the security considerations that the evaluation of arbitrary code
does.
@seealso{evalin, evalc, assignin, feval}
@end deftypefn */)
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin == 0)
    print_usage ();

  octave::unwind_protect frame;

  if (nargin > 1)
    {
      frame.protect_var (buffer_error_messages);
      buffer_error_messages++;
    }

  int parse_status = 0;

  bool execution_error = false;

  octave_value_list tmp;

  try
    {
      tmp = eval_string (args(0), nargout > 0, parse_status, nargout);
    }
  catch (const octave::execution_exception&)
    {
      recover_from_exception ();

      execution_error = true;
    }

  if (nargin > 1 && (parse_status != 0 || execution_error))
    {
      // Set up for letting the user print any messages from
      // errors that occurred in the first part of this eval().

      buffer_error_messages--;

      tmp = eval_string (args(1), nargout > 0, parse_status, nargout);

      if (nargout > 0)
        retval = tmp;
    }
  else
    {
      if (nargout > 0)
        retval = tmp;

      // FIXME: we should really be rethrowing whatever exception occurred,
      // not just throwing an execution exception.
      if (execution_error)
        octave_throw_execution_exception ();
    }

  return retval;
}

/*

%!shared x
%! x = 1;

%!assert (eval ("x"), 1)
%!assert (eval ("x;"))
%!assert (eval ("x;"), 1)

%!test
%! y = eval ("x");
%! assert (y, 1);

%!test
%! y = eval ("x;");
%! assert (y, 1);

%!test
%! eval ("x = 1;");
%! assert (x,1);

%!test
%! eval ("flipud = 2;");
%! assert (flipud, 2);

%!function y = __f ()
%!  eval ("flipud = 2;");
%!  y = flipud;
%!endfunction
%!assert (__f(), 2)

% bug #35645
%!test
%! [a,] = gcd (1,2);
%! [a,b,] = gcd (1, 2);

%!error eval ("switch = 13;")

*/

DEFUN (assignin, args, ,
       doc: /* -*- texinfo -*-
@deftypefn {} {} assignin (@var{context}, @var{varname}, @var{value})
Assign @var{value} to @var{varname} in context @var{context}, which
may be either @qcode{"base"} or @qcode{"caller"}.
@seealso{evalin}
@end deftypefn */)
{
  octave_value_list retval;

  if (args.length () != 3)
    print_usage ();

  std::string context = args(0).xstring_value ("assignin: CONTEXT must be a string");

  octave::unwind_protect frame;

  if (context == "caller")
    octave_call_stack::goto_caller_frame ();
  else if (context == "base")
    octave_call_stack::goto_base_frame ();
  else
    error ("assignin: CONTEXT must be \"caller\" or \"base\"");

  frame.add_fcn (octave_call_stack::pop);

  std::string nm = args(1).xstring_value ("assignin: VARNAME must be a string");

  if (valid_identifier (nm))
    {
      // Put the check here so that we don't slow down assignments
      // generally.  Any that go through Octave's parser should have
      // already been checked.

      if (octave::is_keyword (nm))
        error ("assignin: invalid assignment to keyword '%s'", nm.c_str ());

      symbol_table::assign (nm, args(2));
    }
  else
    error ("assignin: invalid variable name in argument VARNAME");

  return retval;
}

/*

%!error assignin ("base", "switch", "13")

*/

DEFUN (evalin, args, nargout,
       doc: /* -*- texinfo -*-
@deftypefn  {} {} evalin (@var{context}, @var{try})
@deftypefnx {} {} evalin (@var{context}, @var{try}, @var{catch})
Like @code{eval}, except that the expressions are evaluated in the context
@var{context}, which may be either @qcode{"caller"} or @qcode{"base"}.
@seealso{eval, assignin}
@end deftypefn */)
{
  octave_value_list retval;

  int nargin = args.length ();

  if (nargin < 2)
    print_usage ();

  std::string context = args(0).xstring_value ("evalin: CONTEXT must be a string");

  octave::unwind_protect frame;

  if (context == "caller")
    octave_call_stack::goto_caller_frame ();
  else if (context == "base")
    octave_call_stack::goto_base_frame ();
  else
    error ("evalin: CONTEXT must be \"caller\" or \"base\"");

  frame.add_fcn (octave_call_stack::pop);

  if (nargin > 2)
    {
      frame.protect_var (buffer_error_messages);
      buffer_error_messages++;
    }

  int parse_status = 0;

  bool execution_error = false;

  octave_value_list tmp;

  try
    {
      tmp = eval_string (args(1), nargout > 0,
                         parse_status, nargout);
    }
  catch (const octave::execution_exception&)
    {
      recover_from_exception ();

      execution_error = true;
    }

  if (nargin > 2 && (parse_status != 0 || execution_error))
    {
      // Set up for letting the user print any messages from
      // errors that occurred in the first part of this eval().

      buffer_error_messages--;

      tmp = eval_string (args(2), nargout > 0,
                         parse_status, nargout);

      retval = (nargout > 0) ? tmp : octave_value_list ();
    }
  else
    {
      if (nargout > 0)
        retval = tmp;

      // FIXME: we should really be rethrowing whatever
      // exception occurred, not just throwing an
      // execution exception.
      if (execution_error)
        octave_throw_execution_exception ();
    }

  return retval;
}

static void
maybe_print_last_error_message (bool *doit)
{
  if (doit && *doit)
    // Print error message again, which was lost because of the stderr buffer
    // Note: this keeps error_state and last_error_stack intact
    message_with_id ("error", last_error_id ().c_str (),
                     last_error_message ().c_str ());
}

static void
restore_octave_stdout (std::streambuf *buf)
{
  octave_stdout.flush ();
  octave_stdout.rdbuf (buf);
}

static void
restore_octave_stderr (std::streambuf *buf)
{
  std::cerr.flush ();
  std::cerr.rdbuf (buf);
}

DEFUN (evalc, args, nargout,
       doc: /* -*- texinfo -*-
@deftypefn  {} {@var{s} =} evalc (@var{try})
@deftypefnx {} {@var{s} =} evalc (@var{try}, @var{catch})
Parse and evaluate the string @var{try} as if it were an Octave program,
while capturing the output into the return variable @var{s}.

If execution fails, evaluate the optional string @var{catch}.

This function behaves like @code{eval}, but any output or warning messages
which would normally be written to the console are captured and returned in
the string @var{s}.

The @code{diary} is disabled during the execution of this function.  When
@code{system} is used, any output produced by external programs is
@emph{not} captured, unless their output is captured by the @code{system}
function itself.

@example
@group
s = evalc ("t = 42"), t
  @result{} s = t =  42

  @result{} t =  42
@end group
@end example
@seealso{eval, diary}
@end deftypefn */)
{
  int nargin = args.length ();

  if (nargin == 0 || nargin > 2)
    print_usage ();

  // redirect stdout/stderr to capturing buffer
  std::ostringstream buffer;

  std::ostream& out_stream = octave_stdout;
  std::ostream& err_stream = std::cerr;

  out_stream.flush ();
  err_stream.flush ();

  std::streambuf* old_out_buf = out_stream.rdbuf (buffer.rdbuf ());
  std::streambuf* old_err_buf = err_stream.rdbuf (buffer.rdbuf ());

  bool eval_error_occurred = true;

  octave::unwind_protect frame;

  frame.add_fcn (maybe_print_last_error_message, &eval_error_occurred);
  frame.add_fcn (restore_octave_stdout, old_out_buf);
  frame.add_fcn (restore_octave_stderr, old_err_buf);

  // call standard eval function
  octave_value_list retval;
  int eval_nargout = std::max (0, nargout - 1);

  retval = Feval (args, eval_nargout);
  eval_error_occurred = false;

  retval.prepend (buffer.str ());
  return retval;
}

/*

%!assert (evalc ("1"), "ans =  1\n")
%!assert (evalc ("1;"), "")

%!test
%! [s, y] = evalc ("1");
%! assert (s, "");
%! assert (y, 1);

%!test
%! [s, y] = evalc ("1;");
%! assert (s, "");
%! assert (y, 1);

%!test
%! assert (evalc ("y = 2"), "y =  2\n");
%! assert (y, 2);

%!test
%! assert (evalc ("y = 3;"), "");
%! assert (y, 3);

%!test
%! [s, a, b] = evalc ("deal (1, 2)");
%! assert (s, "");
%! assert (a, 1);
%! assert (b, 2);

%!function [a, b] = __f_evalc ()
%!  printf ("foo");
%!  fprintf (stdout, "bar");
%!  disp (pi);
%!  a = 1;
%!  b = 2;
%!endfunction
%!test
%! [s, a, b] = evalc ("__f_evalc ()");
%! assert (s, "foobar 3.1416\n");
%! assert (a, 1);
%! assert (b, 2);

%!error <foo> (evalc ("error ('foo')"))
%!error <bar> (evalc ("error ('foo')", "error ('bar')"))

%!test
%! warning ("off", "quiet", "local");
%! assert (evalc ("warning ('foo')"), "warning: foo\n");

%!test
%! warning ("off", "quiet", "local");
%! assert (evalc ("error ('foo')", "warning ('bar')"), "warning: bar\n");

%!error evalc ("switch = 13;")

*/

DEFUN (__parser_debug_flag__, args, nargout,
       doc: /* -*- texinfo -*-
@deftypefn  {} {@var{val} =} __parser_debug_flag__ ()
@deftypefnx {} {@var{old_val} =} __parser_debug_flag__ (@var{new_val})
Query or set the internal flag that determines whether Octave's parser
prints debug information as it processes an expression.
@seealso{__lexer_debug_flag__}
@end deftypefn */)
{
  octave_value retval;

  bool debug_flag = octave_debug;

  retval = set_internal_variable (debug_flag, args, nargout,
                                  "__parser_debug_flag__");

  octave_debug = debug_flag;

  return retval;
}

DEFUN (__parse_file__, args, ,
       doc: /* -*- texinfo -*-
@deftypefn {} {} __parse_file__ (@var{file}, @var{verbose})
Undocumented internal function.
@end deftypefn */)
{
  octave_value retval;

  int nargin = args.length ();

  if (nargin < 1 || nargin > 2)
    print_usage ();

  std::string file = args(0).xstring_value ("__parse_file__: expecting filename as argument");

  std::string full_file = octave::sys::env::make_absolute (file);

  size_t file_len = file.length ();

  if ((file_len > 4 && file.substr (file_len-4) == ".oct")
      || (file_len > 4 && file.substr (file_len-4) == ".mex")
      || (file_len > 2 && file.substr (file_len-2) == ".m"))
    {
      file = octave::sys::env::base_pathname (file);
      file = file.substr (0, file.find_last_of ('.'));

      size_t pos = file.find_last_of (octave::sys::file_ops::dir_sep_str ());
      if (pos != std::string::npos)
        file = file.substr (pos+1);
    }

  if (nargin == 2)
    octave_stdout << "parsing " << full_file << std::endl;

  octave_function *fcn = parse_fcn_file (full_file, file, "", "",
                                         true, false, false,
                                         false, "__parse_file__");

  if (fcn)
    delete fcn;

  return retval;
}
