%{

/*
 *  This file is part of the KDE libraries
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
 *  Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
 *  Copyright (C) 2019 froglogic GmbH (contact@froglogic.com)
 *
 *  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 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 Street, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "value.h"
#include "object.h"
#include "types.h"
#include "interpreter.h"
#include "nodes.h"
#include "makenodes.h"
#include "lexer.h"
#include "internal.h"

// Not sure why, but yacc doesn't add this define along with the others.
#define yylloc kjsyylloc

/* default values for bison */
#define YYDEBUG 0 // Set to 1 to debug a parse error.
#define kjsyydebug 0 // Set to 1 to debug a parse error.
#if !PLATFORM(DARWIN)
    // avoid triggering warnings in older bison
#define YYERROR_VERBOSE
#endif

extern int kjsyylex();
int kjsyyerror(const char *);
static bool allowAutomaticSemicolon();

#define AUTO_SEMICOLON do { if (!allowAutomaticSemicolon()) YYABORT; } while (0)
#define DBG(l, s, e) (l)->setLoc((s).first_line, (e).last_line)

#ifndef __GNUC__
#   define  __attribute__(x)
#endif

using namespace KJS;

%}

%union {
  int                 ival;
  double              dval;
  UString             *ustr;
  Identifier          *ident;
  Node                *node;
  StatementNode       *stat;
  ParameterNode       *param;
  FunctionBodyNode    *body;
  FuncDeclNode        *func;
  FuncExprNode        *funcExpr;
  ProgramNode         *prog;
  AssignExprNode      *init;
  SourceElementsNode  *srcs;
  ArgumentsNode       *args;
  ArgumentListNode    *alist;
  VarDeclNode         *decl;
  VarDeclListNode     *vlist;
  CaseBlockNode       *cblk;
  ClauseListNode      *clist;
  CaseClauseNode      *ccl;
  ElementNode         *elm;
  Operator            op;
  PropertyListNode   *plist;
  PropertyNode       *pnode;
  PropertyNameNode   *pname;
  PackageNameNode     *pkgn;
}

%start Program

/* literals */
%token NULLTOKEN TRUETOKEN FALSETOKEN

/* keywords */
%token BREAK CASE DEFAULT FOR NEW VAR CONSTTOKEN CONTINUE
%token FUNCTION RETURN VOIDTOKEN DELETETOKEN
%token IF THISTOKEN DO WHILE INTOKEN INSTANCEOF TYPEOF
%token SWITCH WITH RESERVED
%token THROW TRY CATCH FINALLY
%token DEBUGGER IMPORT

/* give an if without an else higher precedence than an else to resolve the ambiguity */
%nonassoc IF_WITHOUT_ELSE
%nonassoc ELSE

/* punctuators */
%token EQEQ NE                     /* == and != */
%token STREQ STRNEQ                /* === and !== */
%token LE GE                       /* < and > */
%token OR AND                      /* || and && */
%token PLUSPLUS MINUSMINUS         /* ++ and --  */
%token LSHIFT                      /* << */
%token RSHIFT URSHIFT              /* >> and >>> */
%token T_EXP                       /* ** */
%token PLUSEQUAL MINUSEQUAL        /* += and -= */
%token MULTEQUAL DIVEQUAL          /* *= and /= */
%token EXPEQUAL                    /* **= */
%token LSHIFTEQUAL                 /* <<= */
%token RSHIFTEQUAL URSHIFTEQUAL    /* >>= and >>>= */
%token ANDEQUAL MODEQUAL           /* &= and %= */
%token XOREQUAL OREQUAL            /* ^= and |= */

/* terminal types */
%token <dval> NUMBER
%token <ustr> STRING
%token <ident> IDENT

/* automatically inserted semicolon */
%token AUTOPLUSPLUS AUTOMINUSMINUS

/* non-terminal types */
%type <node>  Literal ArrayLiteral

%type <node>  PrimaryExpr PrimaryExprNoBrace
%type <node>  MemberExpr MemberExprNoBF /* BF => brace or function */
%type <node>  NewExpr NewExprNoBF
%type <node>  CallExpr CallExprNoBF
%type <node>  LeftHandSideExpr LeftHandSideExprNoBF
%type <node>  UpdateExpr UpdateExprNoBF UpdateExprCommon
%type <node>  UnaryExpr UnaryExprNoBF UnaryExprCommon
%type <node>  ExponentiationExpr ExponentiationExprNoBF
%type <node>  MultiplicativeExpr MultiplicativeExprNoBF
%type <node>  AdditiveExpr AdditiveExprNoBF
%type <node>  ShiftExpr ShiftExprNoBF
%type <node>  RelationalExpr RelationalExprNoIn RelationalExprNoBF
%type <node>  EqualityExpr EqualityExprNoIn EqualityExprNoBF
%type <node>  BitwiseANDExpr BitwiseANDExprNoIn BitwiseANDExprNoBF
%type <node>  BitwiseXORExpr BitwiseXORExprNoIn BitwiseXORExprNoBF
%type <node>  BitwiseORExpr BitwiseORExprNoIn BitwiseORExprNoBF
%type <node>  LogicalANDExpr LogicalANDExprNoIn LogicalANDExprNoBF
%type <node>  LogicalORExpr LogicalORExprNoIn LogicalORExprNoBF
%type <node>  ConditionalExpr ConditionalExprNoIn ConditionalExprNoBF
%type <node>  AssignmentExpr AssignmentExprNoIn AssignmentExprNoBF
%type <node>  Expr ExprNoIn ExprNoBF
%type <node>  ExprOpt ExprNoInOpt

%type <stat>  Statement Block
%type <stat>  VariableStatement ConstStatement EmptyStatement ExprStatement
%type <stat>  IfStatement IterationStatement ContinueStatement
%type <stat>  BreakStatement ReturnStatement WithStatement
%type <stat>  SwitchStatement LabelledStatement
%type <stat>  ThrowStatement TryStatement
%type <stat>  DebuggerStatement ImportStatement
%type <stat>  SourceElement

%type <init>  Initializer InitializerNoIn
%type <func>  FunctionDeclaration
%type <funcExpr>  FunctionExpr
%type <body>  FunctionBody
%type <srcs>  SourceElements
%type <param> FormalParameterList
%type <op>    AssignmentOperator
%type <args>  Arguments
%type <alist> ArgumentList
%type <vlist> VariableDeclarationList VariableDeclarationListNoIn ConstDeclarationList
%type <decl>  VariableDeclaration VariableDeclarationNoIn ConstDeclaration
%type <cblk>  CaseBlock
%type <ccl>   CaseClause DefaultClause
%type <clist> CaseClauses  CaseClausesOpt
%type <ival>  Elision ElisionOpt
%type <elm>   ElementList
%type <pname> PropertyName
%type <pnode> Property
%type <plist> PropertyList
%type <pkgn>  PackageName
%type <ident> Keywords
%type <ident> IdentifierName
%%

Keywords:
    BREAK                               { $$ = new Identifier("break"); }
  | CASE                                { $$ = new Identifier("case"); }
  | DEFAULT                             { $$ = new Identifier("default"); }
  | FOR                                 { $$ = new Identifier("for"); }
  | NEW                                 { $$ = new Identifier("new"); }
  | VAR                                 { $$ = new Identifier("var"); }
  | CONSTTOKEN                          { $$ = new Identifier("const"); }
  | CONTINUE                            { $$ = new Identifier("continue"); }
  | FUNCTION                            { $$ = new Identifier("function"); }
  | RETURN                              { $$ = new Identifier("return"); }
  | VOIDTOKEN                           { $$ = new Identifier("void"); }
  | DELETETOKEN                         { $$ = new Identifier("delete"); }
  | IF                                  { $$ = new Identifier("if"); }
  | THISTOKEN                           { $$ = new Identifier("this"); }
  | DO                                  { $$ = new Identifier("do"); }
  | WHILE                               { $$ = new Identifier("while"); }
  | INTOKEN                             { $$ = new Identifier("in"); }
  | INSTANCEOF                          { $$ = new Identifier("instanceof"); }
  | TYPEOF                              { $$ = new Identifier("typeof"); }
  | SWITCH                              { $$ = new Identifier("switch"); }
  | WITH                                { $$ = new Identifier("with"); }
  | THROW                               { $$ = new Identifier("throw"); }
  | TRY                                 { $$ = new Identifier("try"); }
  | CATCH                               { $$ = new Identifier("catch"); }
  | FINALLY                             { $$ = new Identifier("finally"); }
  | DEBUGGER                            { $$ = new Identifier("debugger"); }
  | IMPORT                              { $$ = new Identifier("import"); }
  | NULLTOKEN                           { $$ = new Identifier("null"); }
  | TRUETOKEN                           { $$ = new Identifier("true"); }
  | FALSETOKEN                          { $$ = new Identifier("false"); }
  | ELSE                                { $$ = new Identifier("else"); }
;

IdentifierName:
    IDENT                               { $$ = $1; }
  | Keywords                            { $$ = $1; }
;

Literal:
    NULLTOKEN                           { $$ = new NullNode(); }
  | TRUETOKEN                           { $$ = new BooleanNode(true); }
  | FALSETOKEN                          { $$ = new BooleanNode(false); }
  | NUMBER                              { $$ = new NumberNode($1); }
  | STRING                              { $$ = new StringNode($1); }
  | '/' /* regexp */                    {
                                            Lexer& l = lexer();
                                            if (!l.scanRegExp())
                                                YYABORT;
                                            $$ = new RegExpNode(l.pattern(), l.flags());
                                        }
  | DIVEQUAL /* regexp with /= */       {
                                            Lexer& l = lexer();
                                            if (!l.scanRegExp())
                                                YYABORT;
                                            $$ = new RegExpNode("=" + l.pattern(), l.flags());
                                        }
;

PropertyName:
    IdentifierName                      { $$ = new PropertyNameNode(*$1); }
  | STRING                              { $$ = new PropertyNameNode(Identifier(*$1)); }
  | NUMBER                              { $$ = new PropertyNameNode(Identifier(UString::from($1))); }
;

Property:
    PropertyName ':' AssignmentExpr     { $$ = new PropertyNode($1, $3, PropertyNode::Constant); }
  | IDENT IdentifierName '(' ')' {inFuncExpr();} FunctionBody  {
          if (!makeGetterOrSetterPropertyNode($$, *$1, *$2, nullptr, $6))
            YYABORT;
        }
  | IDENT IdentifierName '(' FormalParameterList ')' {inFuncExpr();} FunctionBody {
          if (!makeGetterOrSetterPropertyNode($$, *$1, *$2, $4, $7))
            YYABORT;
        }
;

PropertyList:
    Property                            { $$ = new PropertyListNode($1); }
  | PropertyList ',' Property           { $$ = new PropertyListNode($3, $1); }
;

PrimaryExpr:
    PrimaryExprNoBrace
  | '{' '}'                             { $$ = new ObjectLiteralNode(); }
  | '{' PropertyList '}'                { $$ = new ObjectLiteralNode($2); }
  /* allow extra comma, see http://bugs.webkit.org/show_bug.cgi?id=5939 */
  | '{' PropertyList ',' '}'            { $$ = new ObjectLiteralNode($2); }
;

PrimaryExprNoBrace:
    THISTOKEN                           { $$ = new ThisNode(); }
  | Literal
  | ArrayLiteral
  | IDENT                               { $$ = new VarAccessNode(*$1); }
  | '(' Expr ')'                        { $$ = makeGroupNode($2); }

;

ArrayLiteral:
    '[' ElisionOpt ']'                  { $$ = new ArrayNode($2); }
  | '[' ElementList ']'                 { $$ = new ArrayNode($2); }
  | '[' ElementList ',' ElisionOpt ']'  { $$ = new ArrayNode($4, $2); }
;

ElementList:
    ElisionOpt AssignmentExpr           { $$ = new ElementNode($1, $2); }
  | ElementList ',' ElisionOpt AssignmentExpr
                                        { $$ = new ElementNode($1, $3, $4); }
;

ElisionOpt:
    /* nothing */                       { $$ = nullptr; }
  | Elision
;

Elision:
    ','                                 { $$ = 1; }
  | Elision ','                         { $$ = $1 + 1; }
;

MemberExpr:
    PrimaryExpr
  | FunctionExpr                        { $$ = $1; }
  | MemberExpr '[' Expr ']'             { $$ = new BracketAccessorNode($1, $3); }
  | MemberExpr '.' IdentifierName       { $$ = new DotAccessorNode($1, *$3); }
  | NEW MemberExpr Arguments            { $$ = new NewExprNode($2, $3); }
;

MemberExprNoBF:
    PrimaryExprNoBrace
  | MemberExprNoBF '[' Expr ']'         { $$ = new BracketAccessorNode($1, $3); }
  | MemberExprNoBF '.' IdentifierName   { $$ = new DotAccessorNode($1, *$3); }
  | NEW MemberExpr Arguments            { $$ = new NewExprNode($2, $3); }
;

NewExpr:
    MemberExpr
  | NEW NewExpr                         { $$ = new NewExprNode($2); }
;

NewExprNoBF:
    MemberExprNoBF
  | NEW NewExpr                         { $$ = new NewExprNode($2); }
;

CallExpr:
    MemberExpr Arguments                { $$ = makeFunctionCallNode($1, $2); }
  | CallExpr Arguments                  { $$ = makeFunctionCallNode($1, $2); }
  | CallExpr '[' Expr ']'               { $$ = new BracketAccessorNode($1, $3); }
  | CallExpr '.' IdentifierName         { $$ = new DotAccessorNode($1, *$3); }
;

CallExprNoBF:
    MemberExprNoBF Arguments            { $$ = makeFunctionCallNode($1, $2); }
  | CallExprNoBF Arguments              { $$ = makeFunctionCallNode($1, $2); }
  | CallExprNoBF '[' Expr ']'           { $$ = new BracketAccessorNode($1, $3); }
  | CallExprNoBF '.' IdentifierName     { $$ = new DotAccessorNode($1, *$3); }
;

Arguments:
    '(' ')'                             { $$ = new ArgumentsNode(); }
  | '(' ArgumentList ')'                { $$ = new ArgumentsNode($2); }
;

ArgumentList:
    AssignmentExpr                      { $$ = new ArgumentListNode($1); }
  | ArgumentList ',' AssignmentExpr     { $$ = new ArgumentListNode($1, $3); }
;

LeftHandSideExpr:
    NewExpr
  | CallExpr
;

LeftHandSideExprNoBF:
    NewExprNoBF
  | CallExprNoBF
;

UpdateExprCommon:
    PLUSPLUS UnaryExpr                  { $$ = makePrefixNode($2, OpPlusPlus); }
  | AUTOPLUSPLUS UnaryExpr              { $$ = makePrefixNode($2, OpPlusPlus); }
  | MINUSMINUS UnaryExpr                { $$ = makePrefixNode($2, OpMinusMinus); }
  | AUTOMINUSMINUS UnaryExpr            { $$ = makePrefixNode($2, OpMinusMinus); }
;

UpdateExpr:
    LeftHandSideExpr
  | LeftHandSideExpr PLUSPLUS           { $$ = makePostfixNode($1, OpPlusPlus); }
  | LeftHandSideExpr MINUSMINUS         { $$ = makePostfixNode($1, OpMinusMinus); }
  | UpdateExprCommon
;

UpdateExprNoBF:
    LeftHandSideExprNoBF
  | LeftHandSideExprNoBF PLUSPLUS       { $$ = makePostfixNode($1, OpPlusPlus); }
  | LeftHandSideExprNoBF MINUSMINUS     { $$ = makePostfixNode($1, OpMinusMinus); }
  | UpdateExprCommon
;

UnaryExprCommon:
    DELETETOKEN UnaryExpr               { $$ = makeDeleteNode($2); }
  | VOIDTOKEN UnaryExpr                 { $$ = new VoidNode($2); }
  | TYPEOF UnaryExpr                    { $$ = makeTypeOfNode($2); }
  | '+' UnaryExpr                       { $$ = makeUnaryPlusNode($2); }
  | '-' UnaryExpr                       { $$ = makeNegateNode($2); }
  | '~' UnaryExpr                       { $$ = makeBitwiseNotNode($2); }
  | '!' UnaryExpr                       { $$ = makeLogicalNotNode($2); }

UnaryExpr:
    UpdateExpr
  | UnaryExprCommon
;

UnaryExprNoBF:
    UpdateExprNoBF
  | UnaryExprCommon
;

ExponentiationExpr:
    UnaryExpr
  | UpdateExpr T_EXP ExponentiationExpr       { $$ = makeMultNode($1, $3, OpExp); }
;

ExponentiationExprNoBF:
    UnaryExprNoBF
  | UpdateExprNoBF T_EXP ExponentiationExpr   { $$ = makeMultNode($1, $3, OpExp); }
;

MultiplicativeExpr:
    ExponentiationExpr
  | MultiplicativeExpr '*' ExponentiationExpr  { $$ = makeMultNode($1, $3, OpMult); }
  | MultiplicativeExpr '/' ExponentiationExpr  { $$ = makeMultNode($1, $3, OpDiv); }
  | MultiplicativeExpr '%' ExponentiationExpr  { $$ = makeMultNode($1, $3, OpMod); }
;

MultiplicativeExprNoBF:
    ExponentiationExprNoBF
  | MultiplicativeExprNoBF '*' ExponentiationExpr
                                        { $$ = makeMultNode($1, $3, OpMult); }
  | MultiplicativeExprNoBF '/' ExponentiationExpr
                                        { $$ = makeMultNode($1, $3, OpDiv); }
  | MultiplicativeExprNoBF '%' ExponentiationExpr
                                        { $$ = makeMultNode($1, $3, OpMod); }
;

AdditiveExpr:
    MultiplicativeExpr
  | AdditiveExpr '+' MultiplicativeExpr { $$ = makeAddNode($1, $3, OpPlus); }
  | AdditiveExpr '-' MultiplicativeExpr { $$ = makeAddNode($1, $3, OpMinus); }
;

AdditiveExprNoBF:
    MultiplicativeExprNoBF
  | AdditiveExprNoBF '+' MultiplicativeExpr
                                        { $$ = makeAddNode($1, $3, OpPlus); }
  | AdditiveExprNoBF '-' MultiplicativeExpr
                                        { $$ = makeAddNode($1, $3, OpMinus); }
;

ShiftExpr:
    AdditiveExpr
  | ShiftExpr LSHIFT AdditiveExpr       { $$ = makeShiftNode($1, $3, OpLShift); }
  | ShiftExpr RSHIFT AdditiveExpr       { $$ = makeShiftNode($1, $3, OpRShift); }
  | ShiftExpr URSHIFT AdditiveExpr      { $$ = makeShiftNode($1, $3, OpURShift); }
;

ShiftExprNoBF:
    AdditiveExprNoBF
  | ShiftExprNoBF LSHIFT AdditiveExpr   { $$ = makeShiftNode($1, $3, OpLShift); }
  | ShiftExprNoBF RSHIFT AdditiveExpr   { $$ = makeShiftNode($1, $3, OpRShift); }
  | ShiftExprNoBF URSHIFT AdditiveExpr  { $$ = makeShiftNode($1, $3, OpURShift); }
;

RelationalExpr:
    ShiftExpr
  | RelationalExpr '<' ShiftExpr        { $$ = makeRelationalNode($1, OpLess, $3); }
  | RelationalExpr '>' ShiftExpr        { $$ = makeRelationalNode($1, OpGreater, $3); }
  | RelationalExpr LE ShiftExpr         { $$ = makeRelationalNode($1, OpLessEq, $3); }
  | RelationalExpr GE ShiftExpr         { $$ = makeRelationalNode($1, OpGreaterEq, $3); }
  | RelationalExpr INSTANCEOF ShiftExpr { $$ = makeRelationalNode($1, OpInstanceOf, $3); }
  | RelationalExpr INTOKEN ShiftExpr         { $$ = makeRelationalNode($1, OpIn, $3); }
;

RelationalExprNoIn:
    ShiftExpr
  | RelationalExprNoIn '<' ShiftExpr    { $$ = makeRelationalNode($1, OpLess, $3); }
  | RelationalExprNoIn '>' ShiftExpr    { $$ = makeRelationalNode($1, OpGreater, $3); }
  | RelationalExprNoIn LE ShiftExpr     { $$ = makeRelationalNode($1, OpLessEq, $3); }
  | RelationalExprNoIn GE ShiftExpr     { $$ = makeRelationalNode($1, OpGreaterEq, $3); }
  | RelationalExprNoIn INSTANCEOF ShiftExpr
                                        { $$ = makeRelationalNode($1, OpInstanceOf, $3); }
;

RelationalExprNoBF:
    ShiftExprNoBF
  | RelationalExprNoBF '<' ShiftExpr    { $$ = makeRelationalNode($1, OpLess, $3); }
  | RelationalExprNoBF '>' ShiftExpr    { $$ = makeRelationalNode($1, OpGreater, $3); }
  | RelationalExprNoBF LE ShiftExpr     { $$ = makeRelationalNode($1, OpLessEq, $3); }
  | RelationalExprNoBF GE ShiftExpr     { $$ = makeRelationalNode($1, OpGreaterEq, $3); }
  | RelationalExprNoBF INSTANCEOF ShiftExpr
                                        { $$ = makeRelationalNode($1, OpInstanceOf, $3); }
  | RelationalExprNoBF INTOKEN ShiftExpr     { $$ = makeRelationalNode($1, OpIn, $3); }
;

EqualityExpr:
    RelationalExpr
  | EqualityExpr EQEQ RelationalExpr    { $$ = makeEqualNode($1, OpEqEq, $3); }
  | EqualityExpr NE RelationalExpr      { $$ = makeEqualNode($1, OpNotEq, $3); }
  | EqualityExpr STREQ RelationalExpr   { $$ = makeEqualNode($1, OpStrEq, $3); }
  | EqualityExpr STRNEQ RelationalExpr  { $$ = makeEqualNode($1, OpStrNEq, $3);}
;

EqualityExprNoIn:
    RelationalExprNoIn
  | EqualityExprNoIn EQEQ RelationalExprNoIn
                                        { $$ = makeEqualNode($1, OpEqEq, $3); }
  | EqualityExprNoIn NE RelationalExprNoIn
                                        { $$ = makeEqualNode($1, OpNotEq, $3); }
  | EqualityExprNoIn STREQ RelationalExprNoIn
                                        { $$ = makeEqualNode($1, OpStrEq, $3); }
  | EqualityExprNoIn STRNEQ RelationalExprNoIn
                                        { $$ = makeEqualNode($1, OpStrNEq, $3);}
;

EqualityExprNoBF:
    RelationalExprNoBF
  | EqualityExprNoBF EQEQ RelationalExpr
                                        { $$ = makeEqualNode($1, OpEqEq, $3); }
  | EqualityExprNoBF NE RelationalExpr  { $$ = makeEqualNode($1, OpNotEq, $3); }
  | EqualityExprNoBF STREQ RelationalExpr
                                        { $$ = makeEqualNode($1, OpStrEq, $3); }
  | EqualityExprNoBF STRNEQ RelationalExpr
                                        { $$ = makeEqualNode($1, OpStrNEq, $3);}
;

BitwiseANDExpr:
    EqualityExpr
  | BitwiseANDExpr '&' EqualityExpr     { $$ = makeBitOperNode($1, OpBitAnd, $3); }
;

BitwiseANDExprNoIn:
    EqualityExprNoIn
  | BitwiseANDExprNoIn '&' EqualityExprNoIn
                                        { $$ = makeBitOperNode($1, OpBitAnd, $3); }
;

BitwiseANDExprNoBF:
    EqualityExprNoBF
  | BitwiseANDExprNoBF '&' EqualityExpr { $$ = makeBitOperNode($1, OpBitAnd, $3); }
;

BitwiseXORExpr:
    BitwiseANDExpr
  | BitwiseXORExpr '^' BitwiseANDExpr   { $$ = makeBitOperNode($1, OpBitXOr, $3); }
;

BitwiseXORExprNoIn:
    BitwiseANDExprNoIn
  | BitwiseXORExprNoIn '^' BitwiseANDExprNoIn
                                        { $$ = makeBitOperNode($1, OpBitXOr, $3); }
;

BitwiseXORExprNoBF:
    BitwiseANDExprNoBF
  | BitwiseXORExprNoBF '^' BitwiseANDExpr
                                        { $$ = makeBitOperNode($1, OpBitXOr, $3); }
;

BitwiseORExpr:
    BitwiseXORExpr
  | BitwiseORExpr '|' BitwiseXORExpr    { $$ = makeBitOperNode($1, OpBitOr, $3); }
;

BitwiseORExprNoIn:
    BitwiseXORExprNoIn
  | BitwiseORExprNoIn '|' BitwiseXORExprNoIn
                                        { $$ = makeBitOperNode($1, OpBitOr, $3); }
;

BitwiseORExprNoBF:
    BitwiseXORExprNoBF
  | BitwiseORExprNoBF '|' BitwiseXORExpr
                                        { $$ = makeBitOperNode($1, OpBitOr, $3); }
;

LogicalANDExpr:
    BitwiseORExpr
  | LogicalANDExpr AND BitwiseORExpr    { $$ = makeBinaryLogicalNode($1, OpAnd, $3); }
;

LogicalANDExprNoIn:
    BitwiseORExprNoIn
  | LogicalANDExprNoIn AND BitwiseORExprNoIn
                                        { $$ = makeBinaryLogicalNode($1, OpAnd, $3); }
;

LogicalANDExprNoBF:
    BitwiseORExprNoBF
  | LogicalANDExprNoBF AND BitwiseORExpr
                                        { $$ = makeBinaryLogicalNode($1, OpAnd, $3); }
;

LogicalORExpr:
    LogicalANDExpr
  | LogicalORExpr OR LogicalANDExpr     { $$ = makeBinaryLogicalNode($1, OpOr, $3); }
;

LogicalORExprNoIn:
    LogicalANDExprNoIn
  | LogicalORExprNoIn OR LogicalANDExprNoIn
                                        { $$ = makeBinaryLogicalNode($1, OpOr, $3); }
;

LogicalORExprNoBF:
    LogicalANDExprNoBF
  | LogicalORExprNoBF OR LogicalANDExpr { $$ = makeBinaryLogicalNode($1, OpOr, $3); }
;

ConditionalExpr:
    LogicalORExpr
  | LogicalORExpr '?' AssignmentExpr ':' AssignmentExpr
                                        { $$ = makeConditionalNode($1, $3, $5); }
;

ConditionalExprNoIn:
    LogicalORExprNoIn
  | LogicalORExprNoIn '?' AssignmentExprNoIn ':' AssignmentExprNoIn
                                        { $$ = makeConditionalNode($1, $3, $5); }
;

ConditionalExprNoBF:
    LogicalORExprNoBF
  | LogicalORExprNoBF '?' AssignmentExpr ':' AssignmentExpr
                                        { $$ = makeConditionalNode($1, $3, $5); }
;

AssignmentExpr:
    ConditionalExpr
  | LeftHandSideExpr AssignmentOperator AssignmentExpr
                                        { $$ = makeAssignNode($1, $2, $3); }
;

AssignmentExprNoIn:
    ConditionalExprNoIn
  | LeftHandSideExpr AssignmentOperator AssignmentExprNoIn
                                        { $$ = makeAssignNode($1, $2, $3); }
;

AssignmentExprNoBF:
    ConditionalExprNoBF
  | LeftHandSideExprNoBF AssignmentOperator AssignmentExpr
                                        { $$ = makeAssignNode($1, $2, $3); }
;

AssignmentOperator:
    '='                                 { $$ = OpEqual; }
  | PLUSEQUAL                           { $$ = OpPlusEq; }
  | MINUSEQUAL                          { $$ = OpMinusEq; }
  | MULTEQUAL                           { $$ = OpMultEq; }
  | DIVEQUAL                            { $$ = OpDivEq; }
  | EXPEQUAL                            { $$ = OpExpEq; }
  | LSHIFTEQUAL                         { $$ = OpLShift; }
  | RSHIFTEQUAL                         { $$ = OpRShift; }
  | URSHIFTEQUAL                        { $$ = OpURShift; }
  | ANDEQUAL                            { $$ = OpAndEq; }
  | XOREQUAL                            { $$ = OpXOrEq; }
  | OREQUAL                             { $$ = OpOrEq; }
  | MODEQUAL                            { $$ = OpModEq; }
;

Expr:
    AssignmentExpr
  | Expr ',' AssignmentExpr             { $$ = new CommaNode($1, $3); }
;

ExprNoIn:
    AssignmentExprNoIn
  | ExprNoIn ',' AssignmentExprNoIn     { $$ = new CommaNode($1, $3); }
;

ExprNoBF:
    AssignmentExprNoBF
  | ExprNoBF ',' AssignmentExpr         { $$ = new CommaNode($1, $3); }
;

Statement:
    Block
  | VariableStatement
  | ConstStatement
  | EmptyStatement
  | ExprStatement
  | IfStatement
  | IterationStatement
  | ContinueStatement
  | BreakStatement
  | ReturnStatement
  | WithStatement
  | SwitchStatement
  | LabelledStatement
  | ThrowStatement
  | TryStatement
  | DebuggerStatement
  | ImportStatement
;

Block:
    '{' '}'                             { $$ = new BlockNode(nullptr); DBG($$, @2, @2); }
  | '{' SourceElements '}'              { $$ = new BlockNode($2); DBG($$, @3, @3); }
;

VariableStatement:
    VAR VariableDeclarationList ';'     { $$ = new VarStatementNode($2); DBG($$, @1, @3); }
  | VAR VariableDeclarationList error   { $$ = new VarStatementNode($2); DBG($$, @1, @2); AUTO_SEMICOLON; }
;

VariableDeclarationList:
    VariableDeclaration                 { $$ = new VarDeclListNode($1); }
  | VariableDeclarationList ',' VariableDeclaration
                                        { $$ = new VarDeclListNode($1, $3); }
;

VariableDeclarationListNoIn:
    VariableDeclarationNoIn             { $$ = new VarDeclListNode($1); }
  | VariableDeclarationListNoIn ',' VariableDeclarationNoIn
                                        { $$ = new VarDeclListNode($1, $3); }
;

VariableDeclaration:
    IDENT                               { $$ = new VarDeclNode(*$1, nullptr, VarDeclNode::Variable); }
  | IDENT Initializer                   { $$ = new VarDeclNode(*$1, $2, VarDeclNode::Variable); }
;

VariableDeclarationNoIn:
    IDENT                               { $$ = new VarDeclNode(*$1, nullptr, VarDeclNode::Variable); }
  | IDENT InitializerNoIn               { $$ = new VarDeclNode(*$1, $2, VarDeclNode::Variable); }
;

ConstStatement:
    CONSTTOKEN ConstDeclarationList ';' { $$ = new VarStatementNode($2); DBG($$, @1, @3); }
  | CONSTTOKEN ConstDeclarationList error
                                        { $$ = new VarStatementNode($2); DBG($$, @1, @2); AUTO_SEMICOLON; }
;

ConstDeclarationList:
    ConstDeclaration                    { $$ = new VarDeclListNode($1); }
  | ConstDeclarationList ',' ConstDeclaration
                                        { $$ = new VarDeclListNode($1, $3); }
;

ConstDeclaration:
    IDENT                               { $$ = new VarDeclNode(*$1, 0, VarDeclNode::Constant); }
  | IDENT Initializer                   { $$ = new VarDeclNode(*$1, $2, VarDeclNode::Constant); }
;

Initializer:
    '=' AssignmentExpr                  { $$ = new AssignExprNode($2); }
;

InitializerNoIn:
    '=' AssignmentExprNoIn              { $$ = new AssignExprNode($2); }
;

EmptyStatement:
    ';'                                 { $$ = new EmptyStatementNode(); }
;

ExprStatement:
    ExprNoBF ';'                        { $$ = new ExprStatementNode($1); DBG($$, @1, @2); }
  | ExprNoBF error                      { $$ = new ExprStatementNode($1); DBG($$, @1, @1); AUTO_SEMICOLON; }
;

IfStatement:
    IF '(' Expr ')' Statement %prec IF_WITHOUT_ELSE
                                        { $$ = makeIfNode($3, $5, nullptr); DBG($$, @1, @4); }
  | IF '(' Expr ')' Statement ELSE Statement
                                        { $$ = makeIfNode($3, $5, $7); DBG($$, @1, @4); }
;

IterationStatement:
    DO Statement WHILE '(' Expr ')' ';' { $$ = new DoWhileNode($2, $5); DBG($$, @1, @3);}
  | DO Statement WHILE '(' Expr ')' error { $$ = new DoWhileNode($2, $5); DBG($$, @1, @3); AUTO_SEMICOLON; }
  | WHILE '(' Expr ')' Statement        { $$ = new WhileNode($3, $5); DBG($$, @1, @4); }
  | FOR '(' ExprNoInOpt ';' ExprOpt ';' ExprOpt ')' Statement
                                        { $$ = new ForNode($3, $5, $7, $9); DBG($$, @1, @8); }
  | FOR '(' VAR VariableDeclarationListNoIn ';' ExprOpt ';' ExprOpt ')' Statement
                                        { $$ = new ForNode($4, $6, $8, $10); DBG($$, @1, @9); }
  | FOR '(' LeftHandSideExpr INTOKEN Expr ')' Statement
                                        {
                                            Node *n = $3->nodeInsideAllParens();
                                            if (!n->isLocation())
                                                YYABORT;
                                            $$ = new ForInNode(n, $5, $7);
                                            DBG($$, @1, @6);
                                        }
  | FOR '(' VAR IDENT INTOKEN Expr ')' Statement
                                        { $$ = new ForInNode(*$4, nullptr, $6, $8); DBG($$, @1, @7); }
  | FOR '(' VAR IDENT InitializerNoIn INTOKEN Expr ')' Statement
                                        { $$ = new ForInNode(*$4, $5, $7, $9); DBG($$, @1, @8); }
;

ExprOpt:
    /* nothing */                       { $$ = nullptr; }
  | Expr
;

ExprNoInOpt:
    /* nothing */                       { $$ = nullptr; }
  | ExprNoIn
;

ContinueStatement:
    CONTINUE ';'                        { $$ = new ContinueNode(); DBG($$, @1, @2); }
  | CONTINUE error                      { $$ = new ContinueNode(); DBG($$, @1, @1); AUTO_SEMICOLON; }
  | CONTINUE IDENT ';'                  { $$ = new ContinueNode(*$2); DBG($$, @1, @3); }
  | CONTINUE IDENT error                { $$ = new ContinueNode(*$2); DBG($$, @1, @2); AUTO_SEMICOLON; }
;

BreakStatement:
    BREAK ';'                           { $$ = new BreakNode(); DBG($$, @1, @2); }
  | BREAK error                         { $$ = new BreakNode(); DBG($$, @1, @1); AUTO_SEMICOLON; }
  | BREAK IDENT ';'                     { $$ = new BreakNode(*$2); DBG($$, @1, @3); }
  | BREAK IDENT error                   { $$ = new BreakNode(*$2); DBG($$, @1, @2); AUTO_SEMICOLON; }
;

ReturnStatement:
    RETURN ';'                          { $$ = new ReturnNode(nullptr); DBG($$, @1, @2); }
  | RETURN error                        { $$ = new ReturnNode(nullptr); DBG($$, @1, @1); AUTO_SEMICOLON; }
  | RETURN Expr ';'                     { $$ = new ReturnNode($2); DBG($$, @1, @3); }
  | RETURN Expr error                   { $$ = new ReturnNode($2); DBG($$, @1, @2); AUTO_SEMICOLON; }
;

WithStatement:
    WITH '(' Expr ')' Statement         { $$ = new WithNode($3, $5); DBG($$, @1, @4); }
;

SwitchStatement:
    SWITCH '(' Expr ')' CaseBlock       { $$ = new SwitchNode($3, $5); DBG($$, @1, @4); }
;

CaseBlock:
    '{' CaseClausesOpt '}'              { $$ = new CaseBlockNode($2, nullptr, nullptr); }
  | '{' CaseClausesOpt DefaultClause CaseClausesOpt '}'
                                        { $$ = new CaseBlockNode($2, $3, $4); }
;

CaseClausesOpt:
    /* nothing */                       { $$ = nullptr; }
  | CaseClauses
;

CaseClauses:
    CaseClause                          { $$ = new ClauseListNode($1); }
  | CaseClauses CaseClause              { $$ = new ClauseListNode($1, $2); }
;

CaseClause:
    CASE Expr ':'                       { $$ = new CaseClauseNode($2); }
  | CASE Expr ':' SourceElements        { $$ = new CaseClauseNode($2, $4); }
;

DefaultClause:
    DEFAULT ':'                         { $$ = new CaseClauseNode(nullptr); }
  | DEFAULT ':' SourceElements          { $$ = new CaseClauseNode(nullptr, $3); }
;

LabelledStatement:
    IDENT ':' Statement                 { $$ = makeLabelNode(*$1, $3); }
;

ThrowStatement:
    THROW Expr ';'                      { $$ = new ThrowNode($2); DBG($$, @1, @3); }
  | THROW Expr error                    { $$ = new ThrowNode($2); DBG($$, @1, @2); AUTO_SEMICOLON; }
;

TryStatement:
    TRY Block FINALLY Block             { $$ = new TryNode($2, CommonIdentifiers::shared()->nullIdentifier, 0, $4); DBG($$, @1, @2); }
  | TRY Block CATCH '(' IDENT ')' Block { $$ = new TryNode($2, *$5, $7, nullptr); DBG($$, @1, @2); }
  | TRY Block CATCH '(' IDENT ')' Block FINALLY Block
                                        { $$ = new TryNode($2, *$5, $7, $9); DBG($$, @1, @2); }
;

DebuggerStatement:
    DEBUGGER ';'                        { $$ = new EmptyStatementNode(); DBG($$, @1, @2); }
  | DEBUGGER error                      { $$ = new EmptyStatementNode(); DBG($$, @1, @1); AUTO_SEMICOLON; }
;

PackageName:
    IDENT                               { $$ = new PackageNameNode(*$1); }
  | PackageName '.' IDENT               { $$ = new PackageNameNode($1, *$3); }
;

ImportStatement:
    IMPORT PackageName '.' '*' ';'      { $$ = makeImportNode($2, true, nullptr);
                                          DBG($$, @1, @5); }
  | IMPORT PackageName '.' '*' error    { $$ = makeImportNode($2, true, nullptr);
                                          DBG($$, @1, @5); AUTO_SEMICOLON; }
  | IMPORT PackageName ';'              { $$ = makeImportNode($2, false, nullptr);
                                          DBG($$, @1, @3); }
  | IMPORT PackageName error            { $$ = makeImportNode($2, false, nullptr);
                                          DBG($$, @1, @3); AUTO_SEMICOLON; }
  | IMPORT IDENT '=' PackageName ';'    { $$ = makeImportNode($4, false, *$2);
                                          DBG($$, @1, @5); }
  | IMPORT IDENT '=' PackageName error  { $$ = makeImportNode($4, false, *$2);
                                          DBG($$, @1, @5); AUTO_SEMICOLON; }
;

FunctionDeclaration:
    FUNCTION IDENT '(' ')' {inFuncDecl();} FunctionBody { $$ = new FuncDeclNode(*$2, $6); }
  | FUNCTION IDENT '(' FormalParameterList ')' {inFuncDecl();} FunctionBody
                                        { $$ = new FuncDeclNode(*$2, $4, $7); }
;

FunctionExpr:
    FUNCTION '(' ')' {inFuncExpr();} FunctionBody  {
      $$ = new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, $5);
    }
  | FUNCTION '(' FormalParameterList ')' {inFuncExpr();} FunctionBody {
      $$ = new FuncExprNode(CommonIdentifiers::shared()->nullIdentifier, $6, $3);
    }
  | FUNCTION IDENT '(' ')' {inFuncExpr();} FunctionBody { $$ = new FuncExprNode(*$2, $6); }
  | FUNCTION IDENT '(' FormalParameterList ')' {inFuncExpr();} FunctionBody {
      $$ = new FuncExprNode(*$2, $7, $4);
    }
;

FormalParameterList:
    IDENT                               { $$ = new ParameterNode(*$1); }
  | FormalParameterList ',' IDENT       { $$ = new ParameterNode($1, *$3); }
;

FunctionBody:
    '{' '}' /* not in spec */           { $$ = new FunctionBodyNode(nullptr); DBG($$, @1, @2); }
  | '{' SourceElements '}'              { $$ = new FunctionBodyNode($2); DBG($$, @1, @3); }
;

Program:
    /* not in spec */                   { parser().didFinishParsing(new ProgramNode(nullptr)); }
  | SourceElements                      { parser().didFinishParsing(new ProgramNode($1)); }
;

SourceElements:
    SourceElement                       { $$ = new SourceElementsNode($1); }
  | SourceElements SourceElement        { $$ = new SourceElementsNode($1, $2); }
;

SourceElement:
    FunctionDeclaration                 { $$ = $1; }
  | Statement                           { $$ = $1; }
;

%%

/* called by yyparse on error */
int yyerror(const char *)
{
// fprintf(stderr, "ERROR: %s at line %d\n", s, KJS::Lexer::curr()->lineNo());
    return 1;
}

/* may we automatically insert a semicolon ? */
static bool allowAutomaticSemicolon()
{
    return yychar == '}' || yychar == 0 || lexer().prevTerminator();
}

// kate: indent-width 2; replace-tabs on; tab-width 4; space-indent on;
