OpenSolaris_b135/cmd/awk_xpg4/awk.y

%{
/*
 * 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
 * or http://www.opensolaris.org/os/licensing.
 * 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 -- YACC grammar
 *
 * Copyright (c) 1995 by Sun Microsystems, Inc.
 *
 * Copyright 1986, 1992 by Mortice Kern Systems Inc.  All rights reserved.
 *
 * This Software is unpublished, valuable, confidential property of
 * Mortice Kern Systems Inc.  Use is authorized only in accordance
 * with the terms and conditions of the source licence agreement
 * protecting this Software.  Any unauthorized use or disclosure of
 * this Software is strictly prohibited and will result in the
 * termination of the licence agreement.
 *
 * NOTE: this grammar correctly produces NO shift/reduce conflicts from YACC.
 *
 */

/*
 * Do not use any character constants as tokens, so the resulting C file
 * is codeset independent.
 */

#ident	"%Z%%M%	%I%	%E% SMI"
#include "awk.h"
static NODE * fliplist ANSI((NODE *np));
%}

%union	{
	NODE	*node;
};

/*
 * Do not use any character constants as tokens, so the resulting C file
 * is codeset independent.
 *
 * Declare terminal symbols before their operator
 * precedences to get them in a contiguous block
 * for giant switches in action() and exprreduce().
 */
/* Tokens from exprreduce() */
%token	<node>	PARM ARRAY UFUNC FIELD IN INDEX CONCAT
%token	<node>	NOT AND OR EXP QUEST
%token	<node>	EQ NE GE LE GT LT
%token	<node>	ADD SUB MUL DIV REM INC DEC PRE_INC PRE_DEC
%token	<node>	GETLINE CALLFUNC RE TILDE NRE

/* Tokens shared by exprreduce() and action() */
%token		ASG

/* Tokens from action() */
%token	<node>	PRINT PRINTF
%token	<node>	EXIT RETURN BREAK CONTINUE NEXT
%token	<node>	DELETE WHILE DO FOR FORIN IF

/*
 * Terminal symbols not used in action() and exprrreduce()
 * switch statements.
 */
%token	<node>	CONSTANT VAR FUNC
%token	<node>	DEFFUNC BEGIN END CLOSE ELSE PACT
%right		ELSE
%token		DOT CALLUFUNC

/*
 * Tokens not used in grammar
 */
%token		KEYWORD SVAR
%token		PIPESYM

/*
 * Tokens representing character constants
 * TILDE, '~', taken care of above
 */
%token BAR		/* '|' */ 
       CARAT		/* '^' */ 	
       LANGLE		/* '<' */
       RANGLE		/* '>' */
       PLUSC		/* '+' */
       HYPHEN		/* '-' */
       STAR		/* '*' */
       SLASH		/* '/' */
       PERCENT		/* '%' */
       EXCLAMATION	/* '!' */
       DOLLAR		/* '$' */
       LSQUARE		/* '[' */
       RSQUARE		/* ']' */
       LPAREN		/* '(' */
       RPAREN		/* ')' */
       SEMI		/* ';' */
       LBRACE		/* '{' */
       RBRACE		/* '}' */

/*
 * Priorities of operators
 * Lowest to highest
 */
%left	COMMA
%right	BAR PIPE WRITE APPEND
%right	ASG AADD ASUB AMUL ADIV AREM AEXP
%right	QUEST COLON
%left	OR
%left	AND
%left	IN
%left	CARAT	
%left	TILDE NRE
%left	EQ NE LANGLE RANGLE GE LE
%left	CONCAT
%left	PLUSC HYPHEN 
%left	STAR SLASH PERCENT 
%right	UPLUS UMINUS
%right	EXCLAMATION	
%right	EXP
%right	INC DEC URE
%left	DOLLAR LSQUARE RSQUARE
%left	LPAREN RPAREN 

%type	<node>	prog rule pattern expr rvalue lvalue fexpr varlist varlist2
%type	<node>	statement statlist fileout exprlist eexprlist simplepattern
%type	<node>	getline optvar var
%type	<node>	dummy

%start	dummy
%%

dummy:
		prog			= {
			yytree = fliplist(yytree);
		}
		;
prog:
	  rule				= {
		yytree = $1;
	}
	| rule SEMI prog		= {
		if ($1 != NNULL) {
			if (yytree != NNULL)
				yytree = node(COMMA, $1, yytree); else
				yytree = $1;
		}
	}
	;

rule:	  pattern LBRACE statlist RBRACE	= {
		$$ = node(PACT, $1, $3);
		doing_begin = 0;
	}
	| LBRACE statlist RBRACE		= {
		npattern++;
		$$ = node(PACT, NNULL, $2);
	}
	| pattern				= {
		$$ = node(PACT, $1, node(PRINT, NNULL, NNULL));
		doing_begin = 0;
	}
	| DEFFUNC VAR
		{ $2->n_type = UFUNC; funparm = 1; }
	    LPAREN varlist RPAREN 
		{ funparm = 0; }
	    LBRACE statlist { uexit($5); } RBRACE = {
		$2->n_ufunc = node(DEFFUNC, $5, fliplist($9));
		$$ = NNULL;
	}
	| DEFFUNC UFUNC				= {
		awkerr((char *) gettext("function \"%S\" redefined"), $2->n_name);
		/* NOTREACHED */
	}
	|					= {
		$$ = NNULL;
	}
	;

pattern:
	  simplepattern
	| expr COMMA expr			= {
		++npattern;
		$$ = node(COMMA, $1, $3);
	}
	;

simplepattern:
	  BEGIN					= {
		$$ = node(BEGIN, NNULL, NNULL);
		doing_begin++;
	}
	| END					= {
		++npattern;
		$$ = node(END, NNULL, NNULL);
	}
	| expr					 = {
		++npattern;
		$$ = $1;
	}
	;

eexprlist:
	  exprlist
	|					= {
		$$ = NNULL;
	}
	;

exprlist:
	  expr %prec COMMA
	| exprlist COMMA expr			= {
		$$ = node(COMMA, $1, $3);
	}
	;

varlist:
	  					= {
		$$ = NNULL;
	}
	| varlist2
	;

varlist2:
	  var
	| var COMMA varlist2			= {
		$$ = node(COMMA, $1, $3);
	}
	;

fexpr:
	  expr
	|					= {
		$$ = NNULL;
	}
	;

/*
 * Normal expression (includes regular expression)
 */
expr:
	  expr PLUSC expr			= {
		$$ = node(ADD, $1, $3);
	}
	| expr HYPHEN expr			= {
		$$ = node(SUB, $1, $3);
	}
	| expr STAR expr			= {
		$$ = node(MUL, $1, $3);
	}
	| expr SLASH expr			= {
		$$ = node(DIV, $1, $3);
	}
	| expr PERCENT expr			= {
		$$ = node(REM, $1, $3);
	}
	| expr EXP expr				= {
		$$ = node(EXP, $1, $3);
	}
	| expr AND expr				= {
		$$ = node(AND, $1, $3);
	}
	| expr OR expr				= {
		$$ = node(OR, $1, $3);
	}
	| expr QUEST expr COLON expr		= {
		$$ = node(QUEST, $1, node(COLON, $3, $5));
	}
	| lvalue ASG expr			= {
		$$ = node(ASG, $1, $3);
	}
	| lvalue AADD expr			= {
		$$ = node(AADD, $1, $3);
	}
	| lvalue ASUB expr			= {
		$$ = node(ASUB, $1, $3);
	}
	| lvalue AMUL expr			= {
		$$ = node(AMUL, $1, $3);
	}
	| lvalue ADIV expr			= {
		$$ = node(ADIV, $1, $3);
	}
	| lvalue AREM expr			= {
		$$ = node(AREM, $1, $3);
	}
	| lvalue AEXP expr			= {
		$$ = node(AEXP, $1, $3);
	}
	| lvalue INC				= {
		$$ = node(INC, $1, NNULL);
	}
	| lvalue DEC				= {
		$$ = node(DEC, $1, NNULL);
	}
	| expr EQ expr				= {
		$$ = node(EQ, $1, $3);
	}
	| expr NE expr				= {
		$$ = node(NE, $1, $3);
	}
	| expr RANGLE expr			= {
		$$ = node(GT, $1, $3);
	}
	| expr LANGLE expr			= {
		$$ = node(LT, $1, $3);
	}
	| expr GE expr				= {
		$$ = node(GE, $1, $3);
	}
	| expr LE expr				= {
		$$ = node(LE, $1, $3);
	}
	| expr TILDE expr			= {
		$$ = node(TILDE, $1, $3);
	}
	| expr NRE expr				= {
		$$ = node(NRE, $1, $3);
	}
	| expr IN var				= {
		$$ = node(IN, $3, $1);
	}
	| LPAREN exprlist RPAREN IN var		= {
		$$ = node(IN, $5, $2);
	}
	| getline
	| rvalue
	| expr CONCAT expr			= {
		$$ = node(CONCAT, $1, $3);
	}
	;

lvalue:
	  DOLLAR rvalue				= {
		$$ = node(FIELD, $2, NNULL);
	}
	/*
	 * Prevents conflict with FOR LPAREN var IN var RPAREN production
	 */
	| var %prec COMMA
	| var LSQUARE exprlist RSQUARE		= {
		$$ = node(INDEX, $1, $3);
	}
	;

var:
	  VAR
	| PARM
	;

rvalue:
	  lvalue %prec COMMA
	| CONSTANT
	| LPAREN expr RPAREN term		= {
		$$ = $2;
	}
	| EXCLAMATION expr			= {
		$$ = node(NOT, $2, NNULL);
	}
	| HYPHEN expr %prec UMINUS		= {
		$$ = node(SUB, const0, $2);
	}
	| PLUSC expr %prec UPLUS		= {
		$$ = $2;
	}
	| DEC lvalue				= {
		$$ = node(PRE_DEC, $2, NNULL);
	}
	| INC lvalue				= {
		$$ = node(PRE_INC, $2, NNULL);
	}
	| FUNC					= {
		$$ = node(CALLFUNC, $1, NNULL);
	}
	| FUNC LPAREN eexprlist RPAREN term	= {
		$$ = node(CALLFUNC, $1, $3);
	}
	| UFUNC LPAREN eexprlist RPAREN term	= {
		$$ = node(CALLUFUNC, $1, $3);
	}
	| VAR LPAREN eexprlist RPAREN term	= {
		$$ = node(CALLUFUNC, $1, $3);
	}
	| SLASH {redelim='/';} URE SLASH %prec URE	= {
		$$ = $<node>3;
	}
	;

statement:
	  FOR LPAREN fexpr SEMI fexpr SEMI fexpr RPAREN statement = {
		$$ = node(FOR, node(COMMA, $3, node(COMMA, $5, $7)), $9);
	}
	| FOR LPAREN var IN var RPAREN statement = {
		register NODE *np;

		/*
		 * attempt to optimize statements for the form 
		 *    for (i in x) delete x[i]
		 * to
		 *    delete x
		 */
		np = $7;
		if (np != NNULL
		 && np->n_type == DELETE
		 && (np = np->n_left)->n_type == INDEX
		 && np->n_left == $5
		 && np->n_right == $3)
			$$ = node(DELETE, $5, NNULL);
		else
			$$ = node(FORIN, node(IN, $3, $5), $7);
	}
	| WHILE LPAREN expr RPAREN statement	= {
		$$ = node(WHILE, $3, $5);
	}
	| DO statement WHILE LPAREN expr RPAREN	= {
		$$ = node(DO, $5, $2);
	}
	| IF LPAREN expr RPAREN statement ELSE statement = {
		$$ = node(IF, $3, node(ELSE, $5, $7));
	}
	| IF LPAREN expr RPAREN statement %prec ELSE	= {
		$$ = node(IF, $3, node(ELSE, $5, NNULL));
	}
	| CONTINUE SEMI				= {
		$$ = node(CONTINUE, NNULL, NNULL);
	}
	| BREAK SEMI				= {
		$$ = node(BREAK, NNULL, NNULL);
	}
	| NEXT SEMI				= {
		$$ = node(NEXT, NNULL, NNULL);
	}
	| DELETE lvalue SEMI			= {
		$$ = node(DELETE, $2, NNULL);
	}
	| RETURN fexpr SEMI			= {
		$$ = node(RETURN, $2, NNULL);
	}
	| EXIT fexpr SEMI			= {
		$$ = node(EXIT, $2, NNULL);
	}
	| PRINT eexprlist fileout SEMI		= {
		$$ = node(PRINT, $2, $3);
	}
	| PRINT LPAREN exprlist RPAREN fileout SEMI	= {
		$$ = node(PRINT, $3, $5);
	}
	| PRINTF exprlist fileout SEMI		= {
		$$ = node(PRINTF, $2, $3);
	}
	| PRINTF LPAREN exprlist RPAREN fileout SEMI	= {
		$$ = node(PRINTF, $3, $5);
	}
	| expr SEMI 				= {
		$$ = $1;
	}
	| SEMI					= {
		$$ = NNULL;
	}
	| LBRACE statlist RBRACE		= {
		$$ = $2;
	}
	;


statlist:
	  statement
	| statlist statement			= {
		if ($1 == NNULL)
			$$ = $2;
		else if ($2 == NNULL)
			$$ = $1;
		else
			$$ = node(COMMA, $1, $2);
	}
	;

fileout:
	  WRITE expr				= {
		$$ = node(WRITE, $2, NNULL);
	}
	| APPEND expr				= {
		$$ = node(APPEND, $2, NNULL);
	}
	| PIPE expr				= {
		$$ = node(PIPE, $2, NNULL);
	}
	|					= {
		$$ = NNULL;
	}
	;

getline:
	  GETLINE optvar %prec WRITE		= {
		$$ = node(GETLINE, $2, NNULL);
	}
	| expr BAR GETLINE optvar		= {
		$$ = node(GETLINE, $4, node(PIPESYM, $1, NNULL));
	}
	| GETLINE optvar LANGLE expr		= {
		$$ = node(GETLINE, $2, node(LT, $4, NNULL));
	}
	;

optvar:
	  lvalue
	|					= {
		$$ = NNULL;
	}
	;

term:
	  {catterm = 1;}
	;
%%
/*
 * Flip a left-recursively generated list
 * so that it can easily be traversed from left
 * to right without recursion.
 */
static NODE *
fliplist(np)
register NODE *np;
{
	int type;

	if (np!=NNULL && !isleaf(np->n_flags)
#if 0
		 && (type = np->n_type)!=FUNC && type!=UFUNC
#endif
	) {
		np->n_right = fliplist(np->n_right);
		if ((type=np->n_type)==COMMA) {
			register NODE *lp;

			while ((lp = np->n_left)!=NNULL && lp->n_type==COMMA) {
				register NODE* *spp;

				lp->n_right = fliplist(lp->n_right);
				for (spp = &lp->n_right;
				    *spp != NNULL && (*spp)->n_type==COMMA;
				     spp = &(*spp)->n_right)
					;
				np->n_left = *spp;
				*spp = np;
				np = lp;
			}
		}
		if (np->n_left != NULL &&
		    (type = np->n_left->n_type)!= FUNC && type!=UFUNC)
			np->n_left = fliplist(np->n_left);
	}
	return (np);
}