/*	Copyright (C) 1994 Free Software Foundation, Inc.

This program 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.

This program 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 this software; see the file COPYING.  If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */

/*
 * Tom Lord
 * Cygnus Support
 */

%{
#include "yourscm.h"

extern int parse_line_no;
extern YYSTYPE parse_answer;

extern YYSTYPE parse_cons ();
extern YYSTYPE parse_2list ();
extern YYSTYPE parse_append ();
extern YYSTYPE parse_append_optcons ();
extern YYSTYPE parse_eol;
extern YYSTYPE parse_false;
extern YYSTYPE parse_define_sym;
extern YYSTYPE parse_SCM_sym;
extern YYSTYPE parse_if_sym;
extern YYSTYPE parse_while_sym;
extern YYSTYPE parse_for_sym;
extern YYSTYPE parse_return_sym;
extern YYSTYPE parse_break_sym;
extern YYSTYPE parse_continue_sym;
extern YYSTYPE parse_comma_sym;
extern YYSTYPE parse_begin_sym;
extern YYSTYPE parse_do_sym;
extern YYSTYPE parse_neg_sym;
extern YYSTYPE parse_log_neg_sym;
extern YYSTYPE parse_pos_sym;
extern YYSTYPE parse_bit_neg_sym;
extern YYSTYPE parse_bit_and_sym;
extern YYSTYPE parse_times_sym;
extern YYSTYPE parse_div_sym;
extern YYSTYPE parse_mod_sym;
extern YYSTYPE parse_plus_sym;
extern YYSTYPE parse_minus_sym;
extern YYSTYPE parse_lshift_sym;
extern YYSTYPE parse_rshift_sym;
extern YYSTYPE parse_eq_sym;
extern YYSTYPE parse_ne_sym;
extern YYSTYPE parse_le_sym;
extern YYSTYPE parse_ge_sym;
extern YYSTYPE parse_lt_sym;
extern YYSTYPE parse_gt_sym;
extern YYSTYPE parse_bit_and_sym;
extern YYSTYPE parse_bit_xor_sym;
extern YYSTYPE parse_bit_or_sym;
extern YYSTYPE parse_log_and_sym;
extern YYSTYPE parse_log_or_sym;
extern YYSTYPE parse_assign_sym;
extern YYSTYPE parse_if_exp_sym;
extern YYSTYPE parse_apply_sym;
%}

%right '?' ':'
%left ','
%left '='
%left ctax_or_lx
%left ctax_and_lx
%left '|'
%left '^'
%left '&'
%left ctax_eq_lx ctax_ne_lx
%left '<' '>' ctax_le_lx ctax_ge_lx
%left ctax_lshift_lx ctax_rshift_lx
%left '+' '-'
%left '*' '/' '%'
%right UNARY

%token ctax_number_lx
%token ctax_id_lx
%token ctax_SCM_lx
%token ctax_if_lx
%token ctax_else_lx
%token ctax_for_lx
%token ctax_while_lx
%token ctax_return_lx
%token ctax_do_lx
%token ctax_break_lx
%token ctax_continue_lx
%token ctax_interactive_lx
%token ctax_string_lx
%token ctax_char_lx

%%
command:		definition
			  	     { parse_answer = $1; YYACCEPT; }
			| statement
			  	     { parse_answer = $1; YYACCEPT; }
			;

definition:		ctax_SCM_lx ctax_id_lx opt_initializer ';'
				     {
				       $$ = 
				       parse_cons
					 (parse_SCM_sym,
					  parse_2list
					  (parse_cons ($2, parse_eol), $3));
				     }

			| ctax_SCM_lx symbol_list ';'
				     {
				       $$ = 
				       parse_cons
					 (parse_SCM_sym,
					  parse_2list
					  ($2, parse_number ("0")));
				     }
			

			| ctax_SCM_lx ctax_id_lx definition_form
				     {
				       $$ =
				       parse_cons
				       (parse_define_sym,
				        parse_cons
				        ($2, $3));
				     }
			;

symbol_list:		ctax_id_lx
				     { $$ = parse_cons ($1, parse_eol); }
			| symbol_list ',' ctax_id_lx
				     { $$ = parse_append ($1, $3); }
			;

opt_initializer:	'=' expression
			    	     { $$ = $2; }
			|
				     { $$ = parse_number ("0"); }
			;

definition_form:	'(' parameter_list ')' opt_doc opt_interaction body
				     {
				       $$ =
					 parse_cons
					   ($2,
					    parse_cons
					    ($4, parse_2list ($5, $6)));
				     }
			;

parameter_list:		ctax_id_lx
				     { $$ = parse_cons ($1, parse_eol); }
			| ctax_SCM_lx ctax_id_lx
				     { $$ = parse_cons ($2, parse_eol); }
			| parameter_list ',' ctax_id_lx
				     { $$ = parse_append ($1, $3); }
			|
				     { $$ = parse_eol; }
			;

opt_doc:		ctax_string_lx
				     { $$ = $1; }
			|
				     { $$ = parse_false; }
			;

opt_interaction:	ctax_interactive_lx expression
				     { $$ = $2; }
			|
				     { $$ = parse_false; }
			;

body:			'{' local_definition_list statement_list '}'
				     {
				       $$
				       = parse_cons
				         (parse_begin_sym,
					  parse_cons ($2, $3));
				      }
			;

local_definition_list:	definition
				     { $$ = parse_cons ($1, parse_eol); }
			| local_definition_list definition
				     { $$ = parse_append ($1, $2); }
			|
				     { $$ = parse_eol; }
			;

statement_list:		statement
				     { $$ = parse_cons ($1, parse_eol); }
			| statement_list statement
				     { $$ = parse_append ($1, $2); }
			;

statement:		expression ';'
				     { $$ = $1; }
			| ctax_if_lx '(' expression ')' statement opt_else
				     {
				       $$
				       = parse_cons (parse_if_sym,
						     parse_cons
						     ($3,
						      parse_2list ($5, $6)));
				      }
			| ctax_while_lx '(' expression ')' statement
				     {
				       $$
				       = parse_cons (parse_while_sym,
						     parse_2list ($3, $5));
				      }
			| ctax_for_lx '(' exp1 ';' exp1 ';' exp1  ')'
			    statement
				     {
				       $$
				       = parse_cons
				         (parse_for_sym,
					  parse_cons
					  ($3,
					   parse_cons ($5,
						       parse_2list ($7, $9))));
				      }
			| ctax_do_lx
			     statement
			  ctax_while_lx '(' expression ')' ';'
				     {
				       $$
				       = parse_cons (parse_do_sym,
						     parse_2list ($5, $2));
				      }
			| '{' local_definition_list statement_list '}'
				     {
				       $$
				       = parse_cons
				         (parse_begin_sym,
					  parse_cons ($2, $3));
				      }
			| ctax_return_lx opt_expression ';'
				     {
				       $$
				       = parse_2list (parse_return_sym, $2);
				      }
			| ctax_break_lx ';'
				     { $$ = parse_break_sym; }

			| ctax_continue_lx ';'
				     { $$ = parse_continue_sym; }
			;

opt_expression:		expression
				     { $$ = $1; }
			|
				     { $$ = parse_false; }
			;

opt_else:		ctax_else_lx statement
				     { $$ = $2; }
			|
				     { $$ = parse_false; }
			;


expression:		exp1
			   { $$ = $1; }

exp1	:	exp
			   { $$ = $1; }
	|	exp1 ',' exp
			   {
			     $$ = parse_cons (parse_comma_sym,
					      parse_2list ($1, $3));
			   }
	;

/* Expressions, not including the comma operator.  */
exp	:	'-' exp    %prec UNARY
			   {
			     $$ = parse_2list (parse_neg_sym, $2);
			   }
	|	'!' exp    %prec UNARY
			   {
			     $$ = parse_2list (parse_log_neg_sym, $2);
			   }
	|	'+' exp    %prec UNARY
			   {
			     $$ = parse_2list (parse_pos_sym, $2);
			   }
	|	'~' exp    %prec UNARY
			   {
			     $$ = parse_2list (parse_bit_neg_sym, $2);
			   }
	|	'(' exp1 ')'
			   {
			     $$ = $2;
			   }
	|	exp '(' arg_list ')'
			   { $$ = parse_cons (parse_apply_sym,
					       parse_2list ($1, $3)); }

	;

/* Binary operators in order of decreasing precedence.  */
exp	:	exp '=' exp
			   {
			     $$ =
			     parse_cons (parse_assign_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '*' exp
			   {
			     $$ =
			     parse_cons (parse_times_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '/' exp
			   {
			     $$ =
			     parse_cons (parse_div_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '%' exp
			   {
			     $$ =
			     parse_cons (parse_mod_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '+' exp
			   {
			     $$ =
			     parse_cons (parse_plus_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '-' exp
			   {
			     $$ =
			     parse_cons (parse_minus_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_lshift_lx exp
			   {
			     $$ =
			     parse_cons (parse_lshift_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_rshift_lx exp
			   {
			     $$ =
			     parse_cons (parse_rshift_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_eq_lx exp
			   {
			     $$ =
			     parse_cons (parse_eq_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_ne_lx exp
			   {
			     $$ =
			     parse_cons (parse_ne_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_le_lx exp
			   {
			     $$ =
			     parse_cons (parse_le_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_ge_lx exp
			   {
			     $$ =
			     parse_cons (parse_ge_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '<' exp
			   {
			     $$ =
			     parse_cons (parse_lt_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '>' exp
			   {
			     $$ =
			     parse_cons (parse_gt_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '&' exp
			   {
			     $$ =
			     parse_cons (parse_bit_and_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '^' exp
			   {
			     $$ =
			     parse_cons (parse_bit_xor_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '|' exp
			   {
			     $$ =
			     parse_cons (parse_bit_or_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_and_lx exp
			   {
			     $$ =
			     parse_cons (parse_log_and_sym,
					  parse_2list ($1, $3));
			   }
	|	exp ctax_or_lx exp
			   {
			     $$ =
			     parse_cons (parse_log_or_sym,
					  parse_2list ($1, $3));
			   }
	|	exp '?' exp ':' exp
			   {
			     $$ =
			     parse_cons (parse_if_exp_sym,
					  parse_cons ($1, 
						       parse_2list ($3,$5)));
			   }
	|	ctax_number_lx
			   { $$ = $1; }
	|	ctax_char_lx
			   { $$ = $1; }
	|	ctax_string_lx
			   { $$ = $1; }
	|	ctax_id_lx
			   { $$ = $1; }
	;

arg_list:	exp
		        { $$ = parse_cons ($1, parse_eol); }
	|	arg_list ',' exp
			{ $$ = parse_append ($1, $3); }
	|
			{ $$ = parse_eol; }


%%

int parse_line_no;
#include "yourscm.ch"
