Cparen - C expression parenthesizer, Version 2
Brad Needham
bradn at hammer.UUCP
Wed Jul 18 03:29:33 AEST 1984
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]+ at .[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 at 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 at 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. at n", cmd);
exit(1);
}
if (!(node->l_text = malloc(strlen(text) + 1))) {
fprintf(stderr, "%s: out of memory. at 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!
More information about the Comp.sources.unix
mailing list