home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Geek Gadgets 1
/
ADE-1.bin
/
ade-dist
/
octave-1.1.1p1-src.tgz
/
tar.out
/
fsf
/
octave
/
src
/
parse.y
< prev
next >
Wrap
Text File
|
1996-09-28
|
37KB
|
1,624 lines
/* parse.y -*- text -*-
Copyright (C) 1992, 1993, 1994, 1995 John W. Eaton
This file is part of Octave.
Octave is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation; either version 2, or (at your option) any
later version.
Octave is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with Octave; see the file COPYING. If not, write to the Free
Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Parser for Octave.
// C decarations.
%{
#define YYDEBUG 1
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <strstream.h>
#include "SLStack.h"
#include "Matrix.h"
#include "octave-hist.h"
#include "user-prefs.h"
#include "tree-base.h"
#include "tree-expr.h"
#include "tree-cmd.h"
#include "tree-const.h"
#include "tree-misc.h"
#include "variables.h"
#include "tree-plot.h"
#include "octave.h"
#include "symtab.h"
#include "parse.h"
#include "token.h"
#include "error.h"
#include "pager.h"
#include "input.h"
#include "utils.h"
#include "lex.h"
// Nonzero means we're in the middle of defining a function.
int defining_func = 0;
// Nonzero means we're in the middle of defining a loop.
int looping = 0;
// Nonzero means we're in the middle of defining a conditional expression.
int iffing = 0;
// Nonzero means we need to do some extra lookahead to avoid being
// screwed by bogus function syntax.
int maybe_screwed = 0;
// Nonzero means we need to do some extra lookahead to avoid being
// screwed by bogus function syntax.
int maybe_screwed_again = 0;
// Temporary symbol table pointer used to cope with bogus function syntax.
symbol_table *tmp_local_sym_tab = 0;
// Stack to hold list of literal matrices.
SLStack <tree_matrix *> ml;
// A nonzero element corresponding to an element of ml means we just
// started reading a new matrix. This should probably be part of a
// new struct for matrix lists...
SLStack <int> mlnm;
// The current input line number.
int input_line_number = 0;
// The column of the current token.
int current_input_column = 1;
// Buffer for help text snagged from function files.
char *help_buf = 0;
// Nonzero means we're working on a plot command.
int plotting = 0;
// Nonzero means we've seen something that means we must be past the
// range part of a plot command.
int past_plot_range = 0;
// Nonzero means we're looking at the range part of a plot command.
int in_plot_range = 0;
// Nonzero means we're looking at the using part of a plot command.
int in_plot_using = 0;
// Nonzero means we're looking at the style part of a plot command.
int in_plot_style = 0;
// Nonzero means we're looking at an indirect reference to a structure
// element.
int looking_at_indirect_ref = 0;
// Forward declarations for some functions defined at the bottom of
// the file.
// Generic error messages.
static void yyerror (char *s);
// Error mesages for mismatched end tokens.
static void end_error (char *type, token::end_tok_type ettype, int l, int c);
// Check to see that end tokens are properly matched.
static int check_end (token *tok, token::end_tok_type expected);
// Try to figure out early if an expression should become an
// assignment to the builtin variable ans.
static tree_expression *maybe_convert_to_ans_assign (tree_expression *expr);
// Maybe print a warning if an assignment expression is used as the
// test in a logical expression.
static void maybe_warn_assign_as_truth_value (tree_expression *expr);
// Build a binary expression.
static tree_expression *make_binary_op
(int op, tree_expression *op1, token *tok_val, tree_expression *op2);
// Build a prefix expression.
static tree_expression *make_prefix_op
(int op, tree_identifier *op1, token *tok_val);
// Build a postfix expression.
static tree_expression *make_postfix_op
(int op, tree_identifier *op1, token *tok_val);
// Build a binary expression.
static tree_expression *make_unary_op
(int op, tree_expression *op1, token *tok_val);
// Make an expression that handles assignment of multiple values.
static tree_expression *make_multi_val_ret
(tree_expression *rhs, int l = -1, int c = -1);
// Make an index expression.
static tree_index_expression *make_index_expression
(tree_indirect_ref *indir, tree_argument_list *args);
#define ABORT_PARSE \
do \
{ \
global_command = 0; \
yyerrok; \
if (interactive) \
YYACCEPT; \
else \
YYABORT; \
} \
while (0)
%}
// Bison declarations.
%union
{
// The type of the basic tokens returned by the lexer.
token *tok_val;
// Types for the nonterminals we generate.
tree *tree_type;
tree_expression *tree_expression_type;
tree_constant *tree_constant_type;
tree_matrix *tree_matrix_type;
tree_identifier *tree_identifier_type;
tree_indirect_ref *tree_indirect_ref_type;
tree_function *tree_function_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_global *tree_global_type;
tree_global_init_list *tree_global_init_list_type;
tree_global_command *tree_global_command_type;
tree_statement *tree_statement_type;
tree_statement_list *tree_statement_list_type;
tree_plot_command *tree_plot_command_type;
subplot *subplot_type;
subplot_list *subplot_list_type;
plot_limits *plot_limits_type;
plot_range *plot_range_type;
subplot_using *subplot_using_type;
subplot_style *subplot_style_type;
}
// Tokens with line and column information.
%token <tok_val> '=' ':' '-' '+' '*' '/'
%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 QUOTE TRANSPOSE
%token <tok_val> PLUS_PLUS MINUS_MINUS POW EPOW
%token <tok_val> NUM IMAG_NUM
%token <tok_val> NAME SCREW
%token <tok_val> END
%token <tok_val> PLOT
%token <tok_val> TEXT STYLE
%token <tok_val> FOR WHILE IF ELSEIF ELSE BREAK CONTINUE FUNC_RET
%token <tok_val> UNWIND CLEANUP
%token <tok_val> GLOBAL
%token <tok_val> TEXT_ID
// Other tokens.
%token LEXICAL_ERROR
%token FCN SCREW_TWO
%token ELLIPSIS
%token ALL_VA_ARGS
%token END_OF_INPUT
%token USING TITLE WITH COLON OPEN_BRACE CLOSE_BRACE CLEAR
// Nonterminals we construct.
%type <tree_type> input
%type <tree_expression_type> expression simple_expr simple_expr1
%type <tree_expression_type> ans_expression title
%type <tree_matrix_type> matrix
%type <tree_identifier_type> identifier
%type <tree_indirect_ref_type> indirect_ref indirect_ref1
%type <tree_function_type> func_def1 func_def2 func_def3
%type <tree_index_expression_type> variable word_list_cmd
%type <tree_colon_expression_type> colon_expr
%type <tree_argument_list_type> arg_list word_list
%type <tree_parameter_list_type> param_list param_list1
%type <tree_parameter_list_type> return_list return_list1
%type <tree_command_type> command func_def
%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_global_type> global_decl2
%type <tree_global_init_list_type> global_decl1
%type <tree_global_command_type> global_decl
%type <tree_statement_type> statement
%type <tree_statement_list_type> simple_list simple_list1 list list1
%type <tree_statement_list_type> opt_list input1
%type <tree_plot_command_type> plot_command
%type <subplot_type> plot_command2 plot_options
%type <subplot_list_type> plot_command1
%type <plot_limits_type> ranges
%type <plot_range_type> ranges1
%type <subplot_using_type> using using1
%type <subplot_style_type> style
// Precedence and associativity.
%left ';' ',' '\n'
%right '='
%left EXPR_AND_AND EXPR_OR_OR
%left EXPR_AND EXPR_OR
%left EXPR_LT EXPR_LE EXPR_EQ EXPR_NE EXPR_GE EXPR_GT
%left ':'
%left '-' '+'
%left '*' '/' LEFTDIV EMUL EDIV ELEFTDIV
%left QUOTE TRANSPOSE
%left UNARY PLUS_PLUS MINUS_MINUS EXPR_NOT
%right POW EPOW
// There are 19 shift/reduce conflicts, ok?
%expect 19
// Where to start.
%start input
// Grammar rules.
%%
input : input1
{
global_command = $1;
promptflag = 1;
YYACCEPT;
}
| END_OF_INPUT
{
global_command = 0;
promptflag = 1;
YYABORT;
}
| simple_list parse_error
{ ABORT_PARSE; }
| parse_error
{ ABORT_PARSE; }
;
input1 : '\n'
{ $$ = 0; }
| simple_list
{ $$ = $1; }
| simple_list '\n'
{ $$ = $1; }
| simple_list END_OF_INPUT
{ $$ = $1; }
;
parse_error : LEXICAL_ERROR
{ yyerror ("parse error"); }
| error
;
simple_list : semi_comma
{ $$ = 0; }
| comma_semi
{ $$ = 0; }
| simple_list1
{ $$ = $1; }
| simple_list1 semi_comma
{
tree_statement *tmp = $1->rear ();
tmp->set_print_flag (0);
}
| simple_list1 comma_semi
{ $$ = $1; }
;
simple_list1 : statement
{ $$ = new tree_statement_list ($1); }
| semi_comma statement
{ $$ = new tree_statement_list ($2); }
| comma_semi statement
{ $$ = new tree_statement_list ($2); }
| simple_list1 semi_comma statement
{
tree_statement *tmp = $1->rear ();
tmp->set_print_flag (0);
$1->append ($3);
}
| simple_list1 comma_semi statement
{ $1->append ($3); }
;
semi_comma : ';'
| semi_comma ','
| semi_comma ';'
;
comma_semi : ','
| comma_semi ';'
| comma_semi ','
;
comma_nl_sep : ','
| '\n'
| comma_nl_sep sep
;
semi_sep : ';'
| semi_sep sep
;
opt_list : // empty
{ $$ = new tree_statement_list (); }
| list
{ $$ = $1; }
;
list : list1
{ $$ = $1; }
| list1 comma_nl_sep
{ $$ = $1; }
| list1 semi_sep
{
tree_statement *tmp = $1->rear ();
tmp->set_print_flag (0);
}
;
list1 : statement
{
beginning_of_function = 0;
$$ = new tree_statement_list ($1);
}
| list1 comma_nl_sep statement
{ $1->append ($3); }
| list1 semi_sep statement
{
tree_statement *tmp = $1->rear ();
tmp->set_print_flag (0);
$1->append ($3);
}
;
statement : command
{ $$ = new tree_statement ($1); }
| ans_expression
{ $$ = new tree_statement ($1); }
| PLOT CLEAR
{
symbol_record *sr = lookup_by_name ("clearplot", 0);
tree_identifier *id = new tree_identifier (sr);
$$ = new tree_statement (id);
}
;
plot_command : PLOT plot_command1
{
if (! $2 && $1->pttype () != token::replot)
{
yyerror ("must have something to plot");
ABORT_PARSE;
}
else
{
$$ = new tree_plot_command ($2, $1->pttype ());
plotting = 0;
past_plot_range = 0;
in_plot_range = 0;
in_plot_using = 0;
in_plot_style = 0;
}
}
| PLOT ranges plot_command1
{
if ($1->pttype () == token::replot)
{
yyerror ("cannot specify new ranges with replot");
ABORT_PARSE;
}
else
{
$$ = new tree_plot_command ($3, $2, $1->pttype ());
plotting = 0;
past_plot_range = 0;
in_plot_range = 0;
in_plot_using = 0;
in_plot_style = 0;
}
}
;
ranges : ranges1
{ $$ = new plot_limits ($1); }
| ranges1 ranges1
{ $$ = new plot_limits ($1, $2); }
| ranges1 ranges1 ranges1
{ $$ = new plot_limits ($1, $2, $3); }
;
ranges1 : OPEN_BRACE expression COLON expression CLOSE_BRACE
{ $$ = new plot_range ($2, $4); }
| OPEN_BRACE COLON expression CLOSE_BRACE
{ $$ = new plot_range (0, $3); }
| OPEN_BRACE expression COLON CLOSE_BRACE
{ $$ = new plot_range ($2, 0); }
| OPEN_BRACE COLON CLOSE_BRACE
{ $$ = new plot_range (); }
| OPEN_BRACE CLOSE_BRACE
{ $$ = new plot_range (); }
;
plot_command1 : // empty
{ $$ = 0; }
| plot_command2
{ $$ = new subplot_list ($1); }
| plot_command1 ',' plot_command2
{ $1->append ($3); }
;
plot_command2 : expression
{ $$ = new subplot ($1); }
| expression plot_options
{
$2->set_data ($1);
$$ = $2;
}
;
plot_options : using
{ $$ = new subplot ($1, 0, 0); }
| title
{ $$ = new subplot (0, $1, 0); }
| style
{ $$ = new subplot (0, 0, $1); }
| using title
{ $$ = new subplot ($1, $2, 0); }
| title using
{ $$ = new subplot ($2, $1, 0); }
| using style
{ $$ = new subplot ($1, 0, $2); }
| style using
{ $$ = new subplot ($2, 0, $1); }
| title style
{ $$ = new subplot (0, $1, $2); }
| style title
{ $$ = new subplot (0, $2, $1); }
| using title style
{ $$ = new subplot ($1, $2, $3); }
| using style title
{ $$ = new subplot ($1, $3, $2); }
| title using style
{ $$ = new subplot ($2, $1, $3); }
| title style using
{ $$ = new subplot ($3, $1, $2); }
| style using title
{ $$ = new subplot ($2, $3, $1); }
| style title using
{ $$ = new subplot ($3, $2, $1); }
;
using : using1
{
in_plot_using = 0;
$$ = $1;
}
| using1 expression
{
in_plot_using = 0;
$$ = $1->set_format ($2);
}
;
using1 : USING expression
{
subplot_using *tmp = new subplot_using ();
$$ = tmp->add_qualifier ($2);
}
| using1 COLON expression
{ $$ = $1->add_qualifier ($3); }
;
title : TITLE expression
{ $$ = $2; }
;
style : WITH STYLE
{ $$ = new subplot_style ($2->string ()); }
| WITH STYLE expression
{ $$ = new subplot_style ($2->string (), $3); }
| WITH STYLE expression bogus_syntax expression
{ $$ = new subplot_style ($2->string (), $3, $5); }
;
bogus_syntax : // empty
;
ans_expression : expression
{ $$ = maybe_convert_to_ans_assign ($1); }
;
global_decl : GLOBAL global_decl1
{
$$ = new tree_global_command ($2, $1->line (),
$1->column ());
}
;
global_decl1 : global_decl2
{ $$ = new tree_global_init_list ($1); }
| global_decl1 optcomma global_decl2
{ $1->append ($3); }
global_decl2 : identifier
{ $$ = new tree_global ($1); }
| identifier '=' expression
{
tree_simple_assignment_expression *tmp_ass;
tmp_ass = new tree_simple_assignment_expression
($1, $3, 0, 0, $2->line (), $2->column ());
$$ = new tree_global (tmp_ass);
}
;
optcomma : // empty
| ','
{
if (user_pref.warn_comma_in_global_decl)
warning ("comma in global declaration not\
interpreted as a command separator");
}
;
command : plot_command
{ $$ = $1; }
| func_def
{ $$ = $1; }
| global_decl
{ $$ = $1; }
| if_command
{
iffing--;
$$ = $1;
}
| UNWIND optsep opt_list CLEANUP optsep opt_list END
{
if (check_end ($7, token::unwind_protect_end))
ABORT_PARSE;
$$ = new tree_unwind_protect_command ($3, $6, $1->line (),
$1->column ());
}
| WHILE expression optsep opt_list END
{
maybe_warn_assign_as_truth_value ($2);
if (check_end ($5, token::while_end))
ABORT_PARSE;
looping--;
$$ = new tree_while_command ($2, $4, $1->line (),
$1->column ());
}
| FOR variable '=' expression optsep opt_list END
{
if (check_end ($7, token::for_end))
ABORT_PARSE;
looping--;
$$ = new tree_for_command ($2, $4, $6,
$1->line (), $1->column ());
}
| BREAK
{
if (! (looping || defining_func))
{
yyerror ("break: only meaningful within a loop\
or function body");
ABORT_PARSE;
}
$$ = new tree_break_command ($1->line (), $1->column ());
}
| CONTINUE
{
if (! looping)
{
yyerror ("continue: only meaningful within a\
`for' or `while' loop");
ABORT_PARSE;
}
$$ = new tree_continue_command ($1->line (),
$1->column ());
}
| FUNC_RET
{
if (! defining_func)
{
yyerror ("return: only meaningful within a function");
ABORT_PARSE;
}
$$ = new tree_return_command ($1->line (), $1->column ());
}
;
if_command : IF if_cmd_list END
{
if (check_end ($3, token::if_end))
ABORT_PARSE;
$$ = new tree_if_command ($2, $1->line (), $1->column ());
}
;
if_cmd_list : if_cmd_list1
{ $$ = $1 }
| if_cmd_list1 else_clause
{ $1->append ($2); }
;
if_cmd_list1 : expression optsep opt_list
{
maybe_warn_assign_as_truth_value ($1);
tree_if_clause *t = new tree_if_clause ($1, $3);
$$ = new tree_if_command_list (t);
}
| if_cmd_list1 elseif_clause
{ $1->append ($2); }
;
elseif_clause : ELSEIF optsep expression optsep opt_list
{
maybe_warn_assign_as_truth_value ($3);
$$ = new tree_if_clause ($3, $5);
}
;
else_clause : ELSE optsep opt_list
{ $$ = new tree_if_clause ($3); }
;
optsep : // empty
| sep
;
sep : ','
| ';'
| '\n'
| sep ','
| sep ';'
| sep '\n'
;
screwed_again : // empty
{ maybe_screwed_again++; }
;
expression : variable '=' expression
{ $$ = new tree_simple_assignment_expression
($1, $3, 0, 0, $2->line (), $2->column ()); }
| NUM '=' expression
{
yyerror ("invalid assignment to a number");
$$ = 0;
ABORT_PARSE;
}
| '[' screwed_again matrix_row SCREW_TWO '=' expression
{
$$ = make_multi_val_ret ($6, $5->line (), $5->column ());
if (! $$)
ABORT_PARSE;
}
| simple_expr
{ $$ = $1; }
;
simple_expr : simple_expr1
{ $$ = $1; }
| identifier PLUS_PLUS
{ $$ = make_postfix_op (PLUS_PLUS, $1, $2); }
| identifier MINUS_MINUS
{ $$ = make_postfix_op (MINUS_MINUS, $1, $2); }
| simple_expr QUOTE
{ $$ = make_unary_op (QUOTE, $1, $2); }
| simple_expr TRANSPOSE
{ $$ = make_unary_op (TRANSPOSE, $1, $2); }
| simple_expr POW simple_expr
{ $$ = make_binary_op (POW, $1, $2, $3); }
| simple_expr EPOW simple_expr
{ $$ = make_binary_op (EPOW, $1, $2, $3); }
| simple_expr '+' simple_expr
{ $$ = make_binary_op ('+', $1, $2, $3); }
| simple_expr '-' simple_expr
{ $$ = make_binary_op ('-', $1, $2, $3); }
| simple_expr '*' simple_expr
{ $$ = make_binary_op ('*', $1, $2, $3); }
| simple_expr '/' simple_expr
{ $$ = make_binary_op ('/', $1, $2, $3); }
| simple_expr EMUL simple_expr
{ $$ = make_binary_op (EMUL, $1, $2, $3); }
| simple_expr EDIV simple_expr
{ $$ = make_binary_op (EDIV, $1, $2, $3); }
| simple_expr LEFTDIV simple_expr
{ $$ = make_binary_op (LEFTDIV, $1, $2, $3); }
| simple_expr ELEFTDIV simple_expr
{ $$ = make_binary_op (ELEFTDIV, $1, $2, $3); }
| simple_expr EXPR_LT simple_expr
{ $$ = make_binary_op (EXPR_LT, $1, $2, $3); }
| simple_expr EXPR_LE simple_expr
{ $$ = make_binary_op (EXPR_LE, $1, $2, $3); }
| simple_expr EXPR_EQ simple_expr
{ $$ = make_binary_op (EXPR_EQ, $1, $2, $3); }
| simple_expr EXPR_GE simple_expr
{ $$ = make_binary_op (EXPR_GE, $1, $2, $3); }
| simple_expr EXPR_GT simple_expr
{ $$ = make_binary_op (EXPR_GT, $1, $2, $3); }
| simple_expr EXPR_NE simple_expr
{ $$ = make_binary_op (EXPR_NE, $1, $2, $3); }
| simple_expr EXPR_AND_AND simple_expr
{ $$ = make_binary_op (EXPR_AND_AND, $1, $2, $3); }
| simple_expr EXPR_OR_OR simple_expr
{ $$ = make_binary_op (EXPR_OR_OR, $1, $2, $3); }
| simple_expr EXPR_AND simple_expr
{ $$ = make_binary_op (EXPR_AND, $1, $2, $3); }
| simple_expr EXPR_OR simple_expr
{ $$ = make_binary_op (EXPR_OR, $1, $2, $3); }
;
simple_expr1 : NUM
{
tree_constant *tmp = new tree_constant ($1->number ());
tmp->stash_original_text ($1->text_rep ());
$$ = tmp;
}
| IMAG_NUM
{
Complex c (0.0, $1->number ());
tree_constant *tmp = new tree_constant (c);
tmp->stash_original_text ($1->text_rep ());
$$ = tmp;
}
| TEXT
{ $$ = new tree_constant ($1->string ()); }
| '(' expression ')'
{
$2->in_parens++;
$$ = $2;
}
| word_list_cmd
{ $$ = $1; }
| variable
{ $$ = $1; }
| matrix
{ $$ = $1; }
| '[' ']'
{
mlnm.pop ();
$$ = new tree_constant (Matrix ());
}
| '[' ';' ']'
{
mlnm.pop ();
$$ = new tree_constant (Matrix ());
}
| colon_expr
{ $$ = $1; }
| PLUS_PLUS identifier %prec UNARY
{ $$ = make_prefix_op (PLUS_PLUS, $2, $1); }
| MINUS_MINUS identifier %prec UNARY
{ $$ = make_prefix_op (MINUS_MINUS, $2, $1); }
| EXPR_NOT simple_expr
{ $$ = make_unary_op (EXPR_NOT, $2, $1); }
| '+' simple_expr %prec UNARY
{ $$ = $2; }
| '-' simple_expr %prec UNARY
{ $$ = make_unary_op ('-', $2, $1); }
;
colon_expr : simple_expr ':' simple_expr
{ $$ = new tree_colon_expression
($1, $3, $2->line (), $2->column ()); }
| colon_expr ':' simple_expr
{
$$ = $1->chain ($3);
if (! $$)
ABORT_PARSE;
}
;
word_list_cmd : identifier word_list
{
$$ = new tree_index_expression
($1, $2, $1->line (), $1->column ());
}
;
word_list : TEXT
{
tree_constant *tmp = new tree_constant ($1->string ());
$$ = new tree_argument_list (tmp);
}
| word_list TEXT
{
tree_constant *tmp = new tree_constant ($2->string ());
$1->append (tmp);
}
;
// This is truly disgusting.
g_symtab : // empty
{ curr_sym_tab = global_sym_tab; }
;
local_symtab : // empty
{ curr_sym_tab = tmp_local_sym_tab; }
;
safe : // empty
{ maybe_screwed = 0; }
;
are_we_screwed : // empty
{ maybe_screwed = 1; }
;
func_def : FCN g_symtab are_we_screwed func_def1
{
curr_sym_tab = top_level_sym_tab;
defining_func = 0;
$$ = 0;
}
| FCN g_symtab are_we_screwed func_def2
{
curr_sym_tab = top_level_sym_tab;
defining_func = 0;
$$ = 0;
}
;
func_def1 : SCREW safe g_symtab '=' func_def2
{
tree_identifier *tmp = new tree_identifier
($1->sym_rec (), $1->line (), $1->column ());
tree_parameter_list *tpl = new tree_parameter_list (tmp);
tpl->mark_as_formal_parameters ();
$$ = $5->define_ret_list (tpl);
}
| return_list g_symtab '=' func_def2
{
$1->mark_as_formal_parameters ();
$$ = $4->define_ret_list ($1);
}
;
return_list_x : '[' safe local_symtab
;
return_list : return_list_x ']'
{ $$ = new tree_parameter_list (); }
| return_list_x ELLIPSIS ']'
{
tree_parameter_list *tmp = new tree_parameter_list ();
tmp->mark_varargs_only ();
$$ = tmp;
}
| return_list1 ']'
{ $$ = $1; }
| return_list1 ',' ELLIPSIS ']'
{
$1->mark_varargs ();
$$ = $1;
}
;
return_list1 : return_list_x identifier
{ $$ = new tree_parameter_list ($2); }
| return_list_x error
{
yyerror ("invalid function return list");
ABORT_PARSE;
}
| return_list1 ',' identifier
{ $1->append ($3); }
;
func_def2 : identifier safe local_symtab func_def3
{
char *id_name = $1->name ();
// if (is_text_function_name (id_name))
// {
// yyerror ("invalid use of reserved word %s", id_name);
// ABORT_PARSE;
// }
// 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).
$4->stash_function_name (id_name);
if (reading_fcn_file)
{
if (strcmp (curr_fcn_file_name, id_name) != 0)
{
if (user_pref.warn_function_name_clash)
warning ("function name `%s' does not agree\
with function file name `%s.m'", id_name, curr_fcn_file_name);
global_sym_tab->rename (id_name,
curr_fcn_file_name);
if (error_state)
ABORT_PARSE;
id_name = $1->name ();
}
$4->stash_function_name (id_name);
$4->stash_fcn_file_name ();
$4->stash_fcn_file_time (time (0));
$4->mark_as_system_fcn_file ();
}
else if (! (input_from_tmp_history_file
|| input_from_startup_file)
&& reading_script_file
&& strcmp (curr_fcn_file_name, id_name) == 0)
{
warning ("function `%s' defined within\
script file `%s.m'", id_name, curr_fcn_file_name);
}
top_level_sym_tab->clear (id_name);
$1->define ($4);
$1->document (help_buf);
$$ = $4;
}
;
func_def3 : param_list optsep opt_list fcn_end_or_eof
{
tree_function *fcn = new tree_function ($3, curr_sym_tab);
$$ = fcn->define_param_list ($1);
}
| optsep opt_list fcn_end_or_eof
{ $$ = new tree_function ($2, curr_sym_tab); }
;
fcn_end_or_eof : END
{
if (check_end ($1, token::function_end))
ABORT_PARSE;
if (reading_fcn_file)
check_for_garbage_after_fcn_def ();
}
| END_OF_INPUT
{
if (! (reading_fcn_file || reading_script_file))
YYABORT;
}
;
indirect_ref : indirect_ref1
{
looking_at_indirect_ref = 0;
$$ = $1;
}
indirect_ref1 : identifier
{
$$ = new tree_indirect_ref ($1, $1->line (),
$1->column ());
}
| indirect_ref1 '.' { looking_at_indirect_ref = 1; } TEXT_ID
{ $$ = $1->chain ($4->string ()); }
;
variable : indirect_ref
{ $$ = make_index_expression ($1, 0); }
| indirect_ref '(' ')'
{ $$ = make_index_expression ($1, 0); }
| indirect_ref '(' arg_list ')'
{ $$ = make_index_expression ($1, $3); }
| indirect_ref '['
{
yyerror ("use `(\' and `)\' as index operators, not\
`[\' and `]\'");
$$ = 0;
ABORT_PARSE;
}
;
param_list : '(' ')'
{
quote_is_transpose = 0;
$$ = 0;
}
| '(' ELLIPSIS ')'
{
quote_is_transpose = 0;
tree_parameter_list *tmp = new tree_parameter_list ();
tmp->mark_varargs_only ();
$$ = tmp;
}
| param_list1 ')'
{
quote_is_transpose = 0;
$1->mark_as_formal_parameters ();
}
| param_list1 ',' ELLIPSIS ')'
{
quote_is_transpose = 0;
$1->mark_as_formal_parameters ();
$1->mark_varargs ();
}
;
param_list1 : '(' identifier
{ $$ = new tree_parameter_list ($2); }
| param_list1 ',' identifier
{ $1->append ($3); }
| '(' error
{
yyerror ("invalid parameter list");
ABORT_PARSE;
}
| param_list1 ',' error
{
yyerror ("invalid parameter list");
ABORT_PARSE;
}
;
identifier : NAME
{
$$ = new tree_identifier
($1->sym_rec (), $1->line (), $1->column ());
}
;
arg_list : ':'
{
tree_constant *colon;
tree_constant::magic_colon t;
colon = new tree_constant (t);
$$ = new tree_argument_list (colon);
}
| expression
{ $$ = new tree_argument_list ($1); }
| ALL_VA_ARGS
{
tree_constant *all_va_args;
tree_constant::all_va_args t;
all_va_args = new tree_constant (t);
$$ = new tree_argument_list (all_va_args);
}
| arg_list ',' ':'
{
tree_constant *colon;
tree_constant::magic_colon t;
colon = new tree_constant (t);
$1->append (colon);
}
| arg_list ',' expression
{ $1->append ($3); }
| arg_list ',' ALL_VA_ARGS
{
tree_constant *all_va_args;
tree_constant::all_va_args t;
all_va_args = new tree_constant (t);
$1->append (all_va_args);
}
;
matrix : '[' screwed_again rows ']'
{
mlnm.pop ();
maybe_screwed_again--;
tree_matrix *tmp = ml.pop ();
$$ = tmp->reverse ();
}
;
rows : rows1
| rows1 ';' // Ignore trailing semicolon.
;
rows1 : matrix_row
| rows1 ';' matrix_row
;
matrix_row : matrix_row1
| matrix_row1 ',' // Ignore trailing comma.
;
matrix_row1 : expression // First element on row.
{
if (mlnm.top ())
{
mlnm.pop ();
mlnm.push (0);
tree_matrix *tmp = new tree_matrix
($1, tree_matrix::md_none);
ml.push (tmp);
}
else
{
tree_matrix *tmp = ml.pop ();
tmp = tmp->chain ($1, tree_matrix::md_down);
ml.push (tmp);
}
}
| matrix_row1 ',' expression
{
tree_matrix *tmp = ml.pop ();
tmp = tmp->chain ($3, tree_matrix::md_right);
ml.push (tmp);
}
;
%%
// Generic error messages.
static void
yyerror (char *s)
{
char *line = current_input_line;
int err_col = current_input_column - 1;
ostrstream output_buf;
if (reading_fcn_file || reading_script_file)
output_buf << "parse error near line " << input_line_number
<< " of file " << curr_fcn_file_name << ".m:";
else
output_buf << "parse error:";
if (s && strcmp (s, "parse error") != 0)
output_buf << "\n\n " << s;
output_buf << "\n\n";
if (line)
{
int len = strlen (line);
if (line[len-1] == '\n')
{
len--;
line[len] = '\0';
}
// Print the line, maybe with a pointer near the error token.
output_buf << ">>> " << 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" << ends;
char *msg = output_buf.str ();
parse_error ("%s", msg);
delete [] msg;
}
// Error mesages for mismatched end tokens.
static void
end_error (char *type, token::end_tok_type ettype, int l, int c)
{
static char *fmt = "`%s' command matched by `%s' near line %d column %d";
switch (ettype)
{
case token::simple_end:
error (fmt, type, "end", l, c);
break;
case token::for_end:
error (fmt, type, "endfor", l, c);
break;
case token::function_end:
error (fmt, type, "endfunction", l, c);
break;
case token::if_end:
error (fmt, type, "endif", l, c);
break;
case token::while_end:
error (fmt, type, "endwhile", l, c);
break;
default:
panic_impossible ();
break;
}
}
// Check to see that end tokens are properly matched.
static int
check_end (token *tok, token::end_tok_type expected)
{
token::end_tok_type ettype = tok->ettype ();
if (ettype != expected && ettype != token::simple_end)
{
yyerror ("parse error");
int l = tok->line ();
int c = tok->column ();
switch (expected)
{
case token::for_end:
end_error ("for", ettype, l, c);
break;
case token::function_end:
end_error ("function", ettype, l, c);
break;
case token::if_end:
end_error ("if", ettype, l, c);
break;
case token::while_end:
end_error ("while", ettype, l, c);
break;
default:
panic_impossible ();
break;
}
return 1;
}
else
return 0;
}
// Try to figure out early if an expression should become an
// assignment to the builtin variable ans.
//
// Need to make sure that the expression isn't already an identifier
// that has a name, or an assignment expression.
//
// Note that an expression can't be just an identifier anymore -- it
// must at least be an index expression (see the definition of the
// non-terminal `variable' above).
//
// XXX FIXME XXX. This isn't quite sufficient. For example, try the
// command `x = 4, x' for `x' previously undefined.
//
// XXX FIXME XXX -- we should probably delay doing this until eval-time.
static tree_expression *
maybe_convert_to_ans_assign (tree_expression *expr)
{
if (expr->is_index_expression ())
{
expr->mark_for_possible_ans_assign ();
return expr;
}
else if (expr->is_assignment_expression ()
|| expr->is_prefix_expression ())
{
return expr;
}
else
{
symbol_record *sr = global_sym_tab->lookup ("ans", 1, 0);
assert (sr);
tree_identifier *ans = new tree_identifier (sr);
return new tree_simple_assignment_expression (ans, expr, 0, 1);
}
}
// Maybe print a warning if an assignment expression is used as the
// test in a logical expression.
static void
maybe_warn_assign_as_truth_value (tree_expression *expr)
{
if (user_pref.warn_assign_as_truth_value
&& expr->is_assignment_expression ()
&& expr->in_parens < 2)
{
warning ("suggest parenthesis around assignment used as truth value");
}
}
// Build a binary expression.
static tree_expression *
make_binary_op (int op, tree_expression *op1, token *tok_val,
tree_expression *op2)
{
tree_expression::type t;
switch (op)
{
case POW:
t = tree_expression::power;
break;
case EPOW:
t = tree_expression::elem_pow;
break;
case '+':
t = tree_expression::add;
break;
case '-':
t = tree_expression::subtract;
break;
case '*':
t = tree_expression::multiply;
break;
case '/':
t = tree_expression::divide;
break;
case EMUL:
t = tree_expression::el_mul;
break;
case EDIV:
t = tree_expression::el_div;
break;
case LEFTDIV:
t = tree_expression::leftdiv;
break;
case ELEFTDIV:
t = tree_expression::el_leftdiv;
break;
case EXPR_LT:
t = tree_expression::cmp_lt;
break;
case EXPR_LE:
t = tree_expression::cmp_le;
break;
case EXPR_EQ:
t = tree_expression::cmp_eq;
break;
case EXPR_GE:
t = tree_expression::cmp_ge;
break;
case EXPR_GT:
t = tree_expression::cmp_gt;
break;
case EXPR_NE:
t = tree_expression::cmp_ne;
break;
case EXPR_AND_AND:
t = tree_expression::and_and;
break;
case EXPR_OR_OR:
t = tree_expression::or_or;
break;
case EXPR_AND:
t = tree_expression::and;
break;
case EXPR_OR:
t = tree_expression::or;
break;
default:
panic_impossible ();
break;
}
int l = tok_val->line ();
int c = tok_val->column ();
return new tree_binary_expression (op1, op2, t, l, c);
}
// Build a prefix expression.
static tree_expression *
make_prefix_op (int op, tree_identifier *op1, token *tok_val)
{
tree_expression::type t;
switch (op)
{
case PLUS_PLUS:
t = tree_expression::increment;
break;
case MINUS_MINUS:
t = tree_expression::decrement;
break;
default:
panic_impossible ();
break;
}
int l = tok_val->line ();
int c = tok_val->column ();
return new tree_prefix_expression (op1, t, l, c);
}
// Build a postfix expression.
static tree_expression *
make_postfix_op (int op, tree_identifier *op1, token *tok_val)
{
tree_expression::type t;
switch (op)
{
case PLUS_PLUS:
t = tree_expression::increment;
break;
case MINUS_MINUS:
t = tree_expression::decrement;
break;
default:
panic_impossible ();
break;
}
int l = tok_val->line ();
int c = tok_val->column ();
return new tree_postfix_expression (op1, t, l, c);
}
// Build a unary expression.
static tree_expression *
make_unary_op (int op, tree_expression *op1, token *tok_val)
{
tree_expression::type t;
switch (op)
{
case QUOTE:
t = tree_expression::hermitian;
break;
case TRANSPOSE:
t = tree_expression::transpose;
break;
case EXPR_NOT:
t = tree_expression::not;
break;
case '-':
t = tree_expression::uminus;
break;
default:
panic_impossible ();
break;
}
int l = tok_val->line ();
int c = tok_val->column ();
return new tree_unary_expression (op1, t, l, c);
}
// Make an expression that handles assignment of multiple values.
static tree_expression *
make_multi_val_ret (tree_expression *rhs, int l, int c)
{
// Convert the matrix list to a list of identifiers. If that fails,
// we can abort here, without losing anything -- no other possible
// syntax is valid if we've seen the equals sign as the next token
// after the `]'.
tree_expression *retval = 0;
maybe_screwed_again--;
tree_matrix *tmp = ml.pop ();
tmp = tmp->reverse ();
tree_return_list *id_list = tmp->to_return_list ();
if (id_list)
{
int list_len = id_list->length ();
if (list_len == 1)
{
tree_index_expression *lhs = id_list->remove_front ();
retval = new tree_simple_assignment_expression (lhs, rhs,
0, 0, l, c);
}
else if (list_len > 1)
{
if (rhs->is_multi_val_ret_expression ())
{
tree_multi_val_ret *t = (tree_multi_val_ret *) rhs;
retval = new tree_multi_assignment_expression (id_list, t, l, c);
}
else
yyerror ("RHS must be an expression that returns multiple values");
}
else
panic_impossible ();
}
else
yyerror ("invalid identifier list for assignment");
return retval;
}
static tree_index_expression *
make_index_expression (tree_indirect_ref *indir, tree_argument_list *args)
{
tree_index_expression *retval = 0;
int l = indir->line ();
int c = indir->column ();
if (indir->is_identifier_only ())
{
indir->preserve_identifier ();
retval = new tree_index_expression (indir->ident (), args, l, c);
delete indir;
}
else
retval = new tree_index_expression (indir, args, l, c);
return retval;
}