%{

#ifndef lint
static char *RCSid = "$Id: yaccsrc.y,v 1.15 1993/05/07 21:31:04 anders Exp anders $";
#endif

/*
 *  The Regina Rexx Interpreter
 *  Copyright (C) 1992-1994  Anders Christensen <anders@pvv.unit.no>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
    This code modified for Win32 port by Ataman Software, Inc. June 29, 1995.
*/

#include <time.h>
#include "rexx.h"
/* #if defined(SGI)
 * #include <alloca.h>
 * #endif
 */
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>


#define YYSTYPE nodeptr

extern char retvalue[] ;
extern int retlength ;
extern int nextstart, nextline, tline, tstart ;
extern sysinfobox *systeminfo ;
extern nodeptr parseroot ;
int tmplno, tmpchr ;

void checkconst( nodeptr this ) ;
void transform( nodeptr this ) ;
nodeptr create_head( char *name ) ;
void checkout( void ) ;
nodeptr makenode( int type, int numb, ... ) ;
streng *stralloc( char *ptr ) ;
void checkdosyntax( nodeptr this ) ;
void newlabel( nodeptr this ) ;

%}

%token ADDRESS ARG CALL DO TO BY FOR WHILE UNTIL EXIT IF THEN ELSE
%token ITERATE INTERPRET LEAVE NOP NUMERIC PARSE EXTERNAL SOURCE VAR
%token VALUE WITH PROCEDURE EXPOSE PULL PUSH QUEUE SAY RETURN SELECT
%token WHEN DROP OTHERWISE SIGNAL ON OFF ERROR SYNTAX HALT NOVALUE
%token TRACE END UPPER ASSIGNMENTVARIABLE STATSEP FOREVER DIGITS FORM
%token FUZZ SCIENTIFIC ENGINEERING NOT CONCATENATE MODULUS GTE GT LTE
%token LT DIFFERENT EQUALEQUAL NOTEQUALEQUAL OFFSET SPACE EXP XOR
%token PLACEHOLDER NOTREADY CONSYMBOL SIMSYMBOL EXFUNCNAME INFUNCNAME
%token LABEL DOVARIABLE HEXSTRING STRING VERSION LINEIN WHATEVER NAME
%token FAILURE BINSTRING ENVIRONMENT OPTIONS

%start prog

%left '|' XOR
%left '&'
%nonassoc '=' DIFFERENT GTE GT LT LTE EQUALEQUAL NOTEQUALEQUAL
%left CONCATENATE SPACE CCAT
%left '+' '-'
%left '*' '/' '%' MODULUS
%left EXP
%left UMINUS UPLUSS NOT
%nonassoc SYNTOP

%nonassoc THEN
%nonassoc ELSE

%right STATSEP

%{ 
#ifdef NDEBUG
# define YYDEBUG 0
#else
# define YYDEBUG 1
#endif
%}

%%

prog         : nseps stats { parseroot = $2 ; checkout() ; }
             | nseps       { parseroot = NULL ; checkout() ; }
             ;

stats        : ystatement stats         { $$->next = $2 ; }
             | ystatement               { $$->next = NULL ; }
             ;

xstats       : statement xstats         { $$->next = $2 ; }
             | statement gruff          { $$->next = NULL ; }
             ;

ystatement   : statement               { $$ = $1 ; }
             | lonely_end        { exiterror( ERR_UNMATCHED_END ) ; }
             ;

lonely_end   : gruff end seps 
             ;

nxstats      : xstats                  { $$ = $1 ; }
             | gruff                   { $$ = NULL ; }
             ;

nseps        : seps
             | 
             ;

seps         : STATSEP seps 
             | STATSEP      
             ;

statement    : mstatement   { $$=$1; }
             | ex_when_stat { $$=$1; }
             ;

mstatement   : mttstatement { $$=$1; }
             | unexp_then   { $$=$1; }
             ;

gruff	     : { tmpchr=tstart; tmplno=tline; } 
             ;

mttstatement : gruff mtstatement { $$=$2; }
             ;

mtstatement  : address_stat
             | expr_stat
             | arg_stat
             | call_stat
             | do_stat 
             | drop_stat
             | exit_stat
             | if_stat 
             | unexp_else
             | ipret_stat
             | iterate_stat
             | label_stat
             | leave_stat
             | nop_stat
             | numeric_stat
             | options_stat
             | parse_stat
             | proc_stat
             | pull_stat
             | push_stat
             | queue_stat
             | return_stat
             | say_stat
             | select_stat
             | signal_stat
             | trace_stat
             | assignment
             ;

call         : CALL		       { $$ = makenode(X_CALL,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
do           : DO 		       { $$ = makenode(X_DO,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
exit         : EXIT		       { $$ = makenode(X_EXIT,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
if           : IF		       { $$ = makenode(X_IF,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
iterate      : ITERATE  	       { $$ = makenode(X_ITERATE,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
leave        : LEAVE		       { $$ = makenode(X_LEAVE,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
say          : SAY		       { $$ = makenode(X_SAY,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
return       : RETURN		       { $$ = makenode(X_RETURN,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
address      : ADDRESS                 { $$ = makenode(X_ADDR_N,0) ;
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
arg          : ARG                     { $$ = makenode(X_PARSE_ARG_U,0) ;
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
drop         : DROP                    { $$ = makenode(X_DROP,0) ;
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
interpret    : INTERPRET               { $$ = makenode(X_IPRET,0) ;
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
label        : LABEL                   { $$ = makenode(X_LABEL,0) ;
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
nop          : NOP                     { $$ = makenode(X_NULL,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; }
numeric      : NUMERIC                 { $$ = makenode(0,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
options      : OPTIONS                 { $$ = makenode(X_OPTIONS,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
parse        : PARSE                   { $$ = makenode(0,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
proc         : PROCEDURE               { $$ = makenode(X_PROC,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
pull         : PULL                    { $$ = makenode(X_PULL,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
push         : PUSH                    { $$ = makenode(X_PUSH,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
queue        : QUEUE                   { $$ = makenode(X_QUEUE,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
select       : SELECT                  { $$ = makenode(X_SELECT,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
signal       : SIGNAL                  { $$ = makenode(X_SIG_LAB,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
when         : WHEN                    { $$ = makenode(X_WHEN,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
otherwise    : OTHERWISE               { $$ = makenode(X_OTHERWISE,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
trace        : TRACE                   { $$ = makenode(X_TRACE,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } ;
address_stat : address VALUE expr seps { $$ = $1 ;
                                         $$->type = X_ADDR_V ;
                                         $$->p[0] = $3 ; }
             | address seps            { $$ = $1 ;
                                         $$->type = X_ADDR_S ; }
             | address nvir nexpr seps { $$ = $1 ;
                                         $$->type = X_ADDR_N ;
                                         $$->p[0] = $3 ;
                                         $$->name = (streng *)$2 ; }
             | address expr seps       { $$ = $1 ;
                                         $$->type = X_ADDR_V ;
                                         $$->p[0] = $2 ; }
             ;

arg_stat     : arg templs seps         { $$ = $1 ;
                                         $$->p[0] = $2 ; }
             ;

call_stat    : call asymbol exprs seps { $$ = $1 ;
                                         $$->p[0] = $3 ;
                                         $$->name = (streng *) $2 ; }
             | call string exprs seps  { $$ = $1 ;
                                         $$->type = X_EX_FUNC ;
                                         $$->p[0] = $3 ;
                                         $$->name = (streng *) $2 ; }
             | call function seps      { $$ = $1 ;
                                         $$->name = $2->name ;
                                         $$->p[0] = $2->p[0] ;
                                         Free( $2 ) ; }
             | call on error seps      { exiterror( ERR_SYMBOL_EXPECTED );}
             | call off error seps     { exiterror( ERR_SYMBOL_EXPECTED );}
             | call on c_action error seps 
                                       { exiterror( ERR_INV_SUBKEYWORD );}
             | call on c_action namespec error seps  
                                       { exiterror( ERR_EXTRA_DATA );}
             | call on c_action namespec seps  
                                       { $$ = $1 ;
                                         $$->type = X_CALL_SET ;
                                         $$->p[0] = $2 ;
                                         $$->name = (streng *)$4 ;
                                         $$->p[1] = $3 ; }
             | call on c_action seps   { $$ = $1 ;
                                         $$->type = X_CALL_SET ;
                                         $$->p[0] = $2 ;
                                         $$->name = NULL ;
                                         $$->p[1] = $3 ; }
             | call off c_action error seps 
                                       { exiterror( ERR_EXTRA_DATA );}
             | call off c_action seps  { $$ = $1 ;
                                         $$->type = X_CALL_SET ;
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $3 ; }
             ;

expr_stat    : expr seps
                                       { $$ = makenode(X_COMMAND,0) ; 
                                         $$->charnr = tmpchr ; 
				         $$->lineno = tmplno; 
                                         $$->p[0] = $1 ; } 
             ;

end_stat     : END                     { $$ = makenode(X_END,0) ;
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } 
             ;

end          : end_stat simsymb       { $$ = $1 ;
                                         $$->name = (streng*)($2) ; } 
             | end_stat                { $$ = $1 ; }
             | end_stat simsymb error { exiterror( ERR_EXTRA_DATA );}
             ;

do_stat      :  do repetitor conditional seps nxstats end seps 
                                       { $$ = $1 ;
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $3 ;
                                         $$->p[2] = $5 ; 
                                         $$->p[3] = $6 ; 
                          if (($$->p[0]==NULL || $$->p[0]->name==NULL) 
                              && $$->p[3]->name)
                                           exiterror( ERR_UNMATCHED_END );
                          if (($$->p[0])&&($$->p[0]->name)&&
                              ($$->p[3]->name)&&
                              (($$->p[3]->name->len != $$->p[0]->name->len)||
                               (strncmp($$->p[3]->name->value,
                                        $$->p[0]->name->value,
                                        $$->p[0]->name->len))))
                                           exiterror( ERR_UNMATCHED_END ) ;
                                       }
             ;

repetitor    : dovar '=' expr tobyfor tobyfor tobyfor
                                       { $$ =makenode(X_REP,4,$3,$4,$5,$6) ;
                                         $$->name = (streng *)$1 ; 
                                         checkdosyntax($$) ; }
             | dovar '=' expr tobyfor tobyfor
                                       { $$ =makenode(X_REP,3,$3,$4,$5) ;
                                         $$->name = (streng *)$1 ; 
                                         checkdosyntax($$) ; }
             | dovar '=' expr tobyfor  { $$ =makenode(X_REP,2,$3,$4) ;
                                         $$->name = (streng *)$1 ; 
                                         checkdosyntax($$) ; }
             | dovar '=' expr          { $$ =makenode(X_REP,1,$3) ;
                                         $$->name = (streng *)$1 ; 
                                         checkdosyntax($$) ; }
             | FOREVER                 { $$ = makenode(X_REP_FOREVER,0) ; }
             | FOREVER error seps      { exiterror( ERR_EXTRA_DATA );}
             | expr                    { $1 = makenode(X_DO_FOR,1,$1) ;
                                         $$ = makenode(X_REP,2,NULL,$1) ; } 
             |                         { $$ = NULL ; }  
             ; 

nvir         : ENVIRONMENT             { $$ = (nodeptr)stralloc(retvalue) ; }
             ;

dovar        : DOVARIABLE              { $$ = (nodeptr)stralloc(retvalue) ; }
             ;

tobyfor      : TO expr                 { $$ = makenode(X_DO_TO,1,$2) ; }
             | FOR expr                { $$ = makenode(X_DO_FOR,1,$2) ; }
             | BY expr                 { $$ = makenode(X_DO_BY,1,$2) ; }
             ;

conditional  : WHILE expr              { $$ = makenode(X_WHILE,1,$2) ; }
             | UNTIL expr              { $$ = makenode(X_UNTIL,1,$2) ; }
             |                         { $$ = NULL ; }
             ;

drop_stat    : drop anyvars seps       { $$ = $1 ;
                                         $$->p[0] = $2 ; }
             ;

exit_stat    : exit nexpr seps         { $$ = $1 ;
                                         $$->p[0] = $2 ; }
             ;

if_stat      : if expr nseps THEN nseps ystatement 
                                       { $$ = $1 ;
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $6 ; } 
             | if expr nseps THEN nseps ystatement ELSE nseps ystatement 
                                       { $$ = $1 ;
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $6 ;
                                         $$->p[2] = $9 ; } 
             | if expr nseps THEN nseps ystatement ELSE nseps error
                                       { exiterror( ERR_INCOMPLETE_STRUCT );}
             | if expr nseps THEN nseps error 
                                       { exiterror( ERR_INCOMPLETE_STRUCT );}
             | if seps                 { exiterror( ERR_INCOMPLETE_STRUCT );}
             | if expr nseps error     { exiterror( ERR_THEN_EXPECTED ) ; }
             ;

unexp_then   : gruff THEN              { exiterror( ERR_THEN_UNEXPECTED ) ; }
             ;

unexp_else   : gruff ELSE              { exiterror( ERR_THEN_UNEXPECTED ) ; }
             ;

ipret_stat   : interpret expr seps     { $$ = $1 ;
                                         $$->p[0] = $2 ; }
             ;


iterate_stat : iterate simsymb seps    { $$ = $1 ; 
                                         $$->name = (streng *) $2 ; }
             | iterate simsymb error seps 
                                       { exiterror( ERR_EXTRA_DATA );}
             | iterate seps            { $$ = $1 ; }
             ;

label_stat   : labelname nseps         { $$ = $1 ; 
                                         newlabel( $1 ) ; }
             ;

labelname    : label                   { $$ = $1 ;
                                         $$->name = stralloc(retvalue) ; }
             ;

leave_stat   : leave simsymb seps      { $$ = $1 ;
                                         $$->name = (streng *) $2 ; } 
             | leave simsymb error seps
                                       { exiterror( ERR_EXTRA_DATA );}
             | leave seps              { $$ = $1 ; }
             ;

nop_stat     : nop seps                { $$ = $1 ; }
             | nop error seps          { exiterror( ERR_EXTRA_DATA );}
             ;

numeric_stat : numeric DIGITS expr seps { $$ = $1 ;
                                         $$->type = X_NUM_D ; 
                                         $$->p[0] = $3 ; }
             | numeric DIGITS seps     { $$ = $1; $$->type = X_NUM_DDEF ; }
             | numeric FORM form_expr error seps 
                                       { exiterror( ERR_EXTRA_DATA);}
             | numeric FORM form_expr seps  
         			       { $$ = $1 ;
                                         $$->type = X_NUM_F ; 
                                         $$->p[0] = $3 ; }
	     | numeric FORM seps       { $$ = $1 ; $$->type=X_NUM_FRMDEF ;}
	     | numeric FORM expr seps  { $$ = $1 ; $$->type=X_NUM_V ;
                                         $$->p[0] = $3 ; }
             | numeric FUZZ seps       { $$ = $1; $$->type = X_NUM_FDEF ;}
             | numeric FUZZ expr seps  { $$ = $1 ;
                                         $$->type = X_NUM_FUZZ ; 
                                         $$->p[0] = $3 ; }
             | numeric error seps      { exiterror( ERR_INV_SUBKEYWORD );}
             ;

form_expr    : SCIENTIFIC              { $$ = makenode(X_NUM_SCI,0) ; } 
             | ENGINEERING             { $$ = makenode(X_NUM_ENG,0) ; }
             ;

options_stat : options nexpr seps      { ($$=$1)->p[0]=$2 ; }
             ;

parse_stat   : parse parse_param template seps
                                       { $$ = $1 ;
                                         $$->type = X_PARSE ; 
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $3 ; }
             | parse UPPER parse_param template seps
                                       { $$ = $1 ;
                                         $$->type = X_PARSE_U ; 
                                         $$->p[0] = $3 ;
                                         $$->p[1] = $4 ; }
             | parse ARG templs seps   { $$ = $1 ;
                                         $$->type = X_PARSE_ARG ; 
                                         $$->p[0] = $3 ; }
             | parse UPPER ARG templs seps 
                                       { $$ = $1 ;
                                         $$->type = X_PARSE_ARG_U ; 
                                         $$->p[0] = $4 ; }
             | parse error seps        { exiterror( ERR_INV_SUBKEYWORD );}
             ;

templs       : template ',' templs     { $$ = $1 ; $$->next = $3 ; }
             | template                { $$ = $1 ; }
             ;

parse_param  : LINEIN                  { $$ = makenode(X_PARSE_EXT,0) ; }
             | EXTERNAL                { $$ = makenode(X_PARSE_EXT,0) ; }
             | VERSION                 { $$ = makenode(X_PARSE_VER,0) ; }
             | PULL                    { $$ = makenode(X_PARSE_PULL,0) ; }
             | SOURCE                  { $$ = makenode(X_PARSE_SRC,0) ; }
             | VAR simsymb             { $$ = makenode(X_PARSE_VAR,0) ; 
                                         $$->name = (streng *) $2 ; }
             | VALUE nexpr WITH        { $$ = makenode(X_PARSE_VAL,1,$2) ; }
             ;

proc_stat    : proc seps               { $$ = $1 ; }
             | proc error seps	      	{ exiterror( ERR_INV_SUBKEYWORD );}
             | proc EXPOSE error seps   { exiterror( ERR_SYMBOL_EXPECTED);}
             | proc EXPOSE anyvars error seps 
                                        { exiterror( ERR_SYMBOL_EXPECTED);}
             | proc EXPOSE anyvars seps { $$ = $1 ; 
                                         $$->p[0] = $3 ; }
             ;

pull_stat    : pull template seps      { $$ = $1 ; 
                                         $$->p[0] = $2 ; }
             ;

push_stat    : push nexpr seps         { $$ = $1 ; 
                                         $$->p[0] = $2 ; }
             ;

queue_stat   : queue nexpr seps        { $$ = $1 ;
                                         $$->p[0] = $2 ; } 
             ;

say_stat     : say nexpr seps          { $$ = $1 ;
                                         $$->p[0] = $2 ; }
             ;

return_stat  : return nexpr seps       { $$ = $1 ; 
                                         $$->p[0] = $2 ; }
             ;

sel_end      : END simsymb             { exiterror( ERR_UNMATCHED_END );}
             | END simsymb error       { exiterror( ERR_EXTRA_DATA );}
             | END
             ;

select_stat  : select seps when_stats otherwise_stat sel_end seps
                                       { $$ = $1 ;
                                         $$->p[0] = $3 ;
                                         $$->p[1] = $4 ; }
             | select seps END error   { exiterror( ERR_WHEN_EXPECTED );}
             | select seps otherwise error 
                                       { exiterror( ERR_WHEN_EXPECTED );}
             | select error            { exiterror( ERR_EXTRA_DATA ) ; }
             | select seps THEN	       { exiterror( ERR_THEN_UNEXPECTED );}
             | select seps when_stats otherwise error
                                       { exiterror( ERR_INCOMPLETE_STRUCT );}
             ;

when_stats   : when_stat when_stats    { $$->next = $2 ; }
             | when_stat               { $$ = $1 ; }
             | error                   { exiterror( ERR_WHEN_EXPECTED ) ;}
             ;

when_stat    : when expr nseps THEN nseps statement 
                                       { $$ = $1 ;
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $6 ; } 
             | when expr nseps THEN nseps statement THEN 
                                       { exiterror( ERR_THEN_UNEXPECTED );} 
             | when expr seps 
                                       { exiterror( ERR_THEN_EXPECTED ) ; }
             | when error              { exiterror( ERR_INVALID_EXPRESSION );}
             ;

when_or_other: when 
	     | otherwise
             ;

ex_when_stat : gruff when_or_other    { exiterror( ERR_WHEN_UNEXPECTED );}
             ;

otherwise_stat : otherwise nseps nxstats {
                                         $$ = $1 ; 
                                         $$->p[0] = $3 ; }
             |                         { $$ = makenode(X_NO_OTHERWISE,0) ; 
                                         $$->lineno = tline ;
                                         $$->charnr = tstart ; } 
             ;


signal_stat : signal VALUE expr seps   { $$ = $1 ;
                                         $$->type = X_SIG_VAL ; 
                                         $$->p[0] = $3 ; }
            | signal asymbol error seps { exiterror( ERR_EXTRA_DATA );}
            | signal asymbol seps      { $$ = $1 ;
                                         $$->name = (streng *)$2 ; }
            | signal on error seps     { exiterror( ERR_SYMBOL_EXPECTED );}
            | signal off error seps    { exiterror( ERR_SYMBOL_EXPECTED );}
            | signal on s_action error seps
                                       { exiterror( ERR_INV_SUBKEYWORD );}
            | signal on s_action namespec error seps
                                       { exiterror( ERR_EXTRA_DATA );}
            | signal on s_action namespec seps  
                                       { $$ = $1 ;
                                         $$->type = X_SIG_SET ;
                                         $$->p[0] = $2 ;
                                         $$->name = (streng *)$4 ;
                                         $$->p[1] = $3 ; }
            | signal on s_action seps  
                                       { $$ = $1 ;
                                         $$->type = X_SIG_SET ;
                                         $$->p[0] = $2 ;
                                         $$->name = (streng *)$4 ;
                                         $$->p[1] = $3 ; }
            | signal off s_action error seps 
                                       { exiterror( ERR_EXTRA_DATA );}
            | signal off s_action seps { $$ = $1 ;
                                         $$->type = X_SIG_SET ;
                                         $$->p[0] = $2 ;
                                         $$->p[1] = $3 ; }
            ;

namespec    : NAME SIMSYMBOL           { $$ = (nodeptr)stralloc(retvalue);}
	    | NAME error               { exiterror( ERR_SYMBOL_EXPECTED ) ;}
            ;

asymbol     : CONSYMBOL                { $$ = (nodeptr)stralloc(retvalue) ; }
            | SIMSYMBOL                { $$ = (nodeptr)stralloc(retvalue) ; }
            | error                    { exiterror( ERR_STRING_EXPECTED ) ;}
            ;
 
on          : ON                       { $$ = makenode(X_ON,0) ; }
            ;

off         : OFF                      { $$ = makenode(X_OFF,0) ; }
            ;

c_action    : ERROR                    { $$ = makenode(X_S_ERROR,0) ; }
            | HALT                     {
#ifdef WIN32 /* Such signals occur in a separate thread Win32
				and are thus extremely difficult to integrate into
				the Win32 port.
		     */
										 exiterror(ERR_YACC_SYNTAX);
#endif
										 $$ = makenode(X_S_HALT,0) ;
									   }
            | NOTREADY                 { $$ = makenode(X_S_NOTREADY,0) ; }
            | FAILURE                  { $$ = makenode(X_S_FAILURE,0) ; }
            ;

s_action    : c_action                 { $$ = $1 ; }
            | NOVALUE                  { $$ = makenode(X_S_NOVALUE,0) ; }
            | SYNTAX                   { $$ = makenode(X_S_SYNTAX,0) ; }

trace_stat  : trace VALUE expr seps    { $$ = $1 ;
                                         $$->p[0] = $3 ; }
            | trace expr seps          { $$ = $1 ;
                                         $$->p[0] = $2 ; }
            | trace whatever error seps 
				       { exiterror( ERR_EXTRA_DATA );}
            | trace whatever seps      { $$ = $1 ; 
                                         $$->name = (streng *) $2 ; }
            ;

whatever    : WHATEVER                 { $$ = (nodeptr)stralloc(retvalue) ; }
            ;


assignment  : ass_part nexpr seps      { $$ = $1 ;
                                         $$->p[1] = $2 ; 
                                         if (($2) && gettypeof($$->p[1])==1)
                                            $$->type = X_NASSIGN ; } 
            ;          

ass_part    : ASSIGNMENTVARIABLE       { $$ = makenode(X_ASSIGN,0) ; 
                                         $$->charnr = tstart ;
                                         $$->lineno = tline ;
                                         $$->p[0] = create_head( retvalue ); }
            ;


expr        : '(' expr ')'             { $$ = $2 ; } 
            | NOT expr                 { $$ = makenode(X_LOG_NOT,1,$2) ; }
            |      NOT                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr '+' expr            { $$ = makenode(X_PLUSS,2,$1,$3) ; }
            | expr '=' expr            { $$ = makenode(X_EQUAL,2,$1,$3) ; 
                                         transform( $$ ) ; }
            |      '='                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr '-' expr            { $$ = makenode(X_MINUS,2,$1,$3) ; }
            | expr '*' expr            { $$ = makenode(X_MULT,2,$1,$3) ; }
            |      '*'                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr '/' expr            { $$ = makenode(X_DEVIDE,2,$1,$3) ; }
            |      '/'                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr '%' expr            { $$ = makenode(X_INTDIV,2,$1,$3) ; }
            |      '%'                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr '|' expr            { $$ = makenode(X_LOG_OR,2,$1,$3) ; }
            |      '|'                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr '&' expr            { $$ = makenode(X_LOG_AND,2,$1,$3) ; }
            |      '&'                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr XOR expr            { $$ = makenode(X_LOG_XOR,2,$1,$3) ; }
            |      XOR                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr EXP expr            { $$ = makenode(X_EXP,2,$1,$3) ; }
            |      EXP                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr SPACE expr          { $$ = makenode(X_SPACE,2,$1,$3) ; }
            |      SPACE               { exiterror( ERR_INVALID_EXPRESSION );}
            | expr GTE expr            { $$ = makenode(X_GTE,2,$1,$3) ; 
                                         transform( $$ ) ; }
            |      GTE                 { exiterror( ERR_INVALID_EXPRESSION );}
            | expr LTE expr            { $$ = makenode(X_LTE,2,$1,$3) ; 
                                         transform( $$ ) ; }
            |      GT                  { exiterror( ERR_INVALID_EXPRESSION );}
            | expr GT expr             { $$ = makenode(X_GT,2,$1,$3) ; 
                                         transform( $$ ) ; }
            | expr MODULUS expr        { $$ = makenode(X_MODULUS,2,$1,$3) ; }
            |      MODULUS             { exiterror( ERR_INVALID_EXPRESSION );}
            | expr LT expr             { $$ = makenode(X_LT,2,$1,$3) ; 
                                         transform( $$ ) ; }
            |      LT                  { exiterror( ERR_INVALID_EXPRESSION );}
            | expr DIFFERENT expr      { $$ = makenode(X_DIFF,2,$1,$3) ; 
                                         transform( $$ ) ; }
            |      DIFFERENT           { exiterror( ERR_INVALID_EXPRESSION );}
            | expr EQUALEQUAL expr     { $$ = makenode(X_S_EQUAL,2,$1,$3) ; }
            |      EQUALEQUAL          { exiterror( ERR_INVALID_EXPRESSION );}
            | expr NOTEQUALEQUAL expr  { $$ = makenode(X_S_DIFF,2,$1,$3) ; }
            |      NOTEQUALEQUAL       { exiterror( ERR_INVALID_EXPRESSION );}
            | symbtree                 { $$ = $1 ; }
            | CONSYMBOL                { $$ = makenode(X_STRING,0) ;
                                         $$->name = stralloc(retvalue) ; }
            | HEXSTRING                { $$ = makenode(X_STRING,0) ;
                                         $$->name = Str_make(retlength) ; 
                                         memcpy($$->name->value,retvalue,
                                                    $$->name->len=retlength); }
            | BINSTRING                { $$ = makenode(X_STRING,0) ;
                                         $$->name = Str_make(retlength) ; 
                                         memcpy($$->name->value,retvalue,
                                                    $$->name->len=retlength); }
            | STRING                   { $$ = makenode(X_STRING,0) ; 
                                         $$->name = stralloc(retvalue) ; }
            | function                 { $$ = $1 ; } 
            | '+' expr %prec UPLUSS    { $$ = makenode(X_U_PLUSS,1,$2) ; }
            | '-' expr %prec UMINUS    { $$ = makenode(X_U_MINUS,1,$2) ; }
            | '+'      %prec SYNTOP    { exiterror( ERR_INVALID_EXPRESSION );}
            | '-'      %prec SYNTOP    { exiterror( ERR_INVALID_EXPRESSION );}
            | expr CONCATENATE expr    { $$ = makenode(X_CONCAT,2,$1,$3) ; }
            |      CONCATENATE         { exiterror( ERR_INVALID_EXPRESSION );}
            | expr CCAT expr           { $$ = makenode(X_CONCAT,2,$1,$2) ; }
            |      CCAT                { exiterror( ERR_INVALID_EXPRESSION );}
            ;

symbtree    : SIMSYMBOL                { $$ = create_head( retvalue ) ; }
            ;


function    : extfunc  exprs ')'       { $$ = makenode(X_EX_FUNC,1,$2) ; 
                                         $$->name = (streng *)$1 ; }
            | intfunc  exprs ')'       { $$ = makenode(X_IN_FUNC,1,$2) ; 
                                         $$->name = (streng *)$1 ; }
            ;

intfunc     : INFUNCNAME               { $$ = (nodeptr)stralloc(retvalue) ; }
            ; 

extfunc     : EXFUNCNAME               { $$ = (nodeptr)stralloc(retvalue) ; }
            ;

template    : pv solid template        { $$ =makenode(X_TPL_SOLID,3,$1,$2,$3);}
            | pv                       { $$ =makenode(X_TPL_SOLID,1,$1) ; } 
            | error seps               { exiterror( ERR_INVALID_TEMPLATE );}
            ;

solid	    : '-' offset               { $$ = makenode(X_NEG_OFFS,0) ; 
                                         $$->name = (streng *) $2 ; }
            | '+' offset               { $$ = makenode(X_POS_OFFS,0) ;
                                         $$->name = (streng *) $2 ; }
            | offset                   { $$ = makenode(X_ABS_OFFS,0) ;
                                         $$->name = (streng *) $1 ; }
            | '=' offset               { $$ = makenode(X_ABS_OFFS,0) ;
                                         $$->name = (streng *) $1 ; }
            | '(' symbtree ')'	       { $$ = makenode(X_TPL_VAR,0) ;
                                         $$->p[0] = $2 ; }
            | '-' '(' symbtree ')'     { $$ = makenode(X_NEG_OFFS,0) ;
                                         $$->p[0] = $3 ; }
            | '+' '(' symbtree ')'     { $$ = makenode(X_POS_OFFS,0) ;
                                         $$->p[0] = $3 ; }
            | '=' '(' symbtree ')'     { $$ = makenode(X_ABS_OFFS,0) ;
                                         $$->p[0] = $3 ; }
            | string                   { $$ = makenode(X_TPL_MVE,0) ;
                                         $$->name = (streng *) $1 ; }
            ; 

offset      : OFFSET                   { $$ = (nodeptr)stralloc(retvalue) ; }
	    | CONSYMBOL		       { streng *sptr = stralloc(retvalue) ;
 					  if (myisnumber(sptr))
 				            exiterror(ERR_INVALID_INTEGER);
					  else
					    exiterror(ERR_INVALID_TEMPLATE);}
            ;

string      : STRING                   { $$ = (nodeptr) stralloc(retvalue) ; }
            | HEXSTRING                { streng *sptr = Str_make( retlength ) ;
                                         memcpy(sptr->value,retvalue,
                                                 sptr->len=retlength) ;
                                         $$ = (nodeptr) sptr ; }
            | BINSTRING                { streng *sptr = Str_make( retlength ) ;
                                         memcpy(sptr->value,retvalue,
                                                 sptr->len=retlength) ;
                                         $$ = (nodeptr) sptr ; }
            ;                     

pv          : PLACEHOLDER pv           { $$ = makenode(X_TPL_POINT,1,$2) ; }
            | symbtree pv              { $$ = makenode(X_TPL_SYMBOL,1,$2) ; 
                                         $$->p[1] = $1 ; }
            |                          { $$ = NULL ; }
            ;

exprs       : nexpr ',' exprs          { $$ = makenode(X_EXPRLIST,2,$1,$3) ; 
                                         checkconst( $$ ) ; } 

            | nexpr                    { $$ = makenode(X_EXPRLIST,1,$1) ; 
                                         checkconst( $$ ) ; } 
            ;

nexpr       : expr                     { $$ = $1 ; }
            |                          { $$ = NULL ; }
            ;

anyvars     : xsimsymb anyvars          { $$ = makenode(X_SIM_SYMBOL,1,$2) ; 
                                         $$->name = (streng *) $1 ; }
            | xsimsymb                  { $$ = makenode(X_SIM_SYMBOL,0) ; 
                                         $$->name = (streng *) $1 ; }
            | '(' xsimsymb ')' anyvars  { $$ = makenode(X_IND_SYMBOL,1,$4) ; 
                                         $$->name = (streng *) $2 ; }
            | '(' xsimsymb ')'          { $$ = makenode(X_IND_SYMBOL,0) ; 
                                         $$->name = (streng *) $2 ; }
            ;

xsimsymb    : SIMSYMBOL                { $$ = (treenode *) stralloc(retvalue);}
            ;

simsymb     : SIMSYMBOL                { $$ = (treenode *) stralloc(retvalue);}
	    | error 		       { exiterror( ERR_SYMBOL_EXPECTED );}
            ;               

%%

streng *stralloc( char *ptr )
{
   return Str_cre( ptr ) ;
}


static nodeptr *narray=NULL ;
static int narptr=0, narmax=0 ;

void checkin( nodeptr ptr )
{
   nodeptr *new ;

   if (!narray)
   {
      narray = Malloc( sizeof(nodeptr)* 100 ) ;
      narmax = 100 ;
      narptr = 0 ;
   }

   if (narptr==narmax)
   {
      new = Malloc( sizeof(nodeptr)*narmax*3 ) ;
      memcpy( new, narray, sizeof(nodeptr)*narmax ) ;
      narmax *= 3 ;
      Free( narray ) ;
      narray = new ;
   }

   narray[narptr++] = ptr ;
}

void purge( void )
{
   int i, type ;
   nodeptr this ;
 
   for (i=0; i<narptr; i++)
   {
      this = narray[i] ;
      type = this->type ;
      if (type == X_CON_SYMBOL || type == X_STRING)
         if (this->u.number)
         {
            Free( this->u.number->num ) ;
            Free( this->u.number ) ;
            this->u.number = NULL ;
         }
  
      if (type==X_SIM_SYMBOL || type==X_STEM_SYMBOL || type==X_HEAD_SYMBOL ||
          type==X_CTAIL_SYMBOL || type==X_VTAIL_SYMBOL )
         if (this->u.varbx)
         {
            detach( this->u.varbx ) ;
            this->u.varbx = NULL ;
         }
 
      if (this->type == X_CEXPRLIST)
         if (this->u.strng)
         { 
            Free_string( this->u.strng ) ;
            this->u.strng = NULL ;
         }

      Free( this ) ;
   }  
   narptr = 0 ;
}


void checkout( void )
{
   narptr = 0 ;
}


nodeptr makenode( int type, int numb, ... ) 
{
   nodeptr thisleave ;
   va_list argptr ;
   int i ;

#ifdef REXXDEBUG
   printf("makenode: making new node, type: %d\n",type) ;
#endif /* REXXDEBUG */ 

   thisleave=(nodeptr)Malloc(sizeof(*thisleave)) ;

   for (i=0;i!=5;i++) 
      thisleave->p[i] = NULL ;

   va_start( argptr, numb ) ;
   thisleave->type = type ;
   thisleave->lineno = -1 ;
   thisleave->now = 0 ;
   thisleave->unow = 0 ;
   thisleave->sec = 0 ;
   thisleave->usec = 0 ;
   thisleave->u.node = NULL ;  /* handles ->u.whatever */
/*   thisleave->value = NULL ; */
   thisleave->next = NULL ;
   thisleave->charnr = -1 ;
   thisleave->name = NULL ;
   for (i=0;i!=numb;i++) 
      thisleave->p[i]=va_arg(argptr, nodeptr) ; 

   va_end( argptr ) ;

   checkin( thisleave ) ;
   return( thisleave ) ;
}


void checkdosyntax( nodeptr this ) 
{
   if ((this->p[1]!=NULL)&&(this->p[2]!=NULL))
      if ((this->p[1]->type)==(this->p[2]->type))
         exiterror(ERR_INVALID_DO_SYNTAX) ;
   if ((this->p[2]!=NULL)&&(this->p[3]!=NULL))
      if ((this->p[2]->type)==(this->p[3]->type))
         exiterror(ERR_INVALID_DO_SYNTAX) ;
   if ((this->p[1]!=NULL)&&(this->p[3]!=NULL))
      if ((this->p[1]->type)==(this->p[3]->type))
         exiterror(ERR_INVALID_DO_SYNTAX) ;
   return ;
}


void newlabel( nodeptr this ) 
{
   extern sysinfo systeminfo ;
   labelboxptr new ;

   assert( this ) ;

   new = (labelboxptr)Malloc(sizeof(labelbox)) ;

   new->next = NULL ;
   new->entry = this ;
   if (systeminfo->last!=NULL)
      systeminfo->last->next = new ;
   systeminfo->last = new ;
   if (systeminfo->first==NULL)
      systeminfo->first = new ;
}
 



void destroytree( nodeptr this ) 
{
   int i ;
   int type ;

   for (i=0; i<5; i++)
      if (this->p[i])
         destroytree( this->p[i] ) ;

   if (this->next)
      destroytree( this->next ) ;

   if (this->name)
      Free_string( this->name ) ; 

   type = this->type ;
   if (type == X_CON_SYMBOL || type == X_STRING)
      if (this->u.number)
      {
         Free( this->u.number->num ) ;
         Free( this->u.number ) ;
         this->u.number = NULL ;
      }

   if (type==X_SIM_SYMBOL || type==X_STEM_SYMBOL || type==X_HEAD_SYMBOL ||
       type==X_CTAIL_SYMBOL || type==X_VTAIL_SYMBOL )
      if (this->u.varbx)
      {
         detach( this->u.varbx ) ;
         this->u.varbx = NULL ;
      }

   if (this->type == X_CEXPRLIST)
      if (this->u.strng)
      {
         Free_string( this->u.strng ) ;
         this->u.strng = NULL ;
      }

   Free( this ) ;
}


void kill_lines( lineboxptr first ) 
{
   lineboxptr ptr ;

   for (;first;first=ptr)
   {
      ptr = first->next ;
      assert( first->line ) ;
      Free_string( first->line ) ;
      Free( first ) ;
   }

}


nodeptr create_tail( char *name )
{
   char *cptr ;
   nodeptr node ;
   int constant ;
   streng *tname ;

   if (!*name)
   {
      node = makenode( X_CTAIL_SYMBOL, 0 ) ;
      node->name = nullstringptr() ;
      return node ;
   }

   cptr = name ;
   constant = isdigit(*cptr) || *cptr=='.' || (!*cptr) ;
   node = makenode( (constant) ? X_CTAIL_SYMBOL : X_VTAIL_SYMBOL, 0 ) ;

   for (;*cptr && *cptr!='.'; cptr++) ;
   node->name = Str_ncre( name, cptr-name ) ;
   
   if (*cptr)
   {
      node->p[0] = create_tail( ++cptr ) ;
      if (constant && node->p[0]->type==X_CTAIL_SYMBOL)
      {
         streng *first, *second ;
         nodeptr tptr ;

         first = node->name ;
         second = node->p[0]->name ;
         tname = Str_make( first->len + second->len + 1) ;
         memcpy( tname->value, first->value, first->len ) ;
         tname->value[first->len] = '.' ;
         memcpy( tname->value+first->len+1, second->value, second->len) ;
         tname->len = first->len + second->len + 1 ;

         Free_string( first ) ;
         Free_string( second ) ;
         node->name = tname ;
         tptr = node->p[0] ;
         node->p[0] = tptr->p[0] ;
         Free( tptr ) ;
      }
   } 
  
   return node ;
}

nodeptr create_head( char *name )
{
   char *cptr ;
   nodeptr node ;
   
   for (cptr=name; *cptr && *cptr!='.'; cptr++) ;
   node = makenode( X_SIM_SYMBOL, 0 ) ;
   node->name = Str_ncre( name, cptr-name+(*cptr=='.')) ;

   if (*cptr)
   {
      if (*(++cptr))
         node->p[0] = create_tail( cptr ) ;
      else
         node->p[0] = NULL ;

      node->type = (node->p[0]) ? X_HEAD_SYMBOL : X_STEM_SYMBOL ;
   }

   return node ;
}

#define IS_UNKNOWN   0
#define IS_A_NUMBER  1
#define IS_NO_NUMBER 2
#define IS_IRREG_NUMBER 10

int gettypeof( nodeptr this )
{
   switch(this->type)
   {
      case X_PLUSS:
      case X_MINUS:
      case X_MULT: 
      case X_U_PLUSS:
      case X_U_MINUS:
      case X_DEVIDE:
      case X_INTDIV: 
      case X_MODULUS:
      case X_EQUAL:
      case X_DIFF:
      case X_GTE:
      case X_GT:
      case X_LTE:
      case X_LT:
      case X_SEQUAL:
      case X_SDIFF:
      case X_SGTE:
      case X_SGT:
      case X_SLTE:
      case X_SLT:
      case X_NEQUAL:
      case X_NDIFF:
      case X_NGTE:
      case X_NGT:
      case X_NLTE:
      case X_NLT:
         return IS_A_NUMBER ;
 
 
      case X_SIM_SYMBOL:
         return 3 ;
     
      case X_HEAD_SYMBOL:
         return 4 ;

      case X_STRING:
      case X_CON_SYMBOL:
      {
         if (this->u.number)
            return IS_A_NUMBER ;

         this->u.number = is_a_descr( this->name ) ;
         if (this->u.number)
         {
            streng *stmp = str_norm( this->u.number, NULL ) ;
            if (Str_cmp(stmp,this->name))
            {
               Free_string( stmp ) ;
               return IS_UNKNOWN ;
            }
            Free_string( stmp ) ;
         }
         return (this->u.number) ? IS_A_NUMBER : IS_NO_NUMBER ;
      }
   }
   return IS_UNKNOWN ;
}



void transform( nodeptr this )
{
   int left, rght, type ;

   left = gettypeof( this->p[0] ) ;
   rght = gettypeof( this->p[1] ) ;
   type = this->type ;

   if (left==1 && rght==1)
   {
      if (type==X_EQUAL)
         this->type = X_NEQUAL ;
      else if (type==X_DIFF)
         this->type = X_NDIFF ;
      else if (type==X_GTE)
         this->type = X_NGTE ;
      else if (type==X_GT)
         this->type = X_NGT ;
      else if (type==X_LTE)
         this->type = X_NLTE ;
      else if (type==X_LT)
         this->type = X_NLT ;
   }
   else if (left==2 || rght==2)
   {
      if (type==X_EQUAL)
         this->type = X_SEQUAL ;
      else if (type==X_DIFF)
         this->type = X_SDIFF ;
      else if (type==X_GTE)
         this->type = X_SGTE ;
      else if (type==X_GT)
         this->type = X_SGT ;
      else if (type==X_LTE)
         this->type = X_SLTE ;
      else if (type==X_LT)
         this->type = X_SLT ;
   }     
   else
   {
      type = this->p[0]->type ;
      if (left==1 && (type==X_STRING || type==X_CON_SYMBOL))
         this->u.flags.lnum = 1 ;
      else if (left==3)
         this->u.flags.lsvar = 1 ;
      else if (left==4)
         this->u.flags.lcvar = 1 ;

      type = this->p[1]->type ;
      if (rght==1 && (type==X_STRING || type==X_CON_SYMBOL))
         this->u.flags.rnum = 1 ;
      else if (rght==3)
         this->u.flags.rsvar = 1 ;
      else if (rght==4)
         this->u.flags.rcvar = 1 ;
   }
}


int is_const( nodeptr this ) 
{
   if (!this)
      return 1 ;

   switch (this->type)
   {
      case X_STRING:
      case X_CON_SYMBOL:
         return 1 ;

      case X_U_PLUSS:
      case X_U_MINUS:
         return is_const( this->p[0] ) ;

      case X_PLUSS:
      case X_MINUS:
      case X_MULT: 
      case X_DEVIDE:
      case X_INTDIV: 
      case X_MODULUS:
      case X_EQUAL:
      case X_DIFF:
      case X_GTE:
      case X_GT:
      case X_LTE:
      case X_LT:
      case X_SEQUAL:
      case X_SDIFF:
      case X_SGTE:
      case X_SGT:
      case X_SLTE:
      case X_SLT:
      case X_NEQUAL:
      case X_NDIFF:
      case X_NGTE:
      case X_NGT:
      case X_NLTE:
      case X_NLT:
      case X_SPACE:
      case X_CONCAT:
         return is_const( this->p[0] ) && is_const( this->p[1] ) ;
   }
   return 0 ;
}


void checkconst( nodeptr this ) 
{
   assert( this->type == X_EXPRLIST ) ;
   if (is_const(this->p[0]))
   {
      if (this->p[0])
         this->u.strng = evaluate( this->p[0], NULL ) ;
      else
         this->u.strng = NULL ;

      this->type = X_CEXPRLIST ;
   }
}

