awk3.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* awk -- executor
*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Copyright 1985, 1994 by Mortice Kern Systems Inc. All rights reserved.
*
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "awk.h"
#include "y.tab.h"
static void freetemps(void);
/*
* This code allows for integers to be stored in longs (type INT) and
* only promoted to double precision floating point numbers (type REAL)
* when overflow occurs during +, -, or * operations. This is very
* non-portable if you desire such a speed optimisation. You may wish
* to put something here for your system. This "something" would likely
* include either an assembler "jump on overflow" instruction or a
* method to get traps on overflows from the hardware.
*
* This portable method works for ones and twos complement integer
* representations (which is, realistically) almost all machines.
*/
#if __TURBOC__
#else
/*
* These are portable to two's complement integer machines
*/
#endif
static char notarray[] = "scalar \"%s\" cannot be used as array";
static char badarray[] = "array \"%s\" cannot be used as a scalar";
static char varnotfunc[] = "variable \"%s\" cannot be used as a function";
static char tmfld[] = "Too many fields (LIMIT: %d)";
static char toolong[] = "Record too long (LIMIT: %d bytes)";
static char divzero[] = "division (/ or %%) by zero";
static char toodeep[] = "too deeply nested for in loop (LIMIT: %d)";
#define NINDEXBUF 50
static int concflag; /* In CONCAT operation (no frees) */
/*
* The following stack is used to store the next pointers for all nested
* for-in loops. This needs to be global so that delete can check to see
* if it is deleting the next node to be used by a loop.
*/
#define NFORINLOOP 10
/*
* Assign a string directly to a NODE without creating an intermediate
* NODE. This can handle either FALLOC, FSTATIC, FNOALLOC or FSENSE for
* "flags" argument. Also the NODE "np" must be reduced to an lvalue
* (PARM nodes are not acceptable).
*/
void
{
return;
}
} else {
}
}
} else
}
/*
* Assign to a variable node.
* LHS must be a VAR type and RHS must be reduced by now.
* To speed certain operations up, check for
* certain things here and do special assignments.
*/
NODE *
{
register int len;
/* short circuit assignment of a node to itself */
return (np);
else
}
setrefield(np);
else
}
return (np);
}
}
return (np);
} else
else
return (np);
}
/*
* Set regular expression FS value.
*/
static void
{
int n;
}
}
/*
* Assign to an l-value node.
*/
NODE *
{
} else
top:
case INDEX:
case VAR:
case PARM:
/*
* If it's a parameter then link to the actual value node and
* do the checks again.
*/
goto top;
case FIELD:
case CALLUFUNC:
case UFUNC:
default:
}
/* NOTREACHED */
}
/*
* Compiled tree non-terminal node.
*/
NODE *
{
return (np);
}
/*
* Create an integer node.
*/
NODE *
{
return (np);
}
/*
* Create a real number node.
*/
NODE *
{
return (np);
}
/*
* Make a node for a string.
*/
NODE *
{
} else {
}
}
} else
return (np);
}
/*
* Save a copy of a string.
*/
{
return (new);
}
/*
* Allocate an empty node of given type.
* String space for the node is given by `length'.
*/
NODE *
{
} else {
}
}
return (np);
}
/*
* Free a node.
*/
void
{
}
}
/*
* Install a keyword of given `type'.
*/
void
{
register size_t l;
}
/*
* Install built-in function.
*/
NODE *
{
register size_t l;
return np;
}
/*
* Lookup an identifier.
* nocreate contains the following flag values:
* 1 if no creation of a new NODE,
* 0 if ok to create new NODE
*/
NODE *
{
return (np);
}
if (nocreate) {
} else {
}
return (np);
}
/*
* Add a symbol to the table.
*/
void
{
}
/*
* Delete the given node from the symbol table.
* If fflag is non-zero, also free the node space.
* This routine must also check the stack of forin loop pointers. If
* we are deleting the next item to be used, then the pointer must be
* advanced.
*/
void
{
register ushort h;
/*
* check all of the for-in loop pointers
* to see if any need to be advanced because
* this element is being deleted.
*/
if (next_forin != forindex) {
sptr = next_forin;
do {
break;
}
}
if (fflag)
break;
}
}
}
/*
* Hashing function.
*/
static int
{
register int hash = 0;
while (*name != '\0')
return (hash);
}
/*
* Top level executor for an awk programme.
* This will be passed: pattern, action or a list of these.
* The former function to evaluate a pattern has been
* subsumed into this function for speed.
* Patterns are:
* BEGIN,
* END,
* other expressions (including regular expressions)
*/
void
{
register int type;
if (phase != 0) {
linebuf[0] = '\0';
lbuflen = 0;
}
} else {
}
/*
* Save the parent node and evaluate the pattern.
* If it evaluates to false (0) just continue
*/
if (phase != 0)
continue;
} else if (phase != 0) {
continue;
continue;
/*
* The grammar only allows expressions
* to be separated by the ',' operator
* for range patterns.
*/
} else
continue;
continue;
loopexit = 0;
break;
}
}
freetemps();
}
/*
* Free all temporary nodes.
*/
static void
{
if (concflag)
return;
}
}
}
}
/*
* Do the given action.
* Actions are statements or expressions.
*/
static int
{
register int act = 0;
register NODE *l;
} else {
}
freetemps();
/*
* Don't change order of these cases without
* changing order in awk.y declarations.
* The order is optimised.
*/
case ASG:
continue;
case PRINT:
continue;
case PRINTF:
continue;
case EXIT:
act = 0;
/* NOTREACHED */
case RETURN:
if (slevel == 0)
: const0;
return (RETURN);
case NEXT:
case BREAK:
case CONTINUE:
case DELETE:
l = l->n_next;
l = l->n_alink;
}
switch (l->n_type) {
case ARRAY:
delarray(l);
break;
case INDEX:
}
/*
* get pointer to the node for this array
* element using the hash key.
*/
l = exprreduce(l);
/*
* now search linearly from the beginning of
* the list to find the element before the
* one being deleted. This must be done
* because arrays are singley-linked.
*/
break;
}
}
delsymtab(l, 1);
break;
case VAR:
break;
default:
"may delete only array element or array"));
break;
}
continue;
case WHILE:
case DO:
break;
continue;
case FOR:
break;
continue;
case FORIN:
break;
continue;
case IF:
break;
continue;
default:
(void)exprreduce(np);
if (loopexit != 0) {
break;
}
continue;
}
return (act);
}
return (0);
}
/*
* Delete an entire array
*/
void
{
}
}
/*
* Return the INT value of an expression.
*/
{
goto leaf;
}
case CONSTANT:
case VAR:
leaf:
default:
}
/* NOTREACHED */
}
/*
* Return a real number from an expression tree.
*/
{
if (loopexit)
return (loopexit);
goto leaf;
}
case CONSTANT:
case VAR:
leaf:
default:
}
/* NOTREACHED */
}
/*
* Return a string from an expression tree.
*/
{
goto leaf;
}
case CONSTANT:
case VAR:
leaf:
{
char *tmp;
}
default:
}
/* NOTREACHED */
}
/*
* Convert number to string.
*/
static wchar_t *
lltoa(long long l)
{
register int s;
register int neg;
if (l == 0)
return (zero);
*--p = '\0';
if (l < 0)
neg = 1, l = -l; else
neg = 0;
if ((s = (short)l) == l) {
while (s != 0) {
*--p = s%10 + '0';
s /= 10;
}
} else {
while (l != 0) {
*--p = l%10 + '0';
l /= 10;
}
}
if (neg)
*--p = '-';
}
/*
* Return pointer to node with concatenation of operands of CONCAT node.
* In the interest of speed, a left recursive tree of CONCAT nodes
* is handled with a single malloc. The accumulated lengths of the
* right operands are passed down recursive invocations of this
* routine, which allocates a large enough string when the left
* operand is not a CONCAT node.
*/
static NODE *
{
/* we KNOW (np->n_type==CONCAT) */
int rlen;
} else
}
} else {
} else
}
return (lnp);
}
/*
* Reduce an expression to a terminal node.
*/
NODE *
{
register int temp;
register int t;
register int tag;
/*
* a var or constant is a leaf-node (no further reduction required)
* so return immediately.
*/
return (np);
/*
* If it's a parameter then it is probably a leaf node but it
* might be an array so we check.. If it is an array, then signal
* an error as an array by itself cannot be used in this context.
*/
if (t == PARM)
else
return (np);
/*
* All the rest are non-leaf nodes.
*/
switch (t) {
case CALLUFUNC:
case FIELD:
case IN:
case INDEX:
tag = 0;
/* initially formal var name and array key name are the same */
}
}
else {
/* promotion to array */
} else {
tag = 0;
}
}
}
"SYMTAB must have exactly one index"));
return (np);
}
}
} else
return (np);
case CONCAT:
++concflag;
--concflag;
return (np);
case NOT:
case AND:
case OR:
case EXP:
{
/* evaluate expressions in proper order before
* calling pow().
* Can't guarantee that compiler will do this
* correctly for us if we put them inline.
*/
}
case QUEST:
return (exprreduce(np));
case EQ:
case NE:
case GE:
case LE:
case GT:
case LT:
return (comparison(np));
case ADD:
case SUB:
case MUL:
case DIV:
case REM:
return (arithmetic(np));
case DEC:
goto do_inc_op;
case INC:
else
return (tnp);
case PRE_DEC:
goto do_pinc_op;
case PRE_INC:
case AADD:
goto do_asn_op;
case ASUB:
goto do_asn_op;
case AMUL:
goto do_asn_op;
case ADIV:
goto do_asn_op;
case AREM:
goto do_asn_op;
case AEXP:
case GETLINE:
case CALLFUNC:
case RE:
return (const1);
return (const0);
case TILDE:
return (const1);
return (const0);
case NRE:
return (const1);
return (const0);
case ASG:
case ARRAY:
case UFUNC:
default:
/* NOTREACHED */
}
}
/*
* Do arithmetic operators.
*/
static NODE *
{
int type;
} else {
} else {
}
}
case ADD:
addoverflow();
} else
break;
/*
* Strategically placed between ADD and SUB
* so "jo" branches will reach on 80*86
*/
goto reswitch;
case SUB:
suboverflow();
} else
break;
case MUL:
muloverflow();
} else
break;
case DIV:
}
if (r2 == 0.0)
break;
case REM:
if (i2 == 0)
} else {
double fmod(double x, double y);
errno = 0;
}
break;
}
}
/*
* Do comparison operators.
*/
static NODE *
{
register int cmp;
} else
} else {
++concflag;
--concflag;
}
/*
* Posix mandates semantics for the comparison operators that
* are incompatible with traditional AWK behaviour. If the following
* define is true then awk will use the traditional behaviour.
* if it's false, then AWK will use the POSIX-mandated behaviour.
*/
#define TRADITIONAL 0
#if TRADITIONAL
cmp = -1;
cmp = 1;
else
cmp = 0;
} else {
cmp = -1;
cmp = 1;
else
cmp = 0;
}
#else
} else {
goto do_strcmp;
cmp = -1;
cmp = 1;
else
cmp = 0;
} else {
cmp = -1;
cmp = 1;
else
cmp = 0;
}
}
#endif
case EQ:
case NE:
case GE:
case LE:
case GT:
case LT:
default:
}
/* NOTREACHED */
}
/*
* Return the type of a constant that is a string.
* The node must be a FSTRING type and the return value
* will possibly have FVINT or FVREAL or'ed in.
*/
static int
{
int somedigits = 0;
int seene = 0;
int seenradix = 0;
int seensign = 0;
int digitsaftere = 0;
if (*cp == '\0')
cp++;
cp++;
while (*cp != '\0') {
switch (*cp) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
if (seene)
digitsaftere = 1;
somedigits++;
break;
case 'E':
case 'e':
if (seene || !somedigits)
return (FSTRING);
seene = 1;
break;
case '+':
case '-':
return (FSTRING);
seensign = 1;
break;
default:
if (*cp == radixpoint) {
return (FSTRING);
} else
return (FSTRING);
seenradix = 1;
}
cp++;
}
if (somedigits == 0)
return (FSTRING);
if (seensign && !digitsaftere)
return (FSTRING);
else
} else
}
/*
* Return a field rvalue.
*/
static NODE *
{
if (fieldno == 0)
if (!splitdone)
fieldsplit();
}
/*
* Split linebuf into fields. Done only once
* per input record (maximum).
*/
void
{
register int n;
fcount = 0;
op += n;
*op++ = '\0';
}
else {
}
splitdone++;
}
/*
* Assign to a field as an lvalue.
* Return the unevaluated node as one doesn't always need it
* evaluated in an assignment.
*/
static NODE *
{
register int i;
register int seplen;
register int newlen;
if (fieldno == 0) {
splitdone = 0;
fieldsplit();
} else {
if (!splitdone)
fieldsplit();
} else {
++nfield;
}
for (i=0; i<nfield; i++) {
}
}
/*
* Reconstruct $0
*/
i = 0;
while (i < nfield) {
if (i < nfield) {
}
}
*op = '\0';
else {
}
}
return (np);
}
/*
* Do a user function.
* Each formal parameter must:
* have the actual parameter assigned to it (call by value),
* have a pointer to an array put into it (call by reference),
* and be made undefined (extra formal parameters)
*/
static NODE *
{
#ifndef M_STKCHK
#else
if (!M_STKCHK)
#endif
{
/* pass through formal list, setting up a list
* (on templist) containing temps for the values
* of the actuals.
* If the actual list runs out before the formal
* list, assign 'constundef' as the value
*/
register int t;
register int scope_tag;
actual = constundef;
} else
scope_tag = 0;
case ARRAY:
t = ARRAY;
scope_tag = 0;
break;
case PARM:
break;
default:
t = VAR;
break;
}
if (t == VAR)
if (t != ARRAY)
/*
* link to actual parameter in case of promotion to
* array
*/
if (actual != constundef)
/*
* Build the templist
*/
} else
else
}
/*
* Bind results of the evaluation of actuals to formals.
*/
}
}
{
++slevel;
}
{
/* if node is a local array, free the elements */
}
}
--slevel;
return (np);
}
/*
* Get the regular expression from an expression tree.
*/
{
}
/*
* Get the next element from a list.
*/
NODE *
{
return (np);
} else {
return (np);
}
}
/*
* if statement.
*/
static int
{
register int test;
if (test)
else
}
/*
* while and do{}while statements.
*/
static int
{
register int act = 0;
goto dowhile;
for (;;) {
break;
switch (act) {
case BREAK:
act = 0;
break;
case CONTINUE:
act = 0;
continue;
}
break;
}
}
return (act);
}
/*
* for statement.
*/
static int
{
register int act = 0;
(void)exprreduce(initnp);
for (;;) {
break;
switch (act) {
case BREAK:
act = 0;
break;
case CONTINUE:
act = 0;
goto clabel;
}
break;
}
(void)exprreduce(incnp);
}
return (act);
}
/*
* for variable in array statement.
*/
static int
{
register int act = 0;
register int issymtab = 0;
register int alen;
int nbuck;
}
issymtab++;
nbuck = 0;
} else {
/*l
* At this point if the node is not actually an array
* check to see if it has already been established as
* a scalar. If it is a scalar then flag an error. If
* not then promote the object to an array type.
*/
else {
/* promotion to array */
}
}
/*
* Set up a pointer to the first node in the array list.
* Save this pointer on the delete stack. This information
* is used by the delete function to advance any pointers
* that might be pointing at a node which has been deleted.
* See the delsymtab() function for more information. Note
* that if the a_link field is nil, then just return 0 since
* this array has no elements yet.
*/
return (0);
/*
* array elements have names of the form
* <name>]<index> (global arrays)
* or
* <name>[<scope>]<index> (local arrays)
* We need to know the offset of the index portion of the
* name string in order to place it in the index variable so
* we look for the ']'. This is calculated here and then
* used below.
*/
}
for (;;) {
if (issymtab) {
break;
} else {
break;
}
switch (act) {
case BREAK:
act = 0;
break;
case CONTINUE:
act = 0;
continue;
}
break;
}
}
next_forin--;
return (act);
}
/*
* Walk the symbol table using the same algorithm as arraynode.
*/
NODE *
{
for (;;) {
}
return (np);
}
}
/* NOTREACHED */
}
/*
* Test the result of an expression.
*/
static int
{
register int t;
return (1);
freetemps();
if (isstring(t))
}
if (isreal(t)) {
return (rval != 0.0);
}
}
/*
* Return malloc'ed space that holds the given name "[" scope "]" index ...
* concatenated string.
* The node (np) is the list of indices and 'array' is the array name.
*/
static wchar_t *
{
register uint n;
register int len;
register int seplen;
register int taglen;
/*
* calculate and create the tag string
*/
/*
* Special (normal) case: only one index.
*/
size_t i;
} else {
}
if (i < NINDEXBUF)
else
if (taglen) {
*cp++ = '[';
while (taglen)
}
*cp++ = ']';
return (ocp);
}
n = 0;
if (n == 0) {
if (taglen) {
cp[n++] = '[';
while (taglen)
}
cp[n++] = ']';
} else {
n += seplen;
}
n += len;
}
return (cp);
}
/*
* Promote a node to an array. In the simplest case, just set the
* node type field to ARRAY. The more complicated case involves walking
* a list of variables that haven't been determined yet as scalar or array.
* This routine plays with the pointers to avoid recursion.
*/
void
{
/*
* walk down the variable chain, reversing the pointers and
* setting each node to type array.
*/
prev = n;
n = next;
}
/*
* If the final entity on the chain is a local variable, then
* reset it's alink field to NNULL - normally it points back
* to itself - this is used in other parts of the code to
* reduce the number of conditionals when handling locals.
*/
/*
* Now walk back up the list setting the alink to point to
* the last entry in the chain and clear the 'local array'
* flag.
*/
}
}