root/usr/src/cmd/fm/fminject/common/inj_grammar.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
 *
 * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*
 * FMA Event Injector language parser
 */

#include <stdio.h>
#include <stdlib.h>
#include <limits.h>

#include <inj.h>
#include <inj_err.h>
#include <inj_event.h>
#include <inj_hash.h>
#include <inj_lex.h>

%}

%union {
        inj_decl_t *l_decl;
        inj_declmem_t *l_declmem;
        inj_defn_t *l_defn;
        inj_defnmem_t *l_defnmem;
        inj_cmd_t *l_command;
        inj_randelem_t *l_randelem;

        inj_hash_t *l_hash;

        char *l_string;
        uint_t l_number;
        hrtime_t l_hrtime;
}

%type   <l_decl>        decl_memlist
%type   <l_declmem>     decl_mem
%type   <l_declmem>     decl_baremem
%type   <l_declmem>     decl_mem_intr
%type   <l_number>      decl_intr_type
%type   <l_number>      decl_arraydim
%type   <l_declmem>     decl_mem_cplx
%type   <l_hash>        decl_enumlist

%type   <l_defn>        defn_memlist
%type   <l_defnmem>     defn_memvals
%type   <l_defnmem>     defn_val

%type   <l_command>     command
%type   <l_command>     cmd_repeatable
%type   <l_randelem>    rand_problist
%type   <l_randelem>    rand_element

%type   <l_defn>        defined_event
%type   <l_number>      number
%type   <l_hrtime>      hrtime

%token  INJ_TOK_EVDEF
%token  INJ_TOK_FMRIDEF
%token  INJ_TOK_AUTHDEF
%token  INJ_TOK_LISTDEF

%token  INJ_TOK_INT8
%token  INJ_TOK_INT16
%token  INJ_TOK_INT32
%token  INJ_TOK_INT64
%token  INJ_TOK_UINT8
%token  INJ_TOK_UINT16
%token  INJ_TOK_UINT32
%token  INJ_TOK_UINT64
%token  INJ_TOK_BOOLEAN
%token  INJ_TOK_STRING
%token  INJ_TOK_ENUM

%token  INJ_TOK_EVENT
%token  INJ_TOK_FMRI
%token  INJ_TOK_AUTH
%token  INJ_TOK_LIST

%token  INJ_TOK_ADDHRT
%token  INJ_TOK_ENDHRT
%token  INJ_TOK_SLEEP
%token  INJ_TOK_REPEAT
%token  INJ_TOK_RANDOMIZE

%token  <l_string> INJ_TOK_IDENT
%token  <l_string> INJ_TOK_FMACLASS
%token  <l_string> INJ_TOK_IMM
%token  <l_string> INJ_TOK_QSTRING

%%

statement_list: /* EMPTY */
        |       statement_list statement ';'
        ;

statement:      decl
        |       defn
        |       command {
                        if ($1 != NULL)
                                inj_cmds_add($1);
                }
        ;

/*
 * Event, FMRI, Authority, and list declarations
 */

decl:           INJ_TOK_EVDEF INJ_TOK_FMACLASS '{' decl_memlist '}' {
                        if ($4 != NULL)
                                inj_decl_finish($4, $2, ITEMTYPE_EVENT);
                }
        |       INJ_TOK_FMRIDEF INJ_TOK_IDENT '{' decl_memlist '}' {
                        if ($4 != NULL)
                                inj_decl_finish($4, $2, ITEMTYPE_FMRI);
                }
        |       INJ_TOK_AUTHDEF INJ_TOK_IDENT '{' decl_memlist '}' {
                        if ($4 != NULL)
                                inj_decl_finish($4, $2, ITEMTYPE_AUTH);
                }
        |       INJ_TOK_LISTDEF INJ_TOK_IDENT '{' decl_memlist '}' {
                        if ($4 != NULL)
                                inj_decl_finish($4, $2, ITEMTYPE_LIST);
                }
        ;

decl_memlist:   /* EMPTY */     { $$ = NULL; }
        |       decl_memlist decl_mem ';' {
                        if ($2 == NULL) {
                                $$ = $1;
                        } else if ($1 == NULL) {
                                $$ = inj_decl_create($2);
                        } else {
                                inj_decl_addmem($1, $2);
                                $$ = $1;
                        }
                }
        ;

decl_mem:       decl_baremem
        |       decl_baremem decl_arraydim {
                        if ($1 != NULL)
                                inj_decl_mem_make_array($1, $2);
                        $$ = $1;
                }
        ;

decl_baremem:   decl_mem_intr
        |       decl_mem_cplx
        ;

decl_mem_intr:  decl_intr_type INJ_TOK_IDENT {
                        $$ = inj_decl_mem_create($2, $1);
                }
        ;

decl_intr_type: INJ_TOK_INT8            { $$ = MEMTYPE_INT8; }
        |       INJ_TOK_INT16           { $$ = MEMTYPE_INT16; }
        |       INJ_TOK_INT32           { $$ = MEMTYPE_INT32; }
        |       INJ_TOK_INT64           { $$ = MEMTYPE_INT64; }
        |       INJ_TOK_UINT8           { $$ = MEMTYPE_UINT8; }
        |       INJ_TOK_UINT16          { $$ = MEMTYPE_UINT16; }
        |       INJ_TOK_UINT32          { $$ = MEMTYPE_UINT32; }
        |       INJ_TOK_UINT64          { $$ = MEMTYPE_UINT64; }
        |       INJ_TOK_BOOLEAN         { $$ = MEMTYPE_BOOL; }
        |       INJ_TOK_STRING          { $$ = MEMTYPE_STRING; }
        ;

decl_arraydim:  '[' number ']' {
                        $$ = $2;
                }
        |       '[' ']' {
                        $$ = 0;
                }
        ;

decl_mem_cplx:  INJ_TOK_ENUM INJ_TOK_IDENT '{' decl_enumlist '}' {
                        $$ = inj_decl_mem_create_enum($2, $4);
                }
        |       INJ_TOK_EVENT INJ_TOK_FMACLASS INJ_TOK_IDENT {
                        $$ = inj_decl_mem_create_defined($3, $2,
                            ITEMTYPE_EVENT);
                }
        |       INJ_TOK_FMRI INJ_TOK_IDENT INJ_TOK_IDENT {
                        $$ = inj_decl_mem_create_defined($3, $2,
                            ITEMTYPE_FMRI);
                }
        |       INJ_TOK_AUTH INJ_TOK_IDENT INJ_TOK_IDENT {
                        $$ = inj_decl_mem_create_defined($3, $2,
                            ITEMTYPE_AUTH);
                }
        |       INJ_TOK_LIST INJ_TOK_IDENT INJ_TOK_IDENT {
                        $$ = inj_decl_mem_create_defined($3, $2,
                            ITEMTYPE_LIST);
                }
        ;

decl_enumlist:  INJ_TOK_IDENT {
                        $$ = inj_zalloc(sizeof (inj_hash_t));
                        inj_strhash_create($$);

                        inj_strhash_insert($$, $1, 1);
                }
        |       decl_enumlist ',' INJ_TOK_IDENT {
                        if (inj_strhash_lookup($1, $3) != NULL)
                                yyerror("duplicate enum value \"%s\"", $3);
                        else
                                inj_strhash_insert($1, $3, 1);
                        $$ = $1;
                }
        ;

/*
 * Event, FMRI, Authority, and list definitions
 */

defn:           INJ_TOK_EVENT INJ_TOK_FMACLASS INJ_TOK_IDENT '='
                    '{' defn_memlist '}' {
                        inj_defn_finish($6, $2, $3, ITEMTYPE_EVENT);
                        inj_strfree($2);
                }
        |       INJ_TOK_FMRI INJ_TOK_IDENT INJ_TOK_IDENT '='
                    '{' defn_memlist '}' {
                        inj_defn_finish($6, $2, $3, ITEMTYPE_FMRI);
                        inj_strfree($2);
                }
        |       INJ_TOK_AUTH INJ_TOK_IDENT INJ_TOK_IDENT '='
                    '{' defn_memlist '}' {
                        inj_defn_finish($6, $2, $3, ITEMTYPE_AUTH);
                        inj_strfree($2);
                }
        ;

defn_memlist:   defn_memvals {
                        $$ = inj_defn_create($1);
                }
        |       defn_memlist ',' defn_memvals {
                        inj_defn_addmem($1, $3);
                        $$ = $1;
                }
        ;

defn_memvals:   defn_val
        |       INJ_TOK_EVENT INJ_TOK_FMACLASS {
                        $$ = inj_defn_mem_create($2, DEFNMEM_EVENT);
                }
        |       INJ_TOK_FMRI INJ_TOK_IDENT {
                        $$ = inj_defn_mem_create($2, DEFNMEM_FMRI);
                }
        |       INJ_TOK_AUTH INJ_TOK_IDENT {
                        $$ = inj_defn_mem_create($2, DEFNMEM_AUTH);
                }
        |       '[' defn_memlist ']' {
                        $$ = inj_defn_mem_create_list($2, DEFNMEM_ARRAY);
                }
        |       '{' defn_memlist '}' {
                        $$ = inj_defn_mem_create_list($2, DEFNMEM_LIST);
                }
        ;

defn_val:       INJ_TOK_IMM {
                        $$ = inj_defn_mem_create($1, DEFNMEM_IMM);
                }
        |       INJ_TOK_IDENT {
                        $$ = inj_defn_mem_create($1, DEFNMEM_IDENT);
                }
        |       INJ_TOK_QSTRING {
                        $$ = inj_defn_mem_create($1, DEFNMEM_QSTRING);
                }
        ;

/*
 * Commands
 */

command:        cmd_repeatable
        |       INJ_TOK_ADDHRT hrtime { $$ = inj_cmd_addhrt($2); }
        |       INJ_TOK_ENDHRT { $$ = inj_cmd_endhrt(); }
        |       INJ_TOK_SLEEP number { $$ = inj_cmd_sleep($2); }
        |       INJ_TOK_REPEAT number cmd_repeatable {
                        $$ = ($3 == NULL ? NULL : inj_cmd_repeat($3, $2));
                }
        ;

cmd_repeatable: defined_event {
                        $$ = ($1 == NULL ? NULL : inj_cmd_send($1));
                }
        |       INJ_TOK_RANDOMIZE '{' rand_problist '}' {
                        $$ = ($3 == NULL ? NULL : inj_cmd_rand($3));
                }
        ;

rand_problist:  rand_element
        |       rand_problist ',' rand_element {
                        $$ = ($1 == NULL || $3 == NULL) ?
                            NULL : inj_rand_add($1, $3);
                }
        ;

rand_element:   '{' defined_event ',' number '}' {
                        $$ = ($2 == NULL ? NULL : inj_rand_create($2, $4));
                }
        ;

defined_event:  INJ_TOK_IDENT {
                        inj_defn_t *ev;

                        if ((ev = inj_defn_lookup($1, MEMTYPE_EVENT)) ==
                            NULL) {
                                yyerror("unknown event \"%s\"\n", $1);
                                $$ = NULL;
                        } else
                                $$ = ev;
                }

number:         INJ_TOK_IMM {
                        u_longlong_t val;

                        if (inj_strtoull($1, 32, &val) < 0) {
                                yyerror("invalid number");
                                $$ = 0;
                        } else
                                $$ = (uint32_t)val;
                }

hrtime:         INJ_TOK_IMM INJ_TOK_IDENT {
                        longlong_t val;

                        if (inj_strtoll($1, 64, &val) < 0 ||
                            inj_strtime(&val, $2) < 0) {
                                yyerror("invalid time");
                                $$ = 0;
                        } else
                                $$ = val;
                }

%%

inj_list_t *
inj_program_read(const char *file)
{
        if (strcmp(file, "-") == 0) {
                yyin = stdin;
                yyinname = "stdin";
        } else {
                if ((yyin = fopen(file, "r")) == NULL)
                        die("failed to open %s", file);

                yyinname = strrchr(file, '/');
                if (yyinname != NULL)
                        yyinname++;
                else
                        yyinname = file;
        }

        yyreset();
        (void) yyparse();

        if (yyin != stdin)
                (void) fclose(yyin);

        if (yynerrors != 0) {
                die("parsing failed - %d error%s\n", yynerrors,
                    (yynerrors > 1 ? "s" : ""));
        }

        return (inj_cmds_get());
}