OpenSolaris_b135/cmd/fm/eversholt/common/escparse.y

%{
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (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
 *
 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 *
 * escparse.y -- parser for esc
 *
 * this is the yacc-based parser for Eversholt.  the syntax is simple
 * and is defined by the LALR(1) grammar described by this file.  there
 * should be no shift/reduce or reduce/reduce messages when building this
 * file.
 *
 * as the input is parsed, a parse tree is built by calling the
 * tree_X() functions defined in tree.c.  any syntax errors cause
 * us to skip to the next semicolon, achieved via the "error" clause
 * in the stmt rule below.  the yacc state machine code will call
 * yyerror() in esclex.c and that will keep count of the errors and
 * display the filename, line number, and current input stream of tokens
 * to help the user figure out the problem.  the -Y flag to this program
 * turns on the yacc debugging output which is quite large.  you probably
 * only need to do that if you're debugging the grammar below.
 *
 */

#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include "out.h"
#include "stable.h"
#include "literals.h"
#include "lut.h"
#include "esclex.h"
#include "tree.h"

%}
%union {
	struct tokstr tok;
	struct node *np;
}

%right '='

/*
 * make sure ':' comes immediately after '?' in precedence declarations
 */
%right '?'
%nonassoc ':'

%left OR
%left AND
%left '|'
%left '^'
%left '&'
%left EQ NE
%left LE GE '<' '>'
%left LSHIFT RSHIFT
%left '-' '+'
%left '*' '%' DIV '/'
%right '!' '~'
%left '.'

%token <tok> PROP MASK ARROW EVENT ENGINE ASRU FRU COUNT CONFIG
%token <tok> ID QUOTE NUMBER IF PATHFUNC
%type <tok> enameid
%type <np> root stmtlist stmt nvpairlist nvpair nvname nvexpr
%type <np> exprlist expr iterid ename pname epname eexprlist ipname iname
%type <np> numexpr cexpr func pfunc parglist parg
%type <np> eventlist event nork norkexpr globid propbody

%%

root	: stmtlist
		{ (void)tree_root($1); }
	;

stmtlist   : /*empty*/
		{ $$ = NULL; }
        | stmtlist stmt
		{ $$ = tree_expr(T_LIST, $1, $2); }
	;

stmt	: error ';'
     		{ $$ = tree_nothing(); }
	| IF '(' expr ')' stmt
		{ $$ = $5; }
	| IF '(' expr ')' '{' stmtlist '}'
		{ $$ = $6; }
	| EVENT event nvpairlist ';'
		{ $$ = tree_decl(T_EVENT, $2, $3, $1.file, $1.line); }
	| ENGINE event nvpairlist ';'
		{ $$ = tree_decl(T_ENGINE, $2, $3, $1.file, $1.line); }
	| PROP propbody ';'
		{
			$$ = tree_stmt(T_PROP, $2, $1.file, $1.line);
		}
	| MASK propbody ';'
		{
			$$ = tree_stmt(T_MASK, $2, $1.file, $1.line);
		}
	| ASRU pname nvpairlist ';'
		{
			$$ = tree_decl(T_ASRU, $2, $3, $1.file, $1.line);
		}
	| FRU pname nvpairlist ';'
		{
			$$ = tree_decl(T_FRU, $2, $3, $1.file, $1.line);
		}
	| CONFIG ipname nvpairlist ';'
		{
			$$ = tree_decl(T_CONFIG, $2, $3, $1.file, $1.line);
		}
	| /*superfluous semicolons are ignored*/ ';'
     		{ $$ = tree_nothing(); }
	;

propbody: eventlist nork ARROW nork eventlist
		{
			$$ = tree_arrow($1, $2, $4, $5);
		}
	| propbody nork ARROW nork eventlist
		{
			$$ = tree_arrow($1, $2, $4, $5);
		}
	;

nork	: /* empty */
		{ $$ = NULL; }
	| '(' norkexpr ')'
		{ $$ = $2; }
	;

norkexpr: NUMBER
		{ $$ = tree_num($1.s, $1.file, $1.line); }
	| ID
		/* really can only be 'A', enforced by check_arrow() later */
       		{ $$ = tree_name($1.s, IT_NONE, $1.file, $1.line); }
	| '(' norkexpr ')'
		{ $$ = $2; }
	| norkexpr '-' norkexpr
		{ $$ = tree_expr(T_SUB, $1, $3); }
	| norkexpr '+' norkexpr
		{ $$ = tree_expr(T_ADD, $1, $3); }
	| norkexpr '*' norkexpr
		{ $$ = tree_expr(T_MUL, $1, $3); }
	| norkexpr DIV norkexpr
		{ $$ = tree_expr(T_DIV, $1, $3); }
	| norkexpr '%' norkexpr
		{ $$ = tree_expr(T_MOD, $1, $3); }
	;

nvpairlist: /* empty */
		{ $$ = NULL; }
	| nvpair
	| nvpairlist ',' nvpair
		{ $$ = tree_expr(T_LIST, $1, $3); }
	;
	  
nvpair	: nvname '=' nvexpr
		{ $$ = tree_expr(T_NVPAIR, $1, $3); }
	| ENGINE '=' nvexpr
		/* "engine" is a reserved word, but a valid property name */
		{
			$$ = tree_expr(T_NVPAIR,
				tree_name($1.s, IT_NONE, $1.file, $1.line), $3);
		}
	| COUNT '=' nvexpr
		/* "count" is a reserved word, but a valid property name */
		{
			$$ = tree_expr(T_NVPAIR,
				tree_name($1.s, IT_NONE, $1.file, $1.line), $3);
		}
	;

nvname	: ID
		{ $$ = tree_name($1.s, IT_NONE, $1.file, $1.line); }
	| nvname '-' ID
		{
			/* hack to allow dashes in property names */
			$$ = tree_name_repairdash($1, $3.s);
		}
	;

/* the RHS of an nvpair can be a value, or an ename, or an ename@pname */
nvexpr	: numexpr
	| ename epname
		{ $$ = tree_event($1, $2, NULL); }
	| pname
	| globid
	| func
	| NUMBER ID
		/*
		 * ID must be timevals only ("ms", "us", etc.).
		 * enforced by tree_timeval().
		 */
		{ $$ = tree_timeval($1.s, $2.s, $1.file, $1.line); }
	| QUOTE
		{ $$ = tree_quote($1.s, $1.file, $1.line); }
	;

/* arithmetic operations, no variables or symbols */
numexpr	: numexpr '-' numexpr
		{ $$ = tree_expr(T_SUB, $1, $3); }
	| numexpr '+' numexpr
		{ $$ = tree_expr(T_ADD, $1, $3); }
	| numexpr '*' numexpr
		{ $$ = tree_expr(T_MUL, $1, $3); }
	| numexpr DIV numexpr
		{ $$ = tree_expr(T_DIV, $1, $3); }
	| numexpr '/' numexpr
		{ $$ = tree_expr(T_DIV, $1, $3); }
	| numexpr '%' numexpr
		{ $$ = tree_expr(T_MOD, $1, $3); }
 	| '(' numexpr ')'
		{ $$ = $2; }
	| NUMBER
		{ $$ = tree_num($1.s, $1.file, $1.line); }
	;

eventlist: event
	| eventlist ',' event
		{ $$ = tree_expr(T_LIST, $1, $3); }
	;

event	: ename epname eexprlist
		{ $$ = tree_event($1, $2, $3); }
	;

epname	: /* empty */
		{ $$ = NULL; }
	| '@' pname
		{ $$ = $2; }
	;

eexprlist: /* empty */
		{ $$ = NULL; }
	| '{' exprlist '}'
		{ $$ = $2; }
	;

exprlist: expr
	| exprlist ',' expr
		{ $$ = tree_expr(T_LIST, $1, $3); }
	;

/*
 * note that expr does not include pname, to avoid reduce/reduce
 * conflicts between cexpr and iterid involving the use of ID
 */
expr	: cexpr
	| NUMBER ID
		/*
		 * ID must be timevals only ("ms", "us", etc.).
		 * enforced by tree_timeval().
		 */
		{ $$ = tree_timeval($1.s, $2.s, $1.file, $1.line); }
	;

cexpr	: cexpr '=' cexpr
		{ $$ = tree_expr(T_ASSIGN, $1, $3); }
	| cexpr '?' cexpr
		{ $$ = tree_expr(T_CONDIF, $1, $3); }
	| cexpr ':' cexpr
		{ $$ = tree_expr(T_CONDELSE, $1, $3); }
	| cexpr OR cexpr
		{ $$ = tree_expr(T_OR, $1, $3); }
 	| cexpr AND cexpr
		{ $$ = tree_expr(T_AND, $1, $3); }
	| cexpr '|' cexpr
		{ $$ = tree_expr(T_BITOR, $1, $3); }
	| cexpr '^' cexpr
		{ $$ = tree_expr(T_BITXOR, $1, $3); }
	| cexpr '&' cexpr
		{ $$ = tree_expr(T_BITAND, $1, $3); }
	| cexpr EQ cexpr
		{ $$ = tree_expr(T_EQ, $1, $3); }
	| cexpr NE cexpr
		{ $$ = tree_expr(T_NE, $1, $3); }
	| cexpr '<' cexpr
		{ $$ = tree_expr(T_LT, $1, $3); }
	| cexpr LE cexpr
		{ $$ = tree_expr(T_LE, $1, $3); }
	| cexpr '>' cexpr
		{ $$ = tree_expr(T_GT, $1, $3); }
	| cexpr GE cexpr
		{ $$ = tree_expr(T_GE, $1, $3); }
	| cexpr LSHIFT cexpr
		{ $$ = tree_expr(T_LSHIFT, $1, $3); }
	| cexpr RSHIFT cexpr
		{ $$ = tree_expr(T_RSHIFT, $1, $3); }
	| cexpr '-' cexpr
		{ $$ = tree_expr(T_SUB, $1, $3); }
	| cexpr '+' cexpr
		{ $$ = tree_expr(T_ADD, $1, $3); }
	| cexpr '*' cexpr
		{ $$ = tree_expr(T_MUL, $1, $3); }
	| cexpr DIV cexpr
		{ $$ = tree_expr(T_DIV, $1, $3); }
	| cexpr '/' cexpr
		{ $$ = tree_expr(T_DIV, $1, $3); }
	| cexpr '%' cexpr
		{ $$ = tree_expr(T_MOD, $1, $3); }
	|  '!' cexpr
		{ $$ = tree_expr(T_NOT, $2, NULL); }
	|  '~' cexpr
		{ $$ = tree_expr(T_BITNOT, $2, NULL); }
	| '(' cexpr ')'
		{ $$ = $2; }
	| func
	| NUMBER
		{ $$ = tree_num($1.s, $1.file, $1.line); }
	| ID
       		{
			/* iteration variable */
			$$ = tree_name($1.s, IT_NONE, $1.file, $1.line);
		}
	| globid
	| QUOTE
		{ $$ = tree_quote($1.s, $1.file, $1.line); }
	;

func	: ID '(' ')'
		{ $$ = tree_func($1.s, NULL, $1.file, $1.line); }
	| ID '(' exprlist ')'
		{ $$ = tree_func($1.s, $3, $1.file, $1.line); }
	| PATHFUNC '(' parglist ')'
		{ $$ = tree_func($1.s, $3, $1.file, $1.line); }
	| pfunc
	;

parglist: parg
	| parglist ',' parg
		{ $$ = tree_expr(T_LIST, $1, $3); }
	;

parg	: pfunc
	| pname
		{ $$ = tree_pname($1); }
	| QUOTE
		{ $$ = tree_quote($1.s, $1.file, $1.line); }
	| ID '(' exprlist ')'
		{ $$ = tree_func($1.s, $3, $1.file, $1.line); }
	;

/*
 * these functions are in the grammar so we can force the arg to be
 * a path or an event.  they show up as functions in the parse tree.
 */
pfunc	: ASRU '(' pname ')'
		{ $$ = tree_func($1.s, tree_pname($3), $1.file, $1.line); }
	| FRU '(' pname ')'
		{ $$ = tree_func($1.s, tree_pname($3), $1.file, $1.line); }
	| COUNT '(' event ')'
		{ $$ = tree_func($1.s, $3, $1.file, $1.line); }
	;

globid	: '$' ID
       		{ $$ = tree_globid($2.s, $2.file, $2.line); }
	;

iterid	: ID
       		{ $$ = tree_name($1.s, IT_VERTICAL, $1.file, $1.line); }
	| ID '[' ']'
       		{ $$ = tree_name($1.s, IT_VERTICAL, $1.file, $1.line); }
	| ID '[' cexpr ']'
       		{
			$$ = tree_name_iterator(
			   tree_name($1.s, IT_VERTICAL, $1.file, $1.line), $3);
		}
	| ID '<' '>'
       		{ $$ = tree_name($1.s, IT_HORIZONTAL, $1.file, $1.line); }
	| ID '<' ID '>'
       		{
			$$ = tree_name_iterator(
			    tree_name($1.s, IT_HORIZONTAL, $1.file, $1.line),
			    tree_name($3.s, IT_NONE, $3.file, $3.line));
		}
	| ID '-' iterid
		{
			/* hack to allow dashes in path name components */
			$$ = tree_name_repairdash2($1.s, $3);
		}
	;

/* iname is an ID where we can peel numbers off the end */
iname	: ID
       		{ $$ = tree_iname($1.s, $1.file, $1.line); }
	;

/* base case of ID.ID instead of just ID requires ename to contain one dot */
ename	: ID '.' enameid
       		{
			$$ = tree_name_append(
			    tree_name($1.s, IT_ENAME, $1.file, $1.line),
			    tree_name($3.s, IT_NONE, $3.file, $3.line));
		}
	| ename '.' enameid
		{
			$$ = tree_name_append($1,
			    tree_name($3.s, IT_NONE, $3.file, $3.line));
		}
	| ename '-' enameid
		{
			/*
			 * hack to allow dashes in class names.  when we
			 * detect the dash here, we know we're in a class
			 * name because the left recursion of this rule
			 * means we've already matched at least:
			 * 	ID '.' ID
			 * so the ename here has an incomplete final
			 * component (because the lexer stopped at the
			 * dash).  so we repair that final component here.
			 */
			$$ = tree_name_repairdash($1, $3.s);
		}
	;

/* like an ID, but we let reserved words act unreserved in enames */
enameid	: ID
	| PROP
	| MASK
	| EVENT
	| ENGINE
	| ASRU
	| FRU
	| CONFIG
	| IF
	;

/* pname is a pathname, like x/y, x<i>/y[0], etc */
pname	: iterid
	| pname '/' iterid
		{ $$ = tree_name_append($1, $3); }
	;

/* ipname is an "instanced" pathname, like x0/y1 */
ipname	: iname
	| ipname '/' iname
		{ $$ = tree_name_append($1, $3); }
	;

%%