root/usr/src/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); }
        ;

%%