[net.sources] cparen - C expression parenthesizer

bradn@tekecs.UUCP (06/01/83)

Cparen is a filter which fully parenthesizes C expressions.
It is useful to those of us who can never remember operator precedence
and associativity in C.

The source is in Berkeley 'ar' format.  To install it,
	1) save this message into a file called "cparen.ar"
	2) edit cparen.ar and remove the lines from the beginning
	  of the file up to (but not including) the line which begins
	  with "!<arch>".
	3) say "ar xv cparen.ar".
	4) say "make cparen cparen.cat".

Please report any bugs to
	...!decvax!tektronix!tekecs!bradn

======================= the archive begins with the next line ===============
!<arch>
Makefile        423334288   146   47    100444  463       `
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
	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

cparen.1        423334289   146   47    100444  1363      `
.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
Brad Needham, bradn
.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.
.PP
The input is not filtered through the C preprocessor.

cparen.h        423334291   146   47    100444  1830      `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
#ifdef RCSNAME
	static char *RCSNAME = "$Header: /da/bradn/.bin/src/cparen/RCS/cparen.h,v 1.8 83/04/17 11:53:05 bradn Exp $";
#else

#define	TRUE	1
#define	FALSE	0

char *cmd;			/* the name of this command (for error msgs) */

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)	*/
};

struct lexed *nxttok();		/* used by lex to add a token to the list */
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;
};

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

typedef union {
	int intval;
	struct lexed *lexval;
	struct node *nodep;
} YYSTYPE;
YYSTYPE yylval;

#define NIL	((struct node *) 0)
struct node nonode;
#define	NOP	(&nonode)	/* a no-op node pointer			*/

struct lexed firsttok;		/* the left edge of the token list	*/
struct lexed lasttok;		/* the right edge of the token list	*/

#endif
cparen.c        423334290   146   47    100444  3330      `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
#include <stdio.h>
#include "cparen.h"
#include "y.tab.h"

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/cparen.c,v 1.8 83/04/17 11:53:19 bradn Exp $";

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);
}
hid.c           423334295   146   47    100444  272       `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/hid.c,v 1.6 83/04/17 11:53:26 bradn Exp $";

#define RCSNAME cparen
#include "cparen.h"
#undef RCSNAME
lex.l           423334297   146   47    100444  6466      `
%{
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
# include "stdio.h"
# include "cparen.h"
# include "y.tab.h"

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/lex.l,v 1.9 83/04/17 11:53:32 bradn Exp $";

#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]|(\\'))+\'	{/* char constant */
				yylval.lexval = nxttok(NAME); return(NAME);
			}
\"([^"\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);
}
parse.y         423334301   146   47    100444  6587      `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
%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

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

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/parse.y,v 1.12 83/04/17 11:53:40 bradn Exp $";

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);
}

bradn@tekecs.UUCP (06/06/83)

Evidently the first attempt at posting this message was damaged in
transit.  This is the second attempt.

Cparen is a filter which fully parenthesizes C expressions.
It is useful to those of us who can never remember operator precedence
and associativity in C.

The source is in Berkeley 'ar' format.  To install it,
	1) save this message into a file called "cparen.ar"
	2) edit cparen.ar and remove the lines from the beginning
	  of the file up to (but not including) the line which begins
	  with "!<arch>".
	3) say "ar xv cparen.ar".
	4) say "make cparen cparen.cat".

Please report any bugs to
	...!decvax!tektronix!tekecs!bradn

======================= the archive begins with the next line ===============
!<arch>
Makefile        423334288   146   47    100444  463       `
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
	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

cparen.1        423334289   146   47    100444  1363      `
.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
Brad Needham, bradn
.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.
.PP
The input is not filtered through the C preprocessor.

cparen.h        423334291   146   47    100444  1830      `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
#ifdef RCSNAME
	static char *RCSNAME = "$Header: /da/bradn/.bin/src/cparen/RCS/cparen.h,v 1.8 83/04/17 11:53:05 bradn Exp $";
#else

#define	TRUE	1
#define	FALSE	0

char *cmd;			/* the name of this command (for error msgs) */

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)	*/
};

struct lexed *nxttok();		/* used by lex to add a token to the list */
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;
};

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

typedef union {
	int intval;
	struct lexed *lexval;
	struct node *nodep;
} YYSTYPE;
YYSTYPE yylval;

#define NIL	((struct node *) 0)
struct node nonode;
#define	NOP	(&nonode)	/* a no-op node pointer			*/

struct lexed firsttok;		/* the left edge of the token list	*/
struct lexed lasttok;		/* the right edge of the token list	*/

#endif
cparen.c        423334290   146   47    100444  3330      `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
#include <stdio.h>
#include "cparen.h"
#include "y.tab.h"

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/cparen.c,v 1.8 83/04/17 11:53:19 bradn Exp $";

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);
}
hid.c           423334295   146   47    100444  272       `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/hid.c,v 1.6 83/04/17 11:53:26 bradn Exp $";

#define RCSNAME cparen
#include "cparen.h"
#undef RCSNAME
lex.l           423334297   146   47    100444  6466      `
%{
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
# include "stdio.h"
# include "cparen.h"
# include "y.tab.h"

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/lex.l,v 1.9 83/04/17 11:53:32 bradn Exp $";

#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]|(\\'))+\'	{/* char constant */
				yylval.lexval = nxttok(NAME); return(NAME);
			}
\"([^"\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);
}
parse.y         423334301   146   47    100444  6587      `
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
%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

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

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/parse.y,v 1.12 83/04/17 11:53:40 bradn Exp $";

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);
}

bradn@tekecs.UUCP (07/20/83)

This is the source for Cparen (mail programs willing), imbedded in
a shell script.
To extract the source:
	make a directory called "cparen";
	save this letter under the name "sharch" in that directory;
	edit this letter(vi sharch), removing all lines up to and
	 including the line beginning with "===== remove this line"
	execute the resulting shell script (sh sharch).
Notes on the make:
	during the make, yacc should print the message:
		conflicts: 7 shift/reduce
	that message is normal -- it is not an error.
If you have any problems building cparen, write me at
	...decvax!tektronix!tekecs!bradn
 Brad Needham

===== remove this line and those above =====
cat >cparen.1 <<\EOF
.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
Brad Needham, bradn
.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.
.PP
The input is not filtered through the C preprocessor.
EOF
cat >Makefile <<\EOF
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
	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
EOF
cat >cparen.c <<\EOF
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
#include <stdio.h>
#include "cparen.h"
#include "y.tab.h"

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/cparen.c,v 1.8 83/04/17 11:53:19 bradn Exp $";

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);
}
EOF
cat >cparen.h <<\EOF
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
#ifdef RCSNAME
	static char *RCSNAME = "$Header: /da/bradn/.bin/src/cparen/RCS/cparen.h,v 1.8 83/04/17 11:53:05 bradn Exp $";
#else

#define	TRUE	1
#define	FALSE	0

char *cmd;			/* the name of this command (for error msgs) */

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)	*/
};

struct lexed *nxttok();		/* used by lex to add a token to the list */
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;
};

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

typedef union {
	int intval;
	struct lexed *lexval;
	struct node *nodep;
} YYSTYPE;
YYSTYPE yylval;

#define NIL	((struct node *) 0)
struct node nonode;
#define	NOP	(&nonode)	/* a no-op node pointer			*/

struct lexed firsttok;		/* the left edge of the token list	*/
struct lexed lasttok;		/* the right edge of the token list	*/

#endif
EOF
cat >hid.c <<\EOF
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/hid.c,v 1.6 83/04/17 11:53:26 bradn Exp $";

#define RCSNAME cparen
#include "cparen.h"
#undef RCSNAME
EOF
tr @ '\134' >lex.l <<\EOF
%{
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
# include "stdio.h"
# include "cparen.h"
# include "y.tab.h"

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/lex.l,v 1.9 83/04/17 11:53:32 bradn Exp $";

#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]|(@@@'))+@'	{/* char constant */
				yylval.lexval = nxttok(NAME); return(NAME);
			}
@"([^"@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);
}
EOF
cat >parse.y <<\EOF
/*
 * Copyright (c) 1983 by
 * Tektronix, Incorporated Beaverton, Oregon 97077
 * All rights reserved.
 */
%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

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

static char *rcsid = "$Header: /da/bradn/.bin/src/cparen/RCS/parse.y,v 1.12 83/04/17 11:53:40 bradn Exp $";

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);
}
EOF