[net.sources] Cparen - C expression parenthesizer, Version 2

bradn@hammer.UUCP (Brad Needham) (07/17/84)

Here is the latest version of the C expression parenthesizer, cparen.
It's been improved in several ways since I last sent it out to the net:
	- The copyright notice has been rewritten so that you can
	  *legally* copy & use cparen.
	- The shar file has been modified to work around a mail bug
	  that likes to eat pairs of backslashes.
	- This version will run under V7, Sys III, Sys V, and all BSD's.
	- This version now parses string and character constants that
	  contain quoted newlines.

Since so many people complained about not being able to pump a whole program
through cparen, I've included my epistle on parentheses & cparen.

Portability is necessarily a community effort.
Many thanks go to all the people who shared cparen's successes & failures
with me.

Brad Needham
Tektronix, Inc.
...decvax!tektronix!tekecs!bradn

======= The Epistle on Parentheses & Cparen ==============================
Ways in which I use cparen:
1) from vi.
	I first copy the source line(s) that contains a confusing
	expression, via ":.t."; I then filter the copy through cparen,
	via "!!cparen".  Once I have examined the parenthesized copy,
	it remove it, via "dd".
2) interactively.
	I invoke cparen from the shell, via "cparen".
	I type a confusing expression, e.g.
		--a->x[5]
	I then type ^D and look at the output.

The main reason that Cparen doesn't process whole files is that I
didn't want to go to the trouble of creating a grammar (that's the
parse.y file) that would parse the whole C language -- C declarations,
for example, can be very complex.  I wrote Cparen to be able to read C
source while I was editing it -- I see no reason to make Cparen an
order of magnitude more complex just so I can get a listing of a
program with all the expressions fully parenthesized.

Another reason for not processing whole files is my philosophy of use
of parentheses.  A fully-parenthesized expression is often less
readable than a minimally-parenthesized one.  My rule of parentheses
when writing code is this: use parentheses only to change the
evaluation of the expression or to make the code more readable --
extraneous parentheses confuse the reader.  In keeping with that
philosophy, I use Cparen to answer questions I have when reading code.
======== End of the Epistle ==============================================
# The rest of this file is a shell script which will extract:
# Makefile cparen.1 cparen.c cparen.h hid.c lex.l parse.y
echo x - Makefile
cat >Makefile <<'!Funky!Stuff!'
CFLAGS = -g
announce: cparen
	: cparen is built
cparen.cat: cparen.1
	nroff -man cparen.1 >cparen.cat
cparen: cparen.o parse.o lex.o hid.o
	cc -o cparen cparen.o lex.o parse.o hid.o -ll
cparen.o: cparen.c cparen.h parse.c
parse.o: parse.c cparen.h
lex.o: lex.c cparen.h parse.c
hid.o: hid.c cparen.h
parse.c: parse.y
	-rm -f parse.c
	: There should be 7 shift/reduce conflicts
	yacc -d parse.y
	mv y.tab.c parse.c
	chmod a-w parse.c
lex.c: lex.l
	-rm -f lex.c
	lex lex.l
	mv lex.yy.c lex.c
	chmod a-w lex.c
clean:
	-rm -f parse.c lex.c y.tab.h hid.o lex.o parse.o cparen.o
!Funky!Stuff!
echo x - cparen.1
cat >cparen.1 <<'!Funky!Stuff!'
.TH CPAREN "4/12/83" "Tek local"
.SH NAME
cparen - add parentheses to C expressions
.SH SYNOPSIS
.B cparen
[
.B \-t
.I types
]
.SH DESCRIPTION
Written for those of us who can never remember the precedence and
associativity of operators in C,
.B cparen
reads lines of C code from standard-in,
adds parentheses to indicate operator binding in expressions,
then writes the resultant code to standard-out.
.PP
The input code fragment need not contain complete statements.
For example, the following line is a valid input to
.B cparen:
.br
	} else if (x->d_prep > 56 && x->d_assoc == LEFT)
.br
.PP
Normally,
.B cparen
considers identifiers in expressions to be variable names.
The
.B \-t
option allows you to specify a whitespace-separated list of types.
For example,
.br
	cparen -t 'amap anop'
.br
Declares "amap" and "anop" as type names rather than variable names.
.SH DIAGNOSTICS
Exit status is 2 if
.B cparen
was invoked improperly,
1 if some other error occurred,
0 if all went well.
.SH AUTHOR
.DS
Brad Needham, Tektronix, Inc.

Copyright (c) 1984 by
Tektronix, Incorporated Beaverton, Oregon 97077
All rights reserved.
.DE
.PP
Permission is hereby granted for personal, non-commercial
reproduction and use of this program, provided that this
notice is included in any copy.
.SH BUGS
.B Cparen
assumes that the input code fragment came from a syntactically correct
C program -- it does not attempt to give reasonable syntax-error messages.
.PP
Because
.B cparen
focuses on C statments it does not recognize other constructs e.g.
variable or function declarations.
It cannot process a whole C program.
.PP
The input is not filtered through the C preprocessor.
!Funky!Stuff!
echo x - cparen.c
cat >cparen.c <<'!Funky!Stuff!'
/*
 * Copyright (c) 1984 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 *
 * Permission is hereby granted for personal, non-commercial
 * reproduction and use of this program, provided that this
 * notice is included in any copy.
 */

#define VARS		/* to declare all the variables in cparen.h	*/
#include <stdio.h>
#include "cparen.h"
#include "y.tab.h"

static char *rcsid = "$Header: cparen.c,v 1.10 84/04/05 09:21:42 bradn Stable $";

main(argc,argv)
int argc;
char **argv;
{
	char *tp, *ep, *cp;
	struct lexed *lp;	/* lex token pointer	*/	
	int atend;		/* a temp	*/
	char *rindex();


	if ((cmd = rindex(argv[0], '/'))) {
		cmd++;
	} else {
		cmd = argv[0];
	}

	/* scan the command line	*/

	tp = (char *) 0;
	while (++argv, --argc > 0) {
		cp = *argv;
		if (*cp == '-') {
			while (*++cp) {
				switch(*cp) {
				case 't':
					if (tp) {
fprintf(stderr, "%s: option -%c used more than once\n", cmd, *cp);
					    goto baduse;
					}
					if (++argv, --argc <= 0) {
fprintf(stderr, "%s: missing type list\n", cmd);
					    goto baduse;
					}
					tp = *argv;
					break;
				default:
fprintf(stderr, "%s: unknown option -%c\n", cmd, *cp);
					goto baduse;
				}
			}
		} else {
			fprintf(stderr, "%s: extra string `%s'\n", cmd, cp);
baduse:
			fprintf(stderr, "Usage:\n  %s [-t types]\n", cmd);
			exit(2);
		}
	}

	if (tp) {

		/* install whitespace-separated type names	*/

		atend = 0;
		while (!atend && *tp) {
			for (ep = tp;
			  *ep && *ep != ' ' && *ep != '\t' && *ep != '\n';
			  ep++)
				;
			atend = *ep == '\0';
			*ep = '\0';
			if (ep - tp > 0) {
				newtype(tp);
			}
			tp = ++ep;
		}
	}

	/* init the lexed token list	*/

	firsttok.l_text = "";
	firsttok.l_prev = (struct lexed *) 0;
	firsttok.l_next = &lasttok;
	firsttok.l_val = 0;
	lasttok.l_text = "";
	lasttok.l_prev = &firsttok;
	lasttok.l_next = (struct lexed *) 0;
	lasttok.l_val = 0;

	nonode.n_op = LIT;
	nonode.n_flags = 0;
	nonode.n_ledge = (struct lexed *) 0;
	nonode.n_redge = (struct lexed *) 0;


	if (yyparse() == 1) {
		exit(1);
	}

	for (lp = firsttok.l_next; lp != &lasttok; lp = lp->l_next) {
		fputs(lp->l_text, stdout);
	}

	exit(0);
}

consider(op, lc, rc)	/* consider putting parens around the children	*/
int op;			/* the parent operator				*/
struct node *lc;	/* the left child (or NIL)			*/
struct node *rc;	/* the right child (or NIL)			*/
{
	struct node **cpp;	/* child pointer-pointer (a temp)	*/
	struct node *cp;	/* child pointer			*/


	/* ignore children of the 'literal' operator	*/

	if (op == LIT) {
		return;
	}

	/* for each child ...	*/
	cpp = &lc;
	do {
		/* consider only non-null, non-dummy children	*/
		cp = *cpp;
		if (cp && cp != NOP) {

			if (haspar(cp)
			  || op == CAST && cpp == &lc
			  || cp->n_op == LIT
			  || cp->n_op == CAST && !hasr(cp)
			  || op == CM
			  || op == BRACK && cpp == &rc
			  || op == CALL  && cpp == &rc
			  || op == QUEST && cpp == &rc
			  || op == cp->n_op && (
				/*
				 * don't parenthesize things which
				 * might be rearranged in spite of it.
				 */
			         op == MUL
			      || op == PLUS
			      || op == AND
			      || op == ER
			      || op == OR
			    )
			  ) {
				/* do nothing	*/
			} else {
				/* add parentheses	*/
				cp->n_ledge =
				  instok(LP, "(", cp->n_ledge->l_prev);
				cp->n_redge =
				  instok(RP, ")", cp->n_redge);
				cp->n_flags |= HASPAR;
			}
		}

		/* move on to the next child	*/

		if (cpp == &lc) {
			cpp = &rc;
		} else {
			cpp = (struct node **) 0;
		}
	} while (cpp);
}
!Funky!Stuff!
echo x - cparen.h
cat >cparen.h <<'!Funky!Stuff!'
/*
 * Copyright (c) 1984 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 *
 * Permission is hereby granted for personal, non-commercial
 * reproduction and use of this program, provided that this
 * notice is included in any copy.
 */

#ifdef RCSNAME
	static char *RCSNAME = "$Header: cparen.h,v 1.12 84/04/05 09:21:51 bradn Stable $";
#else

#define	TRUE	1
#define	FALSE	0

#ifdef VARS
char *cmd;			/* the name of this command (for error msgs) */
#else
extern char *cmd;
#endif

struct lexed {			/* one unit of the lex'ed input list */
	char *l_text;		/* text associated with this token	*/
	struct lexed *l_prev;	/* previous token in the list		*/
	struct lexed *l_next;	/* next token in the list		*/
	int l_val;		/* this token's value (yylval'ish)	*/
};

extern struct lexed *nxttok();	/* used by lex to add a token to the list */
extern struct lexed *instok();	/* used by everybody to insert tokens	*/

struct node {			/* a node of an expression subtree	*/
	int n_op;
	int n_flags;
#define	HASL	1		/* 'has a left child'			*/
#define	HASR	2		/* 'has a right child'			*/
#define	BINARY	(HASL | HASR)
#define	HASPAR	4		/* 'has enclosing parens'		*/
#define	hasl(np) ((np)->n_flags & HASL)
#define	hasr(np) ((np)->n_flags & HASR)
#define	haspar(np) ((np)->n_flags & HASPAR)
				/*
				 * n_ledge and n_redge are the left and right
				 * edges of the sublist of tokens associated
				 * with this node.
				 * If haspar() is true, these pointers refer
				 * to the left and right paren tokens in the
				 * input token list.
				 */
	struct lexed *n_ledge;
	struct lexed *n_redge;
};

extern struct node *bld();	/* used by yacc rules to return node info */

typedef union {
	int intval;
	struct lexed *lexval;
	struct node *nodep;
} YYSTYPE;
#ifndef PARSER			/* yacc generates the definition of yylval */
extern YYSTYPE yylval;
#endif

#define NIL	((struct node *) 0)
#ifdef VARS
struct node nonode;
#else
extern struct node nonode;
#endif
#define	NOP	(&nonode)	/* a no-op node pointer			*/

#ifdef VARS
struct lexed firsttok;		/* the left edge of the token list	*/
#else
extern struct lexed firsttok;
#endif
#ifdef VARS
struct lexed lasttok;		/* the right edge of the token list	*/
#else
extern struct lexed lasttok;
#endif

#endif
!Funky!Stuff!
echo x - hid.c
cat >hid.c <<'!Funky!Stuff!'
/*
 * Copyright (c) 1984 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 *
 * Permission is hereby granted for personal, non-commercial
 * reproduction and use of this program, provided that this
 * notice is included in any copy.
 */

static char *rcsid = "$Header: hid.c,v 1.7 84/04/05 09:21:56 bradn Stable $";

#define RCSNAME cparen
#include "cparen.h"
#undef RCSNAME
!Funky!Stuff!
echo x - lex.l
tr '@' '\134' >lex.l <<'!Funky!Stuff!'
%{
/*
 * Copyright (c) 1984 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 *
 * Permission is hereby granted for personal, non-commercial
 * reproduction and use of this program, provided that this
 * notice is included in any copy.
 */
# include "stdio.h"
# include "cparen.h"
# include "y.tab.h"

static char *rcsid = "$Header: lex.l,v 1.12 84/07/17 09:45:18 bradn Stable $";

#define	MAXTYPES 100
char *typnam[MAXTYPES] = {	/* 'type' identifiers	*/
	"char",
	"double",
	"float",
	"int",
	"long",
	"short",
	"unsigned",
	"void",
	0			/* command-line types go here	*/
};
char **typend;			/* first free spot in typnam[]	*/

struct spec {			/* special keywords		*/
	char *s_name;		/* keyword text			*/
	int s_tok;		/* token to return		*/
	int s_val;		/* value to return		*/
};
struct spec specnam[] = {
	{"sizeof", SIZEOF, SIZEOF},
	{"struct", STRUCT, ISSTRUCT},
	{"union", STRUCT, ISUNION},
	{"enum", ENUM, ENUM},
	{"goto", KEYWORD, KEYWORD},
	{"return", KEYWORD, KEYWORD},
	{"if", KEYWORD, KEYWORD},
	{"else", KEYWORD, KEYWORD},
	{"while", KEYWORD, KEYWORD},
	{"do", KEYWORD, KEYWORD},
	{"for", KEYWORD, KEYWORD},
	{"switch", KEYWORD, KEYWORD},
	{"case", KEYWORD, KEYWORD},
	{0,0,0}
};

int tok;	/* a temp	*/
%}
%%

"<"	{yylval.lexval = nxttok(LT); return(RELOP);}
"<="	{yylval.lexval = nxttok(LE); return(RELOP);}
">"	{yylval.lexval = nxttok(GT); return(RELOP);}
">="	{yylval.lexval = nxttok(GE); return(RELOP);}
","	{yylval.lexval = nxttok(CM); return(CM);}
"/"	{yylval.lexval = nxttok(DIV); return(DIVOP);}
"%"	{yylval.lexval = nxttok(MOD); return(DIVOP);}
"+"	{yylval.lexval = nxttok(PLUS); return(PLUS);}
"-"	{yylval.lexval = nxttok(MINUS); return(MINUS);}
"<<"	{yylval.lexval = nxttok(LS); return(SHIFTOP);}
">>"	{yylval.lexval = nxttok(RS); return(SHIFTOP);}
"*"	{yylval.lexval = nxttok(MUL); return(MUL);}
"=="	{yylval.lexval = nxttok(EQ); return(EQUOP);}
"!="	{yylval.lexval = nxttok(NE); return(EQUOP);}
"&"	{yylval.lexval = nxttok(AND); return(AND);}
"|"	{yylval.lexval = nxttok(OR); return(OR);}
"^"	{yylval.lexval = nxttok(ER); return(ER);}
"&&"	{yylval.lexval = nxttok(ANDAND); return(ANDAND);}
"||"	{yylval.lexval = nxttok(OROR); return(OROR);}
"="	{yylval.lexval = nxttok(ASSIGN); return(ASSIGN);}
"?"	{yylval.lexval = nxttok(QUEST); return(QUEST);}
":"	{yylval.lexval = nxttok(COLON); return(COLON);}
"=/"	{yylval.lexval = nxttok(DIV); return(ASOP);}
"=%"	{yylval.lexval = nxttok(MOD); return(ASOP);}
"=+"	{yylval.lexval = nxttok(PLUS); return(ASOP);}
"=-"	{yylval.lexval = nxttok(MINUS); return(ASOP);}
"=<<"	{yylval.lexval = nxttok(LS); return(ASOP);}
"=>>"	{yylval.lexval = nxttok(RS); return(ASOP);}
"=*"	{yylval.lexval = nxttok(MUL); return(ASOP);}
"=&"	{yylval.lexval = nxttok(AND); return(ASOP);}
"=|"	{yylval.lexval = nxttok(OR); return(ASOP);}
"=^"	{yylval.lexval = nxttok(ER); return(ASOP);}
"++"	{yylval.lexval = nxttok(INCR); return(INCOP);}
"--"	{yylval.lexval = nxttok(DECR); return(INCOP);}
"!"	{yylval.lexval = nxttok(NOT); return(UNOP);}
"~"	{yylval.lexval = nxttok(COMPL); return(UNOP);}
"("	{yylval.lexval = nxttok(LP); return(LP);}
")"	{yylval.lexval = nxttok(RP); return(RP);}
"["	{yylval.lexval = nxttok(LB); return(LB);}
"]"	{yylval.lexval = nxttok(RB); return(RB);}
"->"	{yylval.lexval = nxttok(STREF); return(STROP);}
"."	{yylval.lexval = nxttok(DOT); return(STROP);}
";"	{yylval.lexval = nxttok(SM); return(SM);}
"{"	{/* ignore curly-braces */ (void) nxttok(LC);}
"}"	{/* ignore curly-braces */ (void) nxttok(RC);}
0x[0-9a-zA-Z]+[Ll]?	{/* hex constant */
			yylval.lexval = nxttok(NAME); return(NAME);
		}
[0-9]+[Ll]?	{/* decimal or octal constant */
			yylval.lexval = nxttok(NAME); return(NAME);
		}
[0-9]+[Ee]([+-][0-9])?[0-9]*		|
@.[0-9]+([Ee]([+-][0-9])?[0-9]*)?	|
[0-9]+@.[0-9]*([Ee]([+-][0-9])?[0-9]*)?	{/* floating constant */
			yylval.lexval = nxttok(NAME); return(NAME);
		}
@'([^'@n]|(@@[@n@']))+@'	{/* char constant */
				yylval.lexval = nxttok(NAME); return(NAME);
			}
@"([^"@n]|(@@[@n@"]))*@"	{/* string constant */
				yylval.lexval = nxttok(NAME); return(NAME);
			}
[a-zA-Z_$][a-zA-Z0-9_$]*	{/* identifier, type, or keyword */
				yylval.lexval = nxttok(NAME);

				/* take care of magic keywords	*/

				if ((tok =
				   specof(yytext, &yylval.lexval->l_val))) {
					return(tok);
				}

				/* take care of types and identifiers	*/

				if (istype(yytext)) {
					return(TYPE);
				} else {
					return(NAME);
				}
			}
[ @t@n]+	{/* whitespace */ (void) nxttok(FLUFF);}
@/@*([^@*]|(@*[^@/]))*@*?@*@/	{/* comment */ (void) nxttok(FLUFF);}
.	{fprintf(stderr, "%s: unknown character `%c'@n", cmd, yytext[0]);}

%%

static
initype()	/* init the type list					*/
{
	if (typend) {
		return;
	}
	for (typend = &typnam[0]; *typend; typend++)
		;
}

newtype(s)	/* insert this string into the list of type strings	*/
char *s;
{
	char **t;

	initype();
	for (t = &typnam[0]; t < typend && strcmp(s, *t) != 0; t++)
		;
	if (t <typend) {
		/* this type already exists - no need to insert it again */
		return;
	}
	if (typend >= &typnam[MAXTYPES]) {
		fprintf(stderr, "%s: out of memory@n", cmd);
		exit(1);
	}
	*typend++ = s;
}

static int
istype(s)	/* returns 'this string is a TYPE name'			*/
char *s;
{
	char **t;


	initype();
	for (t = &typnam[0]; t < typend && strcmp(s, *t) != 0; t++)
		;
	return(t < typend);
}

static int	/* token to return (or zero, if not a special keyword)	*/
specof(t, vp)
char *t;	/* possible keyword text	*/
int *vp;	/* where to put value		*/
{
	struct spec *sp;

	for (sp = &specnam[0]; sp->s_name && strcmp(t, sp->s_name) != 0;
	  sp++)
		;
	if (sp->s_name) {
		*vp = sp->s_val;
		return(sp->s_tok);
	}
	return(0);
}

struct lexed *
nxttok(val)			/* insert the val at the end of the list */
int val;
{
	/* curtok: current token in the input list	*/
	static struct lexed *curtok = &firsttok;

	curtok = instok(val, yytext, curtok);
	return(curtok);
}

struct lexed *
instok(val, text, after)	/* insert token into input list		*/
int val;			/* token value (yylval'ish)		*/
char *text;			/* associated text (to be copied)	*/
struct lexed *after;		/* node to insert after			*/
{
	char *malloc();
	struct lexed *node;


	if (!(node = (struct lexed *) malloc(sizeof(struct lexed)))) {
		fprintf(stderr, "%s: out of memory.@n", cmd);
		exit(1);
	}
	if (!(node->l_text = malloc(strlen(text) + 1))) {
		fprintf(stderr, "%s: out of memory.@n", cmd);
		exit(1);
	}
	(void) strcpy(node->l_text, text);
	node->l_val = val;

	/* insert into the list after the node 'after'	*/

	after->l_next->l_prev = node;
	node->l_next = after->l_next;
	node->l_prev = after;
	after->l_next = node;

	return(node);
}
!Funky!Stuff!
echo x - parse.y
cat >parse.y <<'!Funky!Stuff!'
/*
 * Copyright (c) 1984 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 *
 * Permission is hereby granted for personal, non-commercial
 * reproduction and use of this program, provided that this
 * notice is included in any copy.
 */
%term	MUL	1	/* start of assignable ops (e.g. + vs +=)	*/
%term	DIV	2
%term	MOD	3
%term	PLUS	4
%term	MINUS	5
%term	LS	6	/* left-shift <<	*/
%term	RS	7	/* right-shift >>	*/
%term	AND	8
%term	OR	9
%term	ER	10	/* exclusive-or ^	*/
%term	ASSIGN	11	/* end of assignable ops (e.g. + vs +=)		*/
%term	MULASG	12	/* assign version of assignable ops		*/
%term	DIVASG	13
%term	MODASG	14
%term	PLUSASG	15
%term	MINUSASG 16
%term	LSASG	17
%term	RSASG	18
%term	ANDASG	19
%term	ORASG	20
%term	ERASG	21	/* end of assign version of ops			*/
%term	LT	22	/* less-than <		*/
%term	LE	23
%term	GT	24
%term	GE	25
%term	EQ	26
%term	NE	27
%term	ANDAND	28
%term	OROR	29
%term	CM	30	/* comma ,		*/
%term	COMOP	31	/* comma operator ,	*/
%term	QUEST	32	/* ?			*/
%term	COLON	33
%term	UMINUS	34	/* unary minus -	*/
%term	INCR	35	/* (post- or pre-) increment ++	*/
%term	DECR	36	/* (post- or pre-) decrement --	*/
%term	INDR	37	/* indirect *		*/
%term	ADDR	38	/* address-of &		*/
%term	NOT	39
%term	COMPL	40	/* complement ~		*/
%term	SIZEOF	41
%term	CAST	42
%term	BRACK	43	/* brackets []		*/
%term	ISSTRUCT 44
%term	ISUNION	45
%term	ENUM	46
%term	STREF	47	/* structure reference (-> or .)	*/
%term	DOT	48	/* subfield .		*/
%term	CALL	49
%term	LIT	51	/* literal. A dummy operator	*/
%term	KEYWORD	52	/* a statement keyword	*/
%term	SM	53	/* semi-colon ;		*/
%term	LC	54	/* left curly-brace {	*/
%term	RC	55	/* right curly-brace }	*/
%term	FLUFF	56	/* syntactic fluff: comments and whitespace	*/



%left	CM
%right	ASOP ASSIGN
%right	QUEST COLON
%left	OROR
%left	ANDAND
%left	OR
%left	ER
%left	AND
%left	EQUOP
%left	RELOP
%left	SHIFTOP
%left	PLUS MINUS
%left	MUL DIVOP
%right	UNOP
%right	INCOP SIZEOF
%left	LB LP STROP

%{
#define PARSER
#include <stdio.h>
#include "cparen.h"

static char *rcsid = "$Header: parse.y,v 1.14 84/04/05 09:22:07 bradn Stable $";

struct node *bld();
%}

%start	unit_list
%type	<nodep>	e term elist cast_type null_decl funct_idn atype struct_dcl
		enum_dcl unit unit_list
%token	<lexval> RELOP CM DIVOP PLUS MINUS SHIFTOP MUL EQUOP AND OR ER
		ANDAND OROR ASSIGN QUEST COLON ASOP INCOP UNOP SIZEOF
		LP RP LB RB STROP STRUCT ENUM NAME TYPE LT LE GT GE DIV
		MOD LS RS EQ NE INCR DECR NOT COMPL STREF DOT ISSTRUCT
		ISUNION COMOP INDR ADDR CAST BRACK CALL UMINUS
		SM LIT KEYWORD


%%

unit_list:
	  unit
		{$$ = NOP;}
	| unit_list unit
		{$$ = NOP;}
	;
unit:	  COLON
		{$$ = NOP;}
	| SM
		{$$ = NOP;}
	| e
		{$$ = $1;}
	| KEYWORD
		{$$ = NOP;}
	;
e:	e RELOP e
		{bop:	consider($2->l_val, $1, $3);
			$$ = bld($2->l_val, BINARY, $1->n_ledge, $3->n_redge);
		}
	| e CM e
		{ $2->l_val = COMOP; goto bop; }
	| e DIVOP e
		{ goto bop; }
	| e PLUS e
		{ goto bop; }
	| e MINUS e
		{ goto bop; }
	| e SHIFTOP e
		{ goto bop; }
	| e MUL e
		{ goto bop; }
	| e EQUOP e
		{ goto bop; }
	| e AND e
		{ goto bop; }
	| e OR e
		{ goto bop; }
	| e ER e
		{ goto bop; }
	| e ANDAND e
		{ goto bop; }
	| e OROR e
		{ goto bop; }
	| e MUL ASSIGN e
		{ abop:
			$2->l_val = asgof($2->l_val);
			consider($2->l_val, $1, $4);
			$$ = bld($2->l_val, BINARY, $1->n_ledge, $4->n_redge);
		}
	| e DIVOP ASSIGN e
		{ goto abop; }
	| e PLUS ASSIGN e
		{ goto abop; }
	| e MINUS ASSIGN e
		{ goto abop; }
	| e SHIFTOP ASSIGN e
		{ goto abop; }
	| e AND ASSIGN e
		{ goto abop; }
	| e OR ASSIGN e
		{ goto abop; }
	| e ER ASSIGN e
		{ goto abop; }
	| e QUEST e COLON e
		{	consider($2->l_val, $1, NIL);
			consider($4->l_val, $3, $5);
			$$ = bld($2->l_val, BINARY, $1->n_ledge, $5->n_redge);
		}
	| e ASOP e
		{
			$2->l_val = asgof($2->l_val);
			consider($2->l_val, $1, $3);
			$$ = bld($2->l_val, BINARY, $1->n_ledge, $3->n_redge);
		}
	| e ASSIGN e
		{ goto bop; }
	| term
	;
term:	term INCOP
		{	consider($2->l_val, $1, NIL);
			$$ = bld($2->l_val, HASL, $1->n_ledge, $2);
		}
	| INCOP term
		{ unop:	consider($1->l_val, NIL, $2);
			$$ = bld($1->l_val, HASR, $1, $2->n_redge);
		}
	| MUL term
		{$1->l_val = INDR; goto unop;}
	| AND term
		{$1->l_val = ADDR; goto unop;}
	| MINUS term
		{$1->l_val = UMINUS; goto unop;}
	| UNOP term
		{ goto unop; }
	| SIZEOF LP cast_type RP	%prec SIZEOF
		{
			$$ = bld($1->l_val, HASR, $1, $4);
		}
	| SIZEOF term
		{ goto unop; }
	| LP cast_type RP term		%prec INCOP
		{
			consider(CAST, NOP, $4);
			$$ = bld(CAST, BINARY, $1, $4->n_redge);
		}
	| term LB e RB
		{	consider(BRACK, $1, $3);
			$$ = bld(BRACK, BINARY, $1->n_ledge, $4);
		}
	| term STROP NAME
		{	consider($2->l_val, $1, NOP);
			$$ = bld($2->l_val, BINARY, $1->n_ledge, $3);
		}
	| funct_idn RP
		{
			$$ = bld(CALL, HASL, $1->n_ledge, $2);
		}
	| funct_idn elist RP
		{
			$$ = bld(CALL, BINARY, $1->n_ledge, $3);
		}
	| NAME			/* includes ICON, FCON, and STRING	*/
		{ $$ = bld(LIT, 0, $1, $1); }
	| LP unit_list RP
		{ $$ = bld(LIT, HASPAR, $1, $3);}
	;
elist:	  e				%prec CM
		{$$ = $1;}
	| elist CM e
		{
			$$ = bld(LIT, 0, $1->n_ledge, $3->n_redge);
		}
	;
cast_type: atype null_decl
		{
			$$ = bld(LIT, 0, $1->n_ledge,
			  $2->n_redge ? $2->n_redge : $1->n_redge);
		}
	;
null_decl:	/* empty */
		{ $$ = NOP; }
	| LP null_decl RP LP RP
		{$$ = bld(LIT, 0, $1, $5);}
	| MUL null_decl
		{$$ = bld(LIT, 0, $1, $2->n_redge ? $2->n_redge : $1);}
	| null_decl LB RB
		{$$ = bld(LIT, 0, $1->n_ledge ? $1->n_ledge : $2, $3);}
	| null_decl LB e RB
		{
			$$ = bld(LIT, 0, $1->n_ledge ? $1->n_ledge : $2, $4);
		}
	| LP null_decl RP
		{$$ = bld(LIT, 0 , $1, $3);}
	;
funct_idn: NAME LP
		{ $$ = bld(LIT, 0, $1, $2); }
	| term LP
		{ $$ = bld(LIT, 0, $1->n_ledge, $2); }
	;
atype:	 TYPE
		{$$ = bld(LIT, 0, $1, $1);}
	| TYPE TYPE
		{$$ = bld(LIT, 0, $1, $2);}
	| TYPE TYPE TYPE
		{$$ = bld(LIT, 0, $1, $3);}
	| struct_dcl
		{$$ = $1;}
	| enum_dcl
		{$$ = $1;}
	;
struct_dcl: STRUCT NAME	
		{$$ = bld(LIT, 0, $1, $2);}
	;
enum_dcl:  ENUM NAME
		{$$ = bld(LIT, 0, $1, $2);}
	;
%%

yyerror(s)
char *s;
{
	fprintf(stderr,"%s: %s\n", cmd, s);
}

struct node *			/* the allocated node			*/
bld(op, flags, left, right)
int op;				/* the operation to be performed	*/
int flags;			/* the flags to set			*/
struct lexed *left;		/* points to the left token		*/
struct lexed *right;		/* points to the right token		*/
{
	struct node *res;


	if (!(res = (struct node *) malloc(sizeof(struct node)))) {
		fprintf(stderr,"%s: out of memory\n", cmd);
		exit(1);
	}
	res->n_op = op;
	res->n_flags = flags;
	res->n_ledge = left;
	res->n_redge = right;
	return(res);
}

static int
asgof(v)	/* convert the given op into the = form e.g. + to +=	*/
int v;
{
	if (v < ASSIGN) {
		v += ASSIGN;
	}
	return(v);
}
!Funky!Stuff!