initial commit for github

master
Brian Kernighan 10 years ago
commit 87b94932e6
  1. 23
      LICENSE
  2. 94
      README
  3. 529
      awk.1
  4. 233
      awk.h
  5. 486
      awkgram.y
  6. 77027
      awktest.a
  7. 958
      b.c
  8. 582
      lex.c
  9. 706
      lib.c
  10. 214
      main.c
  11. 96
      makefile
  12. 168
      maketab.c
  13. 276
      parse.c
  14. 195
      proto.h
  15. 1958
      run.c
  16. 459
      tran.c

@ -0,0 +1,23 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/

@ -0,0 +1,94 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
This is the version of awk described in "The AWK Programming Language",
by Al Aho, Brian Kernighan, and Peter Weinberger
(Addison-Wesley, 1988, ISBN 0-201-07981-X).
Changes, mostly bug fixes and occasional enhancements, are listed
in FIXES. If you distribute this code further, please please please
distribute FIXES with it. If you find errors, please report them
to bwk@cs.princeton.edu. Thanks.
The program itself is created by
make
which should produce a sequence of messages roughly like this:
yacc -d awkgram.y
conflicts: 43 shift/reduce, 85 reduce/reduce
mv y.tab.c ytab.c
mv y.tab.h ytab.h
cc -c ytab.c
cc -c b.c
cc -c main.c
cc -c parse.c
cc maketab.c -o maketab
./maketab >proctab.c
cc -c proctab.c
cc -c tran.c
cc -c lib.c
cc -c run.c
cc -c lex.c
cc ytab.o b.o main.o parse.o proctab.o tran.o lib.o run.o lex.o -lm
This produces an executable a.out; you will eventually want to
move this to some place like /usr/bin/awk.
If your system does not have yacc or bison (the GNU
equivalent), you must compile the pieces manually. We have
included yacc output in ytab.c and ytab.h, and backup copies in
case you overwrite them. We have also included a copy of
proctab.c so you do not need to run maketab.
NOTE: This version uses ANSI C, as you should also. We have
compiled this without any changes using gcc -Wall and/or local C
compilers on a variety of systems, but new systems or compilers
may raise some new complaint; reports of difficulties are
welcome.
This also compiles with Visual C++ on all flavors of Windows,
*if* you provide versions of popen and pclose. The file
missing95.c contains versions that can be used to get started
with, though the underlying support has mysterious properties,
the symptom of which can be truncated pipe output. Beware. The
file makefile.win gives hints on how to proceed; if you run
vcvars32.bat, it will set up necessary paths and parameters so
you can subsequently run nmake -f makefile.win. Beware also that
when running on Windows under command.com, various quoting
conventions are different from Unix systems: single quotes won't
work around arguments, and various characters like % are
interpreted within double quotes.
This compiles without change on Macintosh OS X using gcc and
the standard developer tools.
This is also said to compile on Macintosh OS 9 systems, using the
file "buildmac" provided by Dan Allen (danallen@microsoft.com),
to whom many thanks.
The version of malloc that comes with some systems is sometimes
astonishly slow. If awk seems slow, you might try fixing that.
More generally, turning on optimization can significantly improve
awk's speed, perhaps by 1/3 for highest levels.

529
awk.1

@ -0,0 +1,529 @@
.de EX
.nf
.ft CW
..
.de EE
.br
.fi
.ft 1
..
awk
.TH AWK 1
.CT 1 files prog_other
.SH NAME
awk \- pattern-directed scanning and processing language
.SH SYNOPSIS
.B awk
[
.BI \-F
.I fs
]
[
.BI \-v
.I var=value
]
[
.I 'prog'
|
.BI \-f
.I progfile
]
[
.I file ...
]
.SH DESCRIPTION
.I Awk
scans each input
.I file
for lines that match any of a set of patterns specified literally in
.IR prog
or in one or more files
specified as
.B \-f
.IR progfile .
With each pattern
there can be an associated action that will be performed
when a line of a
.I file
matches the pattern.
Each line is matched against the
pattern portion of every pattern-action statement;
the associated action is performed for each matched pattern.
The file name
.B \-
means the standard input.
Any
.IR file
of the form
.I var=value
is treated as an assignment, not a filename,
and is executed at the time it would have been opened if it were a filename.
The option
.B \-v
followed by
.I var=value
is an assignment to be done before
.I prog
is executed;
any number of
.B \-v
options may be present.
The
.B \-F
.IR fs
option defines the input field separator to be the regular expression
.IR fs.
.PP
An input line is normally made up of fields separated by white space,
or by regular expression
.BR FS .
The fields are denoted
.BR $1 ,
.BR $2 ,
\&..., while
.B $0
refers to the entire line.
If
.BR FS
is null, the input line is split into one field per character.
.PP
A pattern-action statement has the form
.IP
.IB pattern " { " action " }
.PP
A missing
.BI { " action " }
means print the line;
a missing pattern always matches.
Pattern-action statements are separated by newlines or semicolons.
.PP
An action is a sequence of statements.
A statement can be one of the following:
.PP
.EX
.ta \w'\f(CWdelete array[expression]'u
.RS
.nf
.ft CW
if(\fI expression \fP)\fI statement \fP\fR[ \fPelse\fI statement \fP\fR]\fP
while(\fI expression \fP)\fI statement\fP
for(\fI expression \fP;\fI expression \fP;\fI expression \fP)\fI statement\fP
for(\fI var \fPin\fI array \fP)\fI statement\fP
do\fI statement \fPwhile(\fI expression \fP)
break
continue
{\fR [\fP\fI statement ... \fP\fR] \fP}
\fIexpression\fP #\fR commonly\fP\fI var = expression\fP
print\fR [ \fP\fIexpression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
printf\fI format \fP\fR[ \fP,\fI expression-list \fP\fR] \fP\fR[ \fP>\fI expression \fP\fR]\fP
return\fR [ \fP\fIexpression \fP\fR]\fP
next #\fR skip remaining patterns on this input line\fP
nextfile #\fR skip rest of this file, open next, start at top\fP
delete\fI array\fP[\fI expression \fP] #\fR delete an array element\fP
delete\fI array\fP #\fR delete all elements of array\fP
exit\fR [ \fP\fIexpression \fP\fR]\fP #\fR exit immediately; status is \fP\fIexpression\fP
.fi
.RE
.EE
.DT
.PP
Statements are terminated by
semicolons, newlines or right braces.
An empty
.I expression-list
stands for
.BR $0 .
String constants are quoted \&\f(CW"\ "\fR,
with the usual C escapes recognized within.
Expressions take on string or numeric values as appropriate,
and are built using the operators
.B + \- * / % ^
(exponentiation), and concatenation (indicated by white space).
The operators
.B
! ++ \-\- += \-= *= /= %= ^= > >= < <= == != ?:
are also available in expressions.
Variables may be scalars, array elements
(denoted
.IB x [ i ] )
or fields.
Variables are initialized to the null string.
Array subscripts may be any string,
not necessarily numeric;
this allows for a form of associative memory.
Multiple subscripts such as
.B [i,j,k]
are permitted; the constituents are concatenated,
separated by the value of
.BR SUBSEP .
.PP
The
.B print
statement prints its arguments on the standard output
(or on a file if
.BI > file
or
.BI >> file
is present or on a pipe if
.BI | cmd
is present), separated by the current output field separator,
and terminated by the output record separator.
.I file
and
.I cmd
may be literal names or parenthesized expressions;
identical string values in different statements denote
the same open file.
The
.B printf
statement formats its expression list according to the format
(see
.IR printf (3)) .
The built-in function
.BI close( expr )
closes the file or pipe
.IR expr .
The built-in function
.BI fflush( expr )
flushes any buffered output for the file or pipe
.IR expr .
.PP
The mathematical functions
.BR exp ,
.BR log ,
.BR sqrt ,
.BR sin ,
.BR cos ,
and
.BR atan2
are built in.
Other built-in functions:
.TF length
.TP
.B length
the length of its argument
taken as a string,
or of
.B $0
if no argument.
.TP
.B rand
random number on (0,1)
.TP
.B srand
sets seed for
.B rand
and returns the previous seed.
.TP
.B int
truncates to an integer value
.TP
.BI substr( s , " m" , " n\fB)
the
.IR n -character
substring of
.I s
that begins at position
.IR m
counted from 1.
.TP
.BI index( s , " t" )
the position in
.I s
where the string
.I t
occurs, or 0 if it does not.
.TP
.BI match( s , " r" )
the position in
.I s
where the regular expression
.I r
occurs, or 0 if it does not.
The variables
.B RSTART
and
.B RLENGTH
are set to the position and length of the matched string.
.TP
.BI split( s , " a" , " fs\fB)
splits the string
.I s
into array elements
.IB a [1] ,
.IB a [2] ,
\&...,
.IB a [ n ] ,
and returns
.IR n .
The separation is done with the regular expression
.I fs
or with the field separator
.B FS
if
.I fs
is not given.
An empty string as field separator splits the string
into one array element per character.
.TP
.BI sub( r , " t" , " s\fB)
substitutes
.I t
for the first occurrence of the regular expression
.I r
in the string
.IR s .
If
.I s
is not given,
.B $0
is used.
.TP
.B gsub
same as
.B sub
except that all occurrences of the regular expression
are replaced;
.B sub
and
.B gsub
return the number of replacements.
.TP
.BI sprintf( fmt , " expr" , " ...\fB )
the string resulting from formatting
.I expr ...
according to the
.IR printf (3)
format
.I fmt
.TP
.BI system( cmd )
executes
.I cmd
and returns its exit status
.TP
.BI tolower( str )
returns a copy of
.I str
with all upper-case characters translated to their
corresponding lower-case equivalents.
.TP
.BI toupper( str )
returns a copy of
.I str
with all lower-case characters translated to their
corresponding upper-case equivalents.
.PD
.PP
The ``function''
.B getline
sets
.B $0
to the next input record from the current input file;
.B getline
.BI < file
sets
.B $0
to the next record from
.IR file .
.B getline
.I x
sets variable
.I x
instead.
Finally,
.IB cmd " | getline
pipes the output of
.I cmd
into
.BR getline ;
each call of
.B getline
returns the next line of output from
.IR cmd .
In all cases,
.B getline
returns 1 for a successful input,
0 for end of file, and \-1 for an error.
.PP
Patterns are arbitrary Boolean combinations
(with
.BR "! || &&" )
of regular expressions and
relational expressions.
Regular expressions are as in
.IR egrep ;
see
.IR grep (1).
Isolated regular expressions
in a pattern apply to the entire line.
Regular expressions may also occur in
relational expressions, using the operators
.BR ~
and
.BR !~ .
.BI / re /
is a constant regular expression;
any string (constant or variable) may be used
as a regular expression, except in the position of an isolated regular expression
in a pattern.
.PP
A pattern may consist of two patterns separated by a comma;
in this case, the action is performed for all lines
from an occurrence of the first pattern
though an occurrence of the second.
.PP
A relational expression is one of the following:
.IP
.I expression matchop regular-expression
.br
.I expression relop expression
.br
.IB expression " in " array-name
.br
.BI ( expr , expr,... ") in " array-name
.PP
where a relop is any of the six relational operators in C,
and a matchop is either
.B ~
(matches)
or
.B !~
(does not match).
A conditional is an arithmetic expression,
a relational expression,
or a Boolean combination
of these.
.PP
The special patterns
.B BEGIN
and
.B END
may be used to capture control before the first input line is read
and after the last.
.B BEGIN
and
.B END
do not combine with other patterns.
.PP
Variable names with special meanings:
.TF FILENAME
.TP
.B CONVFMT
conversion format used when converting numbers
(default
.BR "%.6g" )
.TP
.B FS
regular expression used to separate fields; also settable
by option
.BI \-F fs.
.TP
.BR NF
number of fields in the current record
.TP
.B NR
ordinal number of the current record
.TP
.B FNR
ordinal number of the current record in the current file
.TP
.B FILENAME
the name of the current input file
.TP
.B RS
input record separator (default newline)
.TP
.B OFS
output field separator (default blank)
.TP
.B ORS
output record separator (default newline)
.TP
.B OFMT
output format for numbers (default
.BR "%.6g" )
.TP
.B SUBSEP
separates multiple subscripts (default 034)
.TP
.B ARGC
argument count, assignable
.TP
.B ARGV
argument array, assignable;
non-null members are taken as filenames
.TP
.B ENVIRON
array of environment variables; subscripts are names.
.PD
.PP
Functions may be defined (at the position of a pattern-action statement) thus:
.IP
.B
function foo(a, b, c) { ...; return x }
.PP
Parameters are passed by value if scalar and by reference if array name;
functions may be called recursively.
Parameters are local to the function; all other variables are global.
Thus local variables may be created by providing excess parameters in
the function definition.
.SH EXAMPLES
.TP
.EX
length($0) > 72
.EE
Print lines longer than 72 characters.
.TP
.EX
{ print $2, $1 }
.EE
Print first two fields in opposite order.
.PP
.EX
BEGIN { FS = ",[ \et]*|[ \et]+" }
{ print $2, $1 }
.EE
.ns
.IP
Same, with input fields separated by comma and/or blanks and tabs.
.PP
.EX
.nf
{ s += $1 }
END { print "sum is", s, " average is", s/NR }
.fi
.EE
.ns
.IP
Add up first column, print sum and average.
.TP
.EX
/start/, /stop/
.EE
Print all lines between start/stop pairs.
.PP
.EX
.nf
BEGIN { # Simulate echo(1)
for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i]
printf "\en"
exit }
.fi
.EE
.SH SEE ALSO
.IR lex (1),
.IR sed (1)
.br
A. V. Aho, B. W. Kernighan, P. J. Weinberger,
.I
The AWK Programming Language,
Addison-Wesley, 1988. ISBN 0-201-07981-X
.SH BUGS
There are no explicit conversions between numbers and strings.
To force an expression to be treated as a number add 0 to it;
to force it to be treated as a string concatenate
\&\f(CW""\fP to it.
.br
The scope rules for variables in functions are a botch;
the syntax is worse.

233
awk.h

@ -0,0 +1,233 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
#include <assert.h>
typedef double Awkfloat;
/* unsigned char is more trouble than it's worth */
typedef unsigned char uschar;
#define xfree(a) { if ((a) != NULL) { free((void *) (a)); (a) = NULL; } }
#define NN(p) ((p) ? (p) : "(null)") /* guaranteed non-null for dprintf
*/
#define DEBUG
#ifdef DEBUG
/* uses have to be doubly parenthesized */
# define dprintf(x) if (dbg) printf x
#else
# define dprintf(x)
#endif
extern int compile_time; /* 1 if compiling, 0 if running */
extern int safe; /* 0 => unsafe, 1 => safe */
#define RECSIZE (8 * 1024) /* sets limit on records, fields, etc., etc. */
extern int recsize; /* size of current record, orig RECSIZE */
extern char **FS;
extern char **RS;
extern char **ORS;
extern char **OFS;
extern char **OFMT;
extern Awkfloat *NR;
extern Awkfloat *FNR;
extern Awkfloat *NF;
extern char **FILENAME;
extern char **SUBSEP;
extern Awkfloat *RSTART;
extern Awkfloat *RLENGTH;
extern char *record; /* points to $0 */
extern int lineno; /* line number in awk program */
extern int errorflag; /* 1 if error has occurred */
extern int donefld; /* 1 if record broken into fields */
extern int donerec; /* 1 if record is valid (no fld has changed */
extern char inputFS[]; /* FS at time of input, for field splitting */
extern int dbg;
extern char *patbeg; /* beginning of pattern matched */
extern int patlen; /* length of pattern matched. set in b.c */
/* Cell: all information about a variable or constant */
typedef struct Cell {
uschar ctype; /* OCELL, OBOOL, OJUMP, etc. */
uschar csub; /* CCON, CTEMP, CFLD, etc. */
char *nval; /* name, for variables only */
char *sval; /* string value */
Awkfloat fval; /* value as number */
int tval; /* type info: STR|NUM|ARR|FCN|FLD|CON|DONTFREE */
struct Cell *cnext; /* ptr to next if chained */
} Cell;
typedef struct Array { /* symbol table array */
int nelem; /* elements in table right now */
int size; /* size of tab */
Cell **tab; /* hash table pointers */
} Array;
#define NSYMTAB 50 /* initial size of a symbol table */
extern Array *symtab;
extern Cell *nrloc; /* NR */
extern Cell *fnrloc; /* FNR */
extern Cell *nfloc; /* NF */
extern Cell *rstartloc; /* RSTART */
extern Cell *rlengthloc; /* RLENGTH */
/* Cell.tval values: */
#define NUM 01 /* number value is valid */
#define STR 02 /* string value is valid */
#define DONTFREE 04 /* string space is not freeable */
#define CON 010 /* this is a constant */
#define ARR 020 /* this is an array */
#define FCN 040 /* this is a function name */
#define FLD 0100 /* this is a field $1, $2, ... */
#define REC 0200 /* this is $0 */
/* function types */
#define FLENGTH 1
#define FSQRT 2
#define FEXP 3
#define FLOG 4
#define FINT 5
#define FSYSTEM 6
#define FRAND 7
#define FSRAND 8
#define FSIN 9
#define FCOS 10
#define FATAN 11
#define FTOUPPER 12
#define FTOLOWER 13
#define FFLUSH 14
/* Node: parse tree is made of nodes, with Cell's at bottom */
typedef struct Node {
int ntype;
struct Node *nnext;
int lineno;
int nobj;
struct Node *narg[1]; /* variable: actual size set by calling malloc */
} Node;
#define NIL ((Node *) 0)
extern Node *winner;
extern Node *nullstat;
extern Node *nullnode;
/* ctypes */
#define OCELL 1
#define OBOOL 2
#define OJUMP 3
/* Cell subtypes: csub */
#define CFREE 7
#define CCOPY 6
#define CCON 5
#define CTEMP 4
#define CNAME 3
#define CVAR 2
#define CFLD 1
#define CUNK 0
/* bool subtypes */
#define BTRUE 11
#define BFALSE 12
/* jump subtypes */
#define JEXIT 21
#define JNEXT 22
#define JBREAK 23
#define JCONT 24
#define JRET 25
#define JNEXTFILE 26
/* node types */
#define NVALUE 1
#define NSTAT 2
#define NEXPR 3
extern int pairstack[], paircnt;
#define notlegal(n) (n <= FIRSTTOKEN || n >= LASTTOKEN || proctab[n-FIRSTTOKEN] == nullproc)
#define isvalue(n) ((n)->ntype == NVALUE)
#define isexpr(n) ((n)->ntype == NEXPR)
#define isjump(n) ((n)->ctype == OJUMP)
#define isexit(n) ((n)->csub == JEXIT)
#define isbreak(n) ((n)->csub == JBREAK)
#define iscont(n) ((n)->csub == JCONT)
#define isnext(n) ((n)->csub == JNEXT || (n)->csub == JNEXTFILE)
#define isret(n) ((n)->csub == JRET)
#define isrec(n) ((n)->tval & REC)
#define isfld(n) ((n)->tval & FLD)
#define isstr(n) ((n)->tval & STR)
#define isnum(n) ((n)->tval & NUM)
#define isarr(n) ((n)->tval & ARR)
#define isfcn(n) ((n)->tval & FCN)
#define istrue(n) ((n)->csub == BTRUE)
#define istemp(n) ((n)->csub == CTEMP)
#define isargument(n) ((n)->nobj == ARG)
/* #define freeable(p) (!((p)->tval & DONTFREE)) */
#define freeable(p) ( ((p)->tval & (STR|DONTFREE)) == STR )
/* structures used by regular expression matching machinery, mostly b.c: */
#define NCHARS (256+3) /* 256 handles 8-bit chars; 128 does 7-bit */
/* watch out in match(), etc. */
#define NSTATES 32
typedef struct rrow {
long ltype; /* long avoids pointer warnings on 64-bit */
union {
int i;
Node *np;
uschar *up;
} lval; /* because Al stores a pointer in it! */
int *lfollow;
} rrow;
typedef struct fa {
uschar gototab[NSTATES][NCHARS];
uschar out[NSTATES];
uschar *restr;
int *posns[NSTATES];
int anchor;
int use;
int initstat;
int curstat;
int accept;
int reset;
struct rrow re[1]; /* variable: actual size set by calling malloc */
} fa;
#include "proto.h"

@ -0,0 +1,486 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
%{
#include <stdio.h>
#include <string.h>
#include "awk.h"
void checkdup(Node *list, Cell *item);
int yywrap(void) { return(1); }
Node *beginloc = 0;
Node *endloc = 0;
int infunc = 0; /* = 1 if in arglist or body of func */
int inloop = 0; /* = 1 if in while, for, do */
char *curfname = 0; /* current function name */
Node *arglist = 0; /* list of args for current function */
%}
%union {
Node *p;
Cell *cp;
int i;
char *s;
}
%token <i> FIRSTTOKEN /* must be first */
%token <p> PROGRAM PASTAT PASTAT2 XBEGIN XEND
%token <i> NL ',' '{' '(' '|' ';' '/' ')' '}' '[' ']'
%token <i> ARRAY
%token <i> MATCH NOTMATCH MATCHOP
%token <i> FINAL DOT ALL CCL NCCL CHAR OR STAR QUEST PLUS EMPTYRE
%token <i> AND BOR APPEND EQ GE GT LE LT NE IN
%token <i> ARG BLTIN BREAK CLOSE CONTINUE DELETE DO EXIT FOR FUNC
%token <i> SUB GSUB IF INDEX LSUBSTR MATCHFCN NEXT NEXTFILE
%token <i> ADD MINUS MULT DIVIDE MOD
%token <i> ASSIGN ASGNOP ADDEQ SUBEQ MULTEQ DIVEQ MODEQ POWEQ
%token <i> PRINT PRINTF SPRINTF
%token <p> ELSE INTEST CONDEXPR
%token <i> POSTINCR PREINCR POSTDECR PREDECR
%token <cp> VAR IVAR VARNF CALL NUMBER STRING
%token <s> REGEXPR
%type <p> pas pattern ppattern plist pplist patlist prarg term re
%type <p> pa_pat pa_stat pa_stats
%type <s> reg_expr
%type <p> simple_stmt opt_simple_stmt stmt stmtlist
%type <p> var varname funcname varlist
%type <p> for if else while
%type <i> do st
%type <i> pst opt_pst lbrace rbrace rparen comma nl opt_nl and bor
%type <i> subop print
%right ASGNOP
%right '?'
%right ':'
%left BOR
%left AND
%left GETLINE
%nonassoc APPEND EQ GE GT LE LT NE MATCHOP IN '|'
%left ARG BLTIN BREAK CALL CLOSE CONTINUE DELETE DO EXIT FOR FUNC
%left GSUB IF INDEX LSUBSTR MATCHFCN NEXT NUMBER
%left PRINT PRINTF RETURN SPLIT SPRINTF STRING SUB SUBSTR
%left REGEXPR VAR VARNF IVAR WHILE '('
%left CAT
%left '+' '-'
%left '*' '/' '%'
%left NOT UMINUS
%right POWER
%right DECR INCR
%left INDIRECT
%token LASTTOKEN /* must be last */
%%
program:
pas { if (errorflag==0)
winner = (Node *)stat3(PROGRAM, beginloc, $1, endloc); }
| error { yyclearin; bracecheck(); SYNTAX("bailing out"); }
;
and:
AND | and NL
;
bor:
BOR | bor NL
;
comma:
',' | comma NL
;
do:
DO | do NL
;
else:
ELSE | else NL
;
for:
FOR '(' opt_simple_stmt ';' opt_nl pattern ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
{ --inloop; $$ = stat4(FOR, $3, notnull($6), $9, $12); }
| FOR '(' opt_simple_stmt ';' ';' opt_nl opt_simple_stmt rparen {inloop++;} stmt
{ --inloop; $$ = stat4(FOR, $3, NIL, $7, $10); }
| FOR '(' varname IN varname rparen {inloop++;} stmt
{ --inloop; $$ = stat3(IN, $3, makearr($5), $8); }
;
funcname:
VAR { setfname($1); }
| CALL { setfname($1); }
;
if:
IF '(' pattern rparen { $$ = notnull($3); }
;
lbrace:
'{' | lbrace NL
;
nl:
NL | nl NL
;
opt_nl:
/* empty */ { $$ = 0; }
| nl
;
opt_pst:
/* empty */ { $$ = 0; }
| pst
;
opt_simple_stmt:
/* empty */ { $$ = 0; }
| simple_stmt
;
pas:
opt_pst { $$ = 0; }
| opt_pst pa_stats opt_pst { $$ = $2; }
;
pa_pat:
pattern { $$ = notnull($1); }
;
pa_stat:
pa_pat { $$ = stat2(PASTAT, $1, stat2(PRINT, rectonode(), NIL)); }
| pa_pat lbrace stmtlist '}' { $$ = stat2(PASTAT, $1, $3); }
| pa_pat ',' opt_nl pa_pat { $$ = pa2stat($1, $4, stat2(PRINT, rectonode(), NIL)); }
| pa_pat ',' opt_nl pa_pat lbrace stmtlist '}' { $$ = pa2stat($1, $4, $6); }
| lbrace stmtlist '}' { $$ = stat2(PASTAT, NIL, $2); }
| XBEGIN lbrace stmtlist '}'
{ beginloc = linkum(beginloc, $3); $$ = 0; }
| XEND lbrace stmtlist '}'
{ endloc = linkum(endloc, $3); $$ = 0; }
| FUNC funcname '(' varlist rparen {infunc++;} lbrace stmtlist '}'
{ infunc--; curfname=0; defn((Cell *)$2, $4, $8); $$ = 0; }
;
pa_stats:
pa_stat
| pa_stats opt_pst pa_stat { $$ = linkum($1, $3); }
;
patlist:
pattern
| patlist comma pattern { $$ = linkum($1, $3); }
;
ppattern:
var ASGNOP ppattern { $$ = op2($2, $1, $3); }
| ppattern '?' ppattern ':' ppattern %prec '?'
{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
| ppattern bor ppattern %prec BOR
{ $$ = op2(BOR, notnull($1), notnull($3)); }
| ppattern and ppattern %prec AND
{ $$ = op2(AND, notnull($1), notnull($3)); }
| ppattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
| ppattern MATCHOP ppattern
{ if (constnode($3))
$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
else
$$ = op3($2, (Node *)1, $1, $3); }
| ppattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
| '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
| ppattern term %prec CAT { $$ = op2(CAT, $1, $2); }
| re
| term
;
pattern:
var ASGNOP pattern { $$ = op2($2, $1, $3); }
| pattern '?' pattern ':' pattern %prec '?'
{ $$ = op3(CONDEXPR, notnull($1), $3, $5); }
| pattern bor pattern %prec BOR
{ $$ = op2(BOR, notnull($1), notnull($3)); }
| pattern and pattern %prec AND
{ $$ = op2(AND, notnull($1), notnull($3)); }
| pattern EQ pattern { $$ = op2($2, $1, $3); }
| pattern GE pattern { $$ = op2($2, $1, $3); }
| pattern GT pattern { $$ = op2($2, $1, $3); }
| pattern LE pattern { $$ = op2($2, $1, $3); }
| pattern LT pattern { $$ = op2($2, $1, $3); }
| pattern NE pattern { $$ = op2($2, $1, $3); }
| pattern MATCHOP reg_expr { $$ = op3($2, NIL, $1, (Node*)makedfa($3, 0)); }
| pattern MATCHOP pattern
{ if (constnode($3))
$$ = op3($2, NIL, $1, (Node*)makedfa(strnode($3), 0));
else
$$ = op3($2, (Node *)1, $1, $3); }
| pattern IN varname { $$ = op2(INTEST, $1, makearr($3)); }
| '(' plist ')' IN varname { $$ = op2(INTEST, $2, makearr($5)); }
| pattern '|' GETLINE var {
if (safe) SYNTAX("cmd | getline is unsafe");
else $$ = op3(GETLINE, $4, itonp($2), $1); }
| pattern '|' GETLINE {
if (safe) SYNTAX("cmd | getline is unsafe");
else $$ = op3(GETLINE, (Node*)0, itonp($2), $1); }
| pattern term %prec CAT { $$ = op2(CAT, $1, $2); }
| re
| term
;
plist:
pattern comma pattern { $$ = linkum($1, $3); }
| plist comma pattern { $$ = linkum($1, $3); }
;
pplist:
ppattern
| pplist comma ppattern { $$ = linkum($1, $3); }
;
prarg:
/* empty */ { $$ = rectonode(); }
| pplist
| '(' plist ')' { $$ = $2; }
;
print:
PRINT | PRINTF
;
pst:
NL | ';' | pst NL | pst ';'
;
rbrace:
'}' | rbrace NL
;
re:
reg_expr
{ $$ = op3(MATCH, NIL, rectonode(), (Node*)makedfa($1, 0)); }
| NOT re { $$ = op1(NOT, notnull($2)); }
;
reg_expr:
'/' {startreg();} REGEXPR '/' { $$ = $3; }
;
rparen:
')' | rparen NL
;
simple_stmt:
print prarg '|' term {
if (safe) SYNTAX("print | is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg APPEND term {
if (safe) SYNTAX("print >> is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg GT term {
if (safe) SYNTAX("print > is unsafe");
else $$ = stat3($1, $2, itonp($3), $4); }
| print prarg { $$ = stat3($1, $2, NIL, NIL); }
| DELETE varname '[' patlist ']' { $$ = stat2(DELETE, makearr($2), $4); }
| DELETE varname { $$ = stat2(DELETE, makearr($2), 0); }
| pattern { $$ = exptostat($1); }
| error { yyclearin; SYNTAX("illegal statement"); }
;
st:
nl
| ';' opt_nl
;
stmt:
BREAK st { if (!inloop) SYNTAX("break illegal outside of loops");
$$ = stat1(BREAK, NIL); }
| CONTINUE st { if (!inloop) SYNTAX("continue illegal outside of loops");
$$ = stat1(CONTINUE, NIL); }
| do {inloop++;} stmt {--inloop;} WHILE '(' pattern ')' st
{ $$ = stat2(DO, $3, notnull($7)); }
| EXIT pattern st { $$ = stat1(EXIT, $2); }
| EXIT st { $$ = stat1(EXIT, NIL); }
| for
| if stmt else stmt { $$ = stat3(IF, $1, $2, $4); }
| if stmt { $$ = stat3(IF, $1, $2, NIL); }
| lbrace stmtlist rbrace { $$ = $2; }
| NEXT st { if (infunc)
SYNTAX("next is illegal inside a function");
$$ = stat1(NEXT, NIL); }
| NEXTFILE st { if (infunc)
SYNTAX("nextfile is illegal inside a function");
$$ = stat1(NEXTFILE, NIL); }
| RETURN pattern st { $$ = stat1(RETURN, $2); }
| RETURN st { $$ = stat1(RETURN, NIL); }
| simple_stmt st
| while {inloop++;} stmt { --inloop; $$ = stat2(WHILE, $1, $3); }
| ';' opt_nl { $$ = 0; }
;
stmtlist:
stmt
| stmtlist stmt { $$ = linkum($1, $2); }
;
subop:
SUB | GSUB
;
term:
term '/' ASGNOP term { $$ = op2(DIVEQ, $1, $4); }
| term '+' term { $$ = op2(ADD, $1, $3); }
| term '-' term { $$ = op2(MINUS, $1, $3); }
| term '*' term { $$ = op2(MULT, $1, $3); }
| term '/' term { $$ = op2(DIVIDE, $1, $3); }
| term '%' term { $$ = op2(MOD, $1, $3); }
| term POWER term { $$ = op2(POWER, $1, $3); }
| '-' term %prec UMINUS { $$ = op1(UMINUS, $2); }
| '+' term %prec UMINUS { $$ = $2; }
| NOT term %prec UMINUS { $$ = op1(NOT, notnull($2)); }
| BLTIN '(' ')' { $$ = op2(BLTIN, itonp($1), rectonode()); }
| BLTIN '(' patlist ')' { $$ = op2(BLTIN, itonp($1), $3); }
| BLTIN { $$ = op2(BLTIN, itonp($1), rectonode()); }
| CALL '(' ')' { $$ = op2(CALL, celltonode($1,CVAR), NIL); }
| CALL '(' patlist ')' { $$ = op2(CALL, celltonode($1,CVAR), $3); }
| CLOSE term { $$ = op1(CLOSE, $2); }
| DECR var { $$ = op1(PREDECR, $2); }
| INCR var { $$ = op1(PREINCR, $2); }
| var DECR { $$ = op1(POSTDECR, $1); }
| var INCR { $$ = op1(POSTINCR, $1); }
| GETLINE var LT term { $$ = op3(GETLINE, $2, itonp($3), $4); }
| GETLINE LT term { $$ = op3(GETLINE, NIL, itonp($2), $3); }
| GETLINE var { $$ = op3(GETLINE, $2, NIL, NIL); }
| GETLINE { $$ = op3(GETLINE, NIL, NIL, NIL); }
| INDEX '(' pattern comma pattern ')'
{ $$ = op2(INDEX, $3, $5); }
| INDEX '(' pattern comma reg_expr ')'
{ SYNTAX("index() doesn't permit regular expressions");
$$ = op2(INDEX, $3, (Node*)$5); }
| '(' pattern ')' { $$ = $2; }
| MATCHFCN '(' pattern comma reg_expr ')'
{ $$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa($5, 1)); }
| MATCHFCN '(' pattern comma pattern ')'
{ if (constnode($5))
$$ = op3(MATCHFCN, NIL, $3, (Node*)makedfa(strnode($5), 1));
else
$$ = op3(MATCHFCN, (Node *)1, $3, $5); }
| NUMBER { $$ = celltonode($1, CCON); }
| SPLIT '(' pattern comma varname comma pattern ')' /* string */
{ $$ = op4(SPLIT, $3, makearr($5), $7, (Node*)STRING); }
| SPLIT '(' pattern comma varname comma reg_expr ')' /* const /regexp/ */
{ $$ = op4(SPLIT, $3, makearr($5), (Node*)makedfa($7, 1), (Node *)REGEXPR); }
| SPLIT '(' pattern comma varname ')'
{ $$ = op4(SPLIT, $3, makearr($5), NIL, (Node*)STRING); } /* default */
| SPRINTF '(' patlist ')' { $$ = op1($1, $3); }
| STRING { $$ = celltonode($1, CCON); }
| subop '(' reg_expr comma pattern ')'
{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, rectonode()); }
| subop '(' pattern comma pattern ')'
{ if (constnode($3))
$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, rectonode());
else
$$ = op4($1, (Node *)1, $3, $5, rectonode()); }
| subop '(' reg_expr comma pattern comma var ')'
{ $$ = op4($1, NIL, (Node*)makedfa($3, 1), $5, $7); }
| subop '(' pattern comma pattern comma var ')'
{ if (constnode($3))
$$ = op4($1, NIL, (Node*)makedfa(strnode($3), 1), $5, $7);
else
$$ = op4($1, (Node *)1, $3, $5, $7); }
| SUBSTR '(' pattern comma pattern comma pattern ')'
{ $$ = op3(SUBSTR, $3, $5, $7); }
| SUBSTR '(' pattern comma pattern ')'
{ $$ = op3(SUBSTR, $3, $5, NIL); }
| var
;
var:
varname
| varname '[' patlist ']' { $$ = op2(ARRAY, makearr($1), $3); }
| IVAR { $$ = op1(INDIRECT, celltonode($1, CVAR)); }
| INDIRECT term { $$ = op1(INDIRECT, $2); }
;
varlist:
/* nothing */ { arglist = $$ = 0; }
| VAR { arglist = $$ = celltonode($1,CVAR); }
| varlist comma VAR {
checkdup($1, $3);
arglist = $$ = linkum($1,celltonode($3,CVAR)); }
;
varname:
VAR { $$ = celltonode($1, CVAR); }
| ARG { $$ = op1(ARG, itonp($1)); }
| VARNF { $$ = op1(VARNF, (Node *) $1); }
;
while:
WHILE '(' pattern rparen { $$ = notnull($3); }
;
%%
void setfname(Cell *p)
{
if (isarr(p))
SYNTAX("%s is an array, not a function", p->nval);
else if (isfcn(p))
SYNTAX("you can't define function %s more than once", p->nval);
curfname = p->nval;
}
int constnode(Node *p)
{
return isvalue(p) && ((Cell *) (p->narg[0]))->csub == CCON;
}
char *strnode(Node *p)
{
return ((Cell *)(p->narg[0]))->sval;
}
Node *notnull(Node *n)
{
switch (n->nobj) {
case LE: case LT: case EQ: case NE: case GT: case GE:
case BOR: case AND: case NOT:
return n;
default:
return op2(NE, n, nullnode);
}
}
void checkdup(Node *vl, Cell *cp) /* check if name already in list */
{
char *s = cp->nval;
for ( ; vl; vl = vl->nnext) {
if (strcmp(s, ((Cell *)(vl->narg[0]))->nval) == 0) {
SYNTAX("duplicate argument %s", s);
break;
}
}
}

77027
awktest.a

File diff suppressed because one or more lines are too long

958
b.c

@ -0,0 +1,958 @@
/****************************************************************
Copyright (C) Lucent Technologies 1997
All Rights Reserved
Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appear in all
copies and that both that the copyright notice and this
permission notice and warranty disclaimer appear in supporting
documentation, and that the name Lucent Technologies or any of
its entities not be used in advertising or publicity pertaining
to distribution of the software without specific, written prior
permission.
LUCENT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS.
IN NO EVENT SHALL LUCENT OR ANY OF ITS ENTITIES BE LIABLE FOR ANY
SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF
THIS SOFTWARE.
****************************************************************/
/* lasciate ogne speranza, voi ch'intrate. */
#define DEBUG
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "awk.h"
#include "ytab.h"
#define HAT (NCHARS+2) /* matches ^ in regular expr */
/* NCHARS is 2**n */
#define MAXLIN 22
#define type(v) (v)->nobj /* badly overloaded here */
#define info(v) (v)->ntype /* badly overloaded here */
#define left(v) (v)->narg[0]
#define right(v) (v)->narg[1]
#define parent(v) (v)->nnext
#define LEAF case CCL: case NCCL: case CHAR: case DOT: case FINAL: case ALL:
#define ELEAF case EMPTYRE: /* empty string in regexp */
#define UNARY case STAR: case PLUS: case QUEST:
/* encoding in tree Nodes:
leaf (CCL, NCCL, CHAR, DOT, FINAL, ALL, EMPTYRE):
left is index, right contains value or pointer to value
unary (STAR, PLUS, QUEST): left is child, right is null
binary (CAT, OR): left and right are children
parent contains pointer to parent
*/
int *setvec;
int *tmpset;
int maxsetvec = 0;
int rtok; /* next token in current re */
int rlxval;
static uschar *rlxstr;
static uschar *prestr; /* current position in current re */
static uschar *lastre; /* origin of last re */
static int setcnt;
static int poscnt;
char *patbeg;
int patlen;
#define NFA 20 /* cache this many dynamic fa's */
fa *fatab[NFA];
int nfatab = 0; /* entries in fatab */
fa *makedfa(const char *s, int anchor) /* returns dfa for reg expr s */
{
int i, use, nuse;
fa *pfa;
static int now = 1;
if (setvec == 0) { /* first time through any RE */
maxsetvec = MAXLIN;
setvec = (int *) malloc(maxsetvec * sizeof(int));
tmpset = (int *) malloc(maxsetvec * sizeof(int));
if (setvec == 0 || tmpset == 0)
overflo("out of space initializing makedfa");
}
if (compile_time) /* a constant for sure */
return mkdfa(s, anchor);
for (i = 0; i < nfatab; i++) /* is it there already? */
if (fatab[i]->anchor == anchor
&& strcmp((const char *) fatab[i]->restr, s) == 0) {
fatab[i]->use = now++;
return fatab[i];
}
pfa = mkdfa(s, anchor);
if (nfatab < NFA) { /* room for another */
fatab[nfatab] = pfa;
fatab[nfatab]->use = now++;
nfatab++;
return pfa;
}
use = fatab[0]->use; /* replace least-recently used */
nuse = 0;
for (i = 1; i < nfatab; i++)
if (fatab[i]->use < use) {
use = fatab[i]->use;
nuse = i;
}
freefa(fatab[nuse]);
fatab[nuse] = pfa;
pfa->use = now++;
return pfa;
}
fa *mkdfa(const char *s, int anchor) /* does the real work of making a dfa */
/* anchor = 1 for anchored matches, else 0 */
{
Node *p, *p1;
fa *f;
p = reparse(s);
p1 = op2(CAT, op2(STAR, op2(ALL, NIL, NIL), NIL), p);
/* put ALL STAR in front of reg. exp. */
p1 = op2(CAT, p1, op2(FINAL, NIL, NIL));
/* put FINAL after reg. exp. */
poscnt = 0;
penter(p1); /* enter parent pointers and leaf indices */
if ((f = (fa *) calloc(1, sizeof(fa) + poscnt*sizeof(rrow))) == NULL)
overflo("out of space for fa");
f->accept = poscnt-1; /* penter has computed number of positions in re */
cfoll(f, p1); /* set up follow sets */
freetr(p1);
if ((f->posns[0] = (int *) calloc(1, *(f->re[0].lfollow)*sizeof(int))) == NULL)
overflo("out of space in makedfa");
if ((f->posns[1] = (int *) calloc(1, sizeof(int))) == NULL)
overflo("out of space in makedfa");
*f->posns[1] = 0;
f->initstat = makeinit(f, anchor);
f->anchor = anchor;
f->restr = (uschar *) tostring(s);
return f;
}
int makeinit(fa *f, int anchor)
{
int i, k;
f->curstat = 2;
f->out[2] = 0;
f->reset = 0;
k = *(f->re[0].lfollow);
xfree(f->posns[2]);
if ((f->posns[2] = (int *) calloc(1, (k+1)*sizeof(int))) == NULL)
overflo("out of space in makeinit");
for (i=0; i <= k; i++) {
(f->posns[2])[i] = (f->re[0].lfollow)[i];
}
if ((f->posns[2])[1] == f->accept)
f->out[2] = 1;
for (i=0; i < NCHARS; i++)
f->gototab[2][i] = 0;
f->curstat = cgoto(f, 2, HAT);
if (anchor) {
*f->posns[2] = k-1; /* leave out position 0 */
for (i=0; i < k; i++) {
(f->posns[0])[i] = (f->posns[2])[i];
}
f->out[0] = f->out[2];
if (f->curstat != 2)
--(*f->posns[f->curstat]);
}
return f->curstat;
}
void penter(Node *p) /* set up parent pointers and leaf indices */
{
switch (type(p)) {
ELEAF
LEAF
info(p) = poscnt;
poscnt++;
break;
UNARY
penter(left(p));
parent(left(p)) = p;
break;
case CAT:
case OR:
penter(left(p));
penter(right(p));
parent(left(p)) = p;
parent(right(p)) = p;
break;
default: /* can't happen */
FATAL("can't happen: unknown type %d in penter", type(p));
break;
}
}
void freetr(Node *p) /* free parse tree */
{
switch (type(p)) {
ELEAF
LEAF
xfree(p);
break;
UNARY
freetr(left(p));
xfree(p);
break;