/*
 * Copyright 2014 Jacek Caban for CodeWeavers
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

%{

#include "jscript.h"
#include "engine.h"
#include "parser.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(jscript);

%}

%lex-param { parser_ctx_t *ctx }
%parse-param { parser_ctx_t *ctx }
%pure-parser
%start CCExpr

%union {
    ccval_t ccval;
}

%token tEQ tEQEQ tNEQ tNEQEQ tLSHIFT tRSHIFT tRRSHIFT tOR tAND tLEQ tGEQ
%token <ccval> tCCValue

%type <ccval> CCUnaryExpression CCLogicalORExpression CCLogicalANDExpression
%type <ccval> CCBitwiseORExpression CCBitwiseXORExpression CCBitwiseANDExpression
%type <ccval> CCEqualityExpression CCRelationalExpression CCShiftExpression CCAdditiveExpression CCMultiplicativeExpression

%{

static int cc_parser_error(parser_ctx_t *ctx, const char *str)
{
    if(SUCCEEDED(ctx->hres)) {
        WARN("%s\n", str);
        ctx->hres = JS_E_SYNTAX;
    }

    return 0;
}

static int cc_parser_lex(void *lval, parser_ctx_t *ctx)
{
    int r;

    r = try_parse_ccval(ctx, lval);
    if(r)
        return r > 0 ? tCCValue : -1;

    switch(*ctx->ptr) {
    case '(':
    case ')':
    case '+':
    case '-':
    case '*':
    case '/':
    case '~':
    case '%':
    case '^':
        return *ctx->ptr++;
    case '=':
         if(*++ctx->ptr == '=') {
             if(*++ctx->ptr == '=') {
                 ctx->ptr++;
                 return tEQEQ;
             }
             return tEQ;
         }
         break;
    case '!':
        if(*++ctx->ptr == '=') {
            if(*++ctx->ptr == '=') {
                ctx->ptr++;
                return tNEQEQ;
            }
            return tNEQ;
        }
        return '!';
    case '<':
        switch(*++ctx->ptr) {
        case '<':
            ctx->ptr++;
            return tLSHIFT;
        case '=':
            ctx->ptr++;
            return tLEQ;
        default:
            return '<';
        }
    case '>':
        switch(*++ctx->ptr) {
        case '>':
            if(*++ctx->ptr == '>') {
                ctx->ptr++;
                return tRRSHIFT;
            }
            return tRSHIFT;
        case '=':
            ctx->ptr++;
            return tGEQ;
        default:
            return '>';
        }
    case '|':
        if(*++ctx->ptr == '|') {
            ctx->ptr++;
            return tOR;
        }
        return '|';
    case '&':
        if(*++ctx->ptr == '&') {
            ctx->ptr++;
            return tAND;
        }
        return '&';
    }

    WARN("Failed to interpret %s\n", debugstr_w(ctx->ptr));
    return -1;
}

%}

%%

/* FIXME: Implement missing expressions. */

CCExpr
    : CCUnaryExpression { ctx->ccval = $1; YYACCEPT; }

CCUnaryExpression
    : tCCValue                      { $$ = $1; }
    | '(' CCLogicalORExpression ')' { $$ = $2; }
    | '!' CCUnaryExpression         { $$ = ccval_bool(!get_ccbool($2)); };
    | '~' CCUnaryExpression         { FIXME("'~' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }
    | '+' CCUnaryExpression         { FIXME("'+' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }
    | '-' CCUnaryExpression         { FIXME("'-' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCLogicalORExpression
    : CCLogicalANDExpression        { $$ = $1; }
    | CCLogicalORExpression tOR CCLogicalANDExpression
                                    { FIXME("'||' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCLogicalANDExpression
    : CCBitwiseORExpression         { $$ = $1; }
    | CCBitwiseANDExpression tAND CCBitwiseORExpression
                                    { FIXME("'&&' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCBitwiseORExpression
    : CCBitwiseXORExpression        { $$ = $1; }
    | CCBitwiseORExpression '|' CCBitwiseXORExpression
                                    { FIXME("'|' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCBitwiseXORExpression
    : CCBitwiseANDExpression        { $$ = $1; }
    | CCBitwiseXORExpression '^' CCBitwiseANDExpression
                                    { FIXME("'^' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCBitwiseANDExpression
    : CCEqualityExpression          { $$ = $1; }
    | CCBitwiseANDExpression '&' CCEqualityExpression
                                    { FIXME("'&' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCEqualityExpression
    : CCRelationalExpression        { $$ = $1; }
    | CCEqualityExpression tEQ CCRelationalExpression
                                    { $$ = ccval_bool(get_ccnum($1) == get_ccnum($3)); }
    | CCEqualityExpression tNEQ CCRelationalExpression
                                    { $$ = ccval_bool(get_ccnum($1) != get_ccnum($3)); }
    | CCEqualityExpression tEQEQ CCRelationalExpression
                                    { FIXME("'===' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }
    | CCEqualityExpression tNEQEQ CCRelationalExpression
                                    { FIXME("'!==' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCRelationalExpression
    : CCShiftExpression             { $$ = $1; }
    | CCRelationalExpression '<' CCShiftExpression
                                    { $$ = ccval_bool(get_ccnum($1) < get_ccnum($3)); }
    | CCRelationalExpression tLEQ CCShiftExpression
                                    { $$ = ccval_bool(get_ccnum($1) <= get_ccnum($3)); }
    | CCRelationalExpression '>' CCShiftExpression
                                    { $$ = ccval_bool(get_ccnum($1) > get_ccnum($3)); }
    | CCRelationalExpression tGEQ CCShiftExpression
                                    { $$ = ccval_bool(get_ccnum($1) >= get_ccnum($3)); }

CCShiftExpression
    : CCAdditiveExpression          { $$ = $1; }
    | CCShiftExpression tLSHIFT CCAdditiveExpression
                                    { FIXME("'<<' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }
    | CCShiftExpression tRSHIFT CCAdditiveExpression
                                    { FIXME("'>>' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }
    | CCShiftExpression tRRSHIFT CCAdditiveExpression
                                    { FIXME("'>>>' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

CCAdditiveExpression
    : CCMultiplicativeExpression    { $$ = $1; }
    | CCAdditiveExpression '+' CCMultiplicativeExpression
                                    { $$ = ccval_num(get_ccnum($1) + get_ccnum($3)); }
    | CCAdditiveExpression '-' CCMultiplicativeExpression
                                    { $$ = ccval_num(get_ccnum($1) - get_ccnum($3)); }

CCMultiplicativeExpression
    : CCUnaryExpression             { $$ = $1; }
    | CCMultiplicativeExpression '*' CCUnaryExpression
                                    { $$ = ccval_num(get_ccnum($1) * get_ccnum($3)); }
    | CCMultiplicativeExpression '/' CCUnaryExpression
                                    { $$ = ccval_num(get_ccnum($1) / get_ccnum($3)); }
    | CCMultiplicativeExpression '%' CCUnaryExpression
                                    { FIXME("'%%' expression not implemented\n"); ctx->hres = E_NOTIMPL; YYABORT; }

%%

BOOL parse_cc_expr(parser_ctx_t *ctx)
{
    ctx->hres = S_OK;
    cc_parser_parse(ctx);
    return SUCCEEDED(ctx->hres);
}
