%{
    /* #define msg
    */
    #include "parser.ih"
%}

%token
    ARG_HEAD
    ARG_TAIL
    ASCII
    BREAK
    CHDIR
    CMD_HEAD
    CMD_TAIL
    CONTINUE
    C_BASE
    C_EXT
    C_PATH
    G_BASE
    G_EXT
    G_DEXT
    G_PATH
    ELEMENT
    ELSE
    EVAL
    EXEC
    EXECUTE
    EXISTS
    EXIT
    FGETS
    FIELDS
    FOR
    FPRINTF
    GETENV
    GETCH
    GETPID
    GETS
    IDENTIFIER
    IF
    INT
    LIST
    LISTFIND
    LISTLEN
    LISTUNION
    MAKELIST
    ECHO_TOKEN
    NUMBER
    PRINTF
    PUTENV
    RETURN
    STAT
    STRCHR
    STRING
    STRINGTYPE
    STRLEN
    STRLWR
    RESIZE
    STRUPR
    STRFIND
    STRFORMAT
    SUBSTR
    SYSTEM
    TRIM
    TRIMLEFT
    TRIMRIGHT
    VOID
    WHILE

%right
    '='
    AND_IS                                  /* binary-p_assignment */
    OR_IS
    XOR_IS
    SHL_IS
    SHR_IS

    DIV_IS                                  /* arithmetic p_assignment */
    MINUS_IS
    MUL_IS
    MOD_IS
    PLUS_IS

%right '?' ':'

%left OR

%left AND

%left '|'

%left '^'

%left '&'

%left EQUAL NOT_EQUAL

%left '<' '>' SMALLER_EQUAL GREATER_EQUAL OLDER YOUNGER

%left SHL SHR

%left '+' '-'

%left '*' '/' '%'

%right '!' INC DEC '~'

%left '['


%expect 1

       /* Grammar Rules */

%%

input:
    input
    def_var_or_fun
|
    def_var_or_fun
;

args:
    args
    comma
    err_expression
    {
        $$ = *p_multipleArgs(&$1, &$3);
    }
|
    err_expression
    {
        $$ = *p_firstArg(&$1);
    }
;
break_ok:
    {
        gp_breakOK++;
    }
;
break_stat:
    BREAK
    {
        $$ = *p_break();
    }
;
closebrace: 
    {
        gp_parse_error = err_closebrace_expected;
        symtab_pop();
    }                       /* '{' for matching */
    '}' 
;
closepar:   
    {
        gp_parse_error = err_closepar_expected; 
    }
    ')' 
;
comma:      
    {
        gp_parse_error = err_comma_expected; 
    }        
    ',' 
;
compound:
    openbrace
    statements
    closebrace
    {
        $$ = $2;
    }
;
condition:
    err_expression
|
    typed_condition
;
continue_stat:
    CONTINUE
    {
        $$ = *p_continue();
    }
;
_voidtype:
    VOID
    {
        gp_varType = 0;
    }
;

def_var_or_fun:
    typed_varlist
    semicol
    {
        gp_init = *p_catCode(&gp_init, &$1);
    }
|
    type_of_var
    funcdef
|
    _voidtype
    funcdef
;

enterid:
    IDENTIFIER
    {
        p_defineVar();    /* the first n variables of a function, up to the
                            end of the parameter list are the parameters. */
    }
;
enter_varid:
    enterid
    {
        $$ = *p_fetchVar();
    }
;

err_expression:
    {
        gp_parse_error = err_in_expression;
    }
    expression
    {
        $$ = $2;
    }
;
expr_code:
    err_expression
    {
        $$ = *p_expression(&$1);
    }
;
_p_casttype:
    INT
|
    LIST
|
    STRINGTYPE
;

_string:
    _string
    STRING
    {                                       /* catenate the new string */
        gp_stringbuf = rss_strcat(gp_stringbuf, util_string());
    }
|
    STRING
    {
        free(gp_stringbuf);                  /* free former string */
        gp_stringbuf = rss_strdup(util_string()); /* duplicate initial string */
    }
;

_func_or_var:
    function
    closepar
|
    IDENTIFIER
    {
        $$ = *p_fetchVar();
    }
;

_backtick:   
    {
        gp_parse_error = err_backtick_expected;
    }
    '`' 
;

expression:
        expression
        '='
        expression
        {
            $$ = *p_assign(&$1, &$3);
        }
    |
        expression
        '['
        expression
        ']'
        {
            $$ = *p_indexOp(&$1, &$3);
        }
    |
        expression
        MUL_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_multiply, "*=");
        }
    |
        expression
        DIV_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_divide, "/=");
        }
    |
        expression
        MOD_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_modulo, "%=");
        }
    |
        expression
        PLUS_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_addition, "+=");
        }
    |
        expression
        MINUS_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_subtract, "-=");
        }
    |
        expression
        AND_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_and, "&=");
        }
    |
        expression
        OR_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_or, "|=");
        }
    |
        expression
        XOR_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_xor, "^=");
        }
    |
        expression
        SHL_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_shl, "<<=");
        }
    |
        expression
        SHR_IS
        expression
        {
            $$ = *p_compoundAss(&$1, &$3, p_shr, ">>=");
        }
    |
        expression
        OR
        expression
        {
            $$ = *p_orBool(&$1, &$3);
        }
    |
        expression
        AND
        expression
        {
            $$ = *p_andBoolean(&$1, &$3);
        }
    |
        expression
        EQUAL
        expression
        {
            $$ = *p_equal(&$1, &$3);
        }
    |
        expression
        NOT_EQUAL
        expression
        {
            $$ = *p_unequal(&$1, &$3);
        }
    |
        expression
        '?'
        expression
        ':'
        expression
        {
            $$ = *p_ternary(&$1, &$3, &$5);
        }
    |
        expression
        '<'
        expression
        {
            $$ = *p_smaller(&$1, &$3);
        }
    |
        expression
        '>'
        expression
        {
            $$ = *p_greater(&$1, &$3);
        }
    |
        expression
        SMALLER_EQUAL
        expression
        {
            $$ = *p_smEqual(&$1, &$3);
        }
    |
        expression
        GREATER_EQUAL
        expression
        {
            $$ = *p_grEqual(&$1, &$3);
        }
    |
        expression
        '+'
        expression
        {
            $$ = *p_addition(&$1, &$3);
        }
    |
        expression
        '&'
        expression
        {
            $$ = *p_and(&$1, &$3);
        }
    |
        expression
        '|'
        expression
        {
            $$ = *p_or(&$1, &$3);
        }
    |
        expression
        '^'
        expression
        {
            $$ = *p_xor(&$1, &$3);
        }
    |
        expression
        SHL
        expression
        {
            $$ = *p_shl(&$1, &$3);
        }
    |
        expression
        SHR
        expression
        {
            $$ = *p_shr(&$1, &$3);
        }
    |
        expression
        '-'
        expression
        {
            $$ = *p_subtract(&$1, &$3);
        }
    |
        expression
        '*'
        expression
        {
            $$ = *p_multiply(&$1, &$3);
        }
    |
        expression
        YOUNGER
        expression
        {
            $$ = *p_young(&$1, &$3);
        }
    |
        expression
        OLDER
        expression
        {
            $$ = *p_old(&$1, &$3);
        }
    |
        expression
        '/'
        expression
        {
            $$ = *p_divide(&$1, &$3);
        }
    |
        expression
        '%'
        expression
        {
            $$ = *p_modulo(&$1, &$3);
        }
    |
        '-'
        expression          %prec '!'
        {
            $$ = *p_negate(&$2);
        }
    |
        INC
        expression
        {
            $$ = *p_incDec(pre_op, op_inc, &$2);
        }
    |
        expression
        INC
        {
            $$ = *p_incDec(post_op, op_inc, &$1);
        }
    |
        DEC
        expression
        {
            $$ = *p_incDec(pre_op, op_dec, &$2);
        }
    |
        expression
        DEC
        {
            $$ = *p_incDec(post_op, op_dec, &$1);
        }
    |
        '+'
        expression          %prec '!'
        {
            $$ = $2;
        }
    |
        '~'
        expression          %prec '!'
        {
            $$ = *p_not(&$2);
        }
    |
        '!'
        expression
        {
            $$ = *p_notBoolean(&$2);
        }
    |
        '('
        _p_casttype
        ')'
        expression         %prec '!'
        {
            $$ = *p_cast($2.type, &$4);
        }
    |
        _string
        {
            $$ = *p_stackFrame(e_str | e_const);
        }
    |
        NUMBER
        {
            $$ = *p_stackFrame(e_int | e_const);
        }
    |
        '('
        expression
        closepar
        {
            $$ = $2;
        }
    |
        _func_or_var
    |
        '`'
        expression
        _backtick
        {
            $$ = *p_oneArg(f_backtick, &$2);
        }
    ;
_for:
    FOR
    nesting
    {
        symtab_push();
    }
;

_expr_list:
    _expr_list
    ','
    expr_code
    {
        $$ = *p_catCode(&$1, &$3);
    }
|
    expr_code
;

_opt_init_expression:
    _expr_list
|
    typed_varlist
|
    zeroframe
;


_opt_cond_expression:
    err_expression
|
    {
        $$ = *p_stackFrame(e_int | e_const);
        $$.evalue = 1;
    }
;

_opt_inc_expression:
    _expr_list
|
    zeroframe
;

for_stat:
    _for
    openpar
    _opt_init_expression    /* $3: init */
    semicol
    _opt_cond_expression    /* $5: cond */
    semicol
    _opt_inc_expression     /* $7 inc   */
    closepar
    break_ok
    statement               /* $10 stmnt    */
    popdead
    {
        $$ = *p_for(&$3, &$5, &$7, &$10);
    }
;








    /*
        parameters are defined as local variables of the current function,
        while counting the number of parameters. 
        At the end of a function definition head its symtab nParams fields
        holds the number of its parameters, which are represented by the
        initial nParams elements of symtab's local variables array.
    */

_partype:
    type_of_var
    enterid
;

_pars:
    _pars
    comma
    _partype
|
    _partype
;

_opt_parlist:
    _pars
|
    /* empty */
;

_funvars:
    openpar
    _opt_parlist
    ')'
    openbrace
    {
        symtab_setFunParams(); /* the # variables so far are the parameters */
    }
;

_funid:
    IDENTIFIER
    {
        p_beginFunction();
    }
;


funcdef:
    _funid                          /* name of the function */
    _funvars                         /* returns init code */
    statements
    closebrace
    {
        p_endFunction(&$3);
    }
;
_zero_arg_funs:
    GETCH
|
    GETPID
|
    GETS
;

_one_arg_funs:
    ASCII
|
    EVAL
|
    EXISTS
|
    LISTLEN
|
    ECHO_TOKEN
|
    CMD_TAIL
|
    CMD_HEAD
|
    ARG_HEAD
|
    ARG_TAIL
|
    G_BASE
|
    G_PATH
|
    G_EXT
|
    G_DEXT
|
    PUTENV
|
    GETENV
|
    STRLEN
|
    STRUPR
|
    STRLWR
|
    TRIM
|
    TRIMLEFT
|
    TRIMRIGHT
;

_two_arg_funs:
    C_EXT                           /* string, string */
|
    C_BASE
|
    C_PATH
|
    ELEMENT                         /* int, list | int, string */
|
    FGETS                           /* list fgets(string, int) */
|
    FIELDS                          /* string, string */
|
    LISTFIND                        /* list, string */
|
    LISTUNION                       /* list, list | list, string */
|
    STRCHR                          /* string, string */
|
    STRFIND                         /* string, string */
|
    RESIZE                          /* string, int */
;

_optint_string:
    STAT
|
    CHDIR
|
    SYSTEM
;

_comma_expr:
    ','
    err_expression
    {
        $$ = $2;
    }
|
    zeroframe
;

_optint_special:
    EXEC                                /* optional int allowed */
|
    EXECUTE
;

_comma_arglist:
    ','
    args
    {
        $$ = $2;
    }
|
    zeroframe
;

_opt_arglist:
    args
|
    zeroframe
;

_funname:
    IDENTIFIER
    {
        $$.evalue = p_functionIdx();
    }
;

function:
    _zero_arg_funs                       /* getch() or gets() */
    openpar
    {
        $$ = *p_zeroArgs($1.type);
    }
|
    _one_arg_funs
    openpar
    err_expression
    {
        $$ = *p_oneArg($1.type, &$3);
    }
|
    _two_arg_funs
    openpar
    err_expression
    comma
    err_expression
    {
        $$ = *p_twoArgs($1.type, &$3, &$5);
    }
|
    SUBSTR                              /* three arg function */
    openpar
    err_expression
    comma
    err_expression
    comma
    err_expression
    {
        $$ = *p_threeArgs($1.type, &$3, &$5, &$7);
    }
|
    _optint_string                      /* CHDIR, SYSTEM, STAT */
    openpar
    err_expression                      /* int inserted if string */
    _comma_expr                         /* may be string if first == int */
    {
        $$ = *p_optIntString($1.type, &$3, &$4);
    }
|
    _optint_special                     /* EXEC, EXECUTE */
    openpar                             /* alternatives: */
    err_expression                      /* fun(int, string, ...) */
    _comma_arglist                      /* fun(string, ...)       */
    {
        $$ = *p_optIntSpecial($1.type, &$3, &$4);
    }
|
    PRINTF
    openpar
    args                                /* first may be anything */
    {
        $$ = *p_specials(f_printf, &$3);
    }
|
    FPRINTF
    openpar
    args                                /* argcount >= 2 required */
    {
        $$ = *p_fprintf($1.type, &$3);
    }
|
    STRFORMAT
    openpar
    args                                /* first may be anything */
    {
        $$ = *p_specials(f_strformat, &$3);
    }
|
    _funname
    openpar
    _opt_arglist
    {
        $$ = *p_callFunction($1.evalue, &$3);
    }
|
    p_makeList
;
_if:
    IF
    nesting
    {
        symtab_push();
    }
;

_else:
    ELSE
    statement
    {
        $$ = $2;
    }
|
    zeroframe
;

if_stat:
    _if
    openpar
    condition
    closepar
    statement
    popdead
    pushdead
    _else
    popdead
    {
        $$ = *p_if(&$3, &$5, &$8);
        symtab_pop();
    }
;







_p_makeList_expr:
    MAKELIST
    openpar
    err_expression
    {
        $$ = $3;
    }
;

_p_makeList_normal:
    {
        $$ = *p_stackFrame(e_int | e_const);
        $$.evalue = IS_FILE;
    }
;

_old_young:
    OLDER
|
    YOUNGER
;

_older_younger:
    {
        gp_parse_error = err_older_younger; 
    }
    _old_young
    {
        $$ = $2;
    }
;

p_makeList:
    _p_makeList_expr                  /* p_makeList(expr) */
    _p_makeList_normal                /* returns IS_FILE expression */
    {
        $$ = *p_makeList
             (
                 p_multipleArgs
                 (
                     p_firstArg(&$2),     /* IS_FILE is passed */
                     &$1                /* expression is passed */
                 ),
                 op_hlt                 /* not op_younger or op_older */
             );
    }
|
                                        /* p_makeList(expr, expr) */
    _p_makeList_expr
    comma
    err_expression
    {
        $$ = *p_makeList
             (
                 p_multipleArgs
                 (
                     p_firstArg(&$1),     /* fileattribute is passed */
                     &$3                /* expression is passed */
                 ),
                 op_hlt                 /* not op_younger or op_older */
             );
    }
|
    _p_makeList_expr                       /* p_makeList(expr, older, expr) */
    comma
    _older_younger
    comma
    err_expression
    _p_makeList_normal
    {
        $$ = *p_makeList
             (
                p_multipleArgs
                (
                    p_multipleArgs
                    (
                        p_firstArg(&$6),  /* IS_FILE   is passed */
                        &$1             /* 1st expression is passed */
                    ),
                    &$5                 /* 2nd expression is passed */
                 ),
                 $3.type                /* older/younger */
             );
    }
|
    _p_makeList_expr                 /* p_makeList(expr, expr, older, expr) */
    comma
    err_expression
    comma
    _older_younger
    comma
    err_expression
    {
        $$ = *p_makeList
             (
                p_multipleArgs
                (
                    p_multipleArgs
                    (
                        p_firstArg(&$1),  /* attribute is passed */
                        &$3             /* 2nd expression is passed */
                    ),
                    &$7                 /* 3rd expression is passed */
                 ),
                 $5.type                /* older/younger */
             );
    }
;
nesting:
    pushdead
    {
        gp_nestLevel++;
    }
;
ok:
    ';'
    {
        yyerrok;
    }
;
openbrace:
    {
        gp_parse_error = err_openbrace_expected;
    }    
    '{'                             /* } (for matching) */
    {
        symtab_push();
    }
;

openpar:
    {
        gp_parse_error = err_openpar_expected;
    }
    '('
;
popdead:
    {
        p_popDead();
    }
;
pushdead:
    {
        p_pushDead();                    /* set new dead-level */
    }
;
_return_tail:
    err_expression
|
    zeroframe
;

_leave:
    RETURN
    {
        msg("saw return");
    }
|
    EXIT
;

return_stat:
    _leave
    _return_tail
    {
        $$ = *p_return($1.type, &$2);
        msg("SAW return stmt");
    }
;


semicol:    
    {
        gp_parse_error = err_semicol_expected; 
    }      
    ';' 
;
_stm:
    compound
|
    ';'
    zeroframe
    {
        $$ = $2;
    }
|
    expr_code
    semicol
|
    while_stat
|
    if_stat
|
    for_stat
|
    return_stat
    semicol
|
    break_stat
    semicol
|
    continue_stat
    semicol
|
    error
    ok
;

statement:
    _stm
|
    typed_varlist
    semicol
;
statements:
    statements
    statement
    {
        $$ = *p_catStmnts(&$1, &$2);
    }
|
    zeroframe
;





typed_condition:
    type_of_var
    var_condition
    {
        $$ = $2;
    }
;
typed_varlist:
    type_of_var
    var_expr_list
    {
        $$ = $2;
    }
;

_varType:
        INT
    |
        STRINGTYPE
    |
        LIST
    ;

type_of_var:
    _varType
    {
        gp_parse_error = err_identifier_expected;
        gp_varType = $1.type;
    }
;
var_condition:
    enter_varid
    {
        p_generateCode(&$1, op_push_imm, 0);
        $$ = $1;
    }
|
    enter_varid
    '='
    expression
    {
        $$ = *p_assign(&$1, &$3);    /* explicit initialization */
    }        
;
var_expr:
    enterid
    zeroframe                           /* no explicit initialization */
|
    enter_varid
    '='
    expression
    {
        $$ = *p_expression(p_assign(&$1, &$3));    /* explicit initialization */
    }        
;

var_expr_list:
    var_expr_list
    comma
    var_expr
    {
        $$ = *p_catCode(&$1, &$3);    /* catenate variable    */
                                    /* initialization code  */
    }            
|
    var_expr
|
    error
    ok
    zeroframe                       /* Empty stmnt  */
    {
        $$ = $3;
    }
;

_while:
    WHILE
    nesting
;

while_stat:
    _while
    openpar
    condition
    closepar
    break_ok
    statement
    popdead
    {
        $$ = *p_while(&$3, &$6, 1);
    }
;
zeroframe:
    {
        $$ = *p_stackFrame(0);   /* by default initializes a variable to 0 */
    }
;

%%

int yywrap(void)
{
    return 1;
}
