Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quellcode-Bibliothek ASTPHP5Parser.cup   Sprache: unbekannt

 
Spracherkennung für: .cup vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.netbeans.modules.php.editor.parser;

import java.util.*;
import org.netbeans.modules.csl.api.OffsetRange;
import org.netbeans.modules.php.editor.parser.astnodes.*;
import org.openide.util.Pair;

parser code {:
    private static short[][] getActionTable() {}

    enum Access {
        NON_STATIC, STATIC, NULLSAFE
    }
    protected final static Integer IMPLICIT_PUBLIC = Integer.valueOf(BodyDeclaration.Modifier.IMPLICIT_PUBLIC);
    protected final static Integer PUBLIC = Integer.valueOf(BodyDeclaration.Modifier.PUBLIC);
    protected final static Integer PRIVATE = Integer.valueOf(BodyDeclaration.Modifier.PRIVATE);
    protected final static Integer PROTECTED = Integer.valueOf(BodyDeclaration.Modifier.PROTECTED);
    protected final static Integer PUBLIC_SET = Integer.valueOf(BodyDeclaration.Modifier.PUBLIC_SET);
    protected final static Integer PRIVATE_SET = Integer.valueOf(BodyDeclaration.Modifier.PRIVATE_SET);
    protected final static Integer PROTECTED_SET = Integer.valueOf(BodyDeclaration.Modifier.PROTECTED_SET);
    protected final static Integer ABSTRACT = Integer.valueOf(BodyDeclaration.Modifier.ABSTRACT);
    protected final static Integer FINAL = Integer.valueOf(BodyDeclaration.Modifier.FINAL);
    protected final static Integer STATIC = Integer.valueOf(BodyDeclaration.Modifier.STATIC);
    protected final static Integer READONLY = Integer.valueOf(BodyDeclaration.Modifier.READONLY);

    private ErrorStrategy defaultStrategy = new DefaultErrorStrategy();;
    private ErrorStrategy errorStrategy = defaultStrategy;

    private ParserErrorHandler errorHandler = null;
    private String fileName = null;
    private int anonymousClassCounter = 0;


    public void setErrorHandler (ParserErrorHandler handler) {
        this.errorHandler = handler;
    }

    public ParserErrorHandler getErrorHandler () {
        return this.errorHandler;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        assert fileName != null;
        this.fileName = fileName;
    }

    public int incrementAndGetAnonymousClassCounter() {
        anonymousClassCounter++;
        return anonymousClassCounter;
    }

    public VariableBase createDispatch(VariableBase dispatcher, Pair<Expression, Access> pair, List dimensions) {
        VariableBase dispatch = null;
        Expression property = pair.first();
        Access access = pair.second();
        if (property instanceof DereferencedArrayAccess) {
            DereferencedArrayAccess arrayAccess = (DereferencedArrayAccess) property;
            dimensions = new LinkedList();
            dimensions.add(arrayAccess.getDimension());
            while (arrayAccess.getDispatcher() instanceof DereferencedArrayAccess) {
                arrayAccess = (DereferencedArrayAccess) arrayAccess.getDispatcher();
                ((LinkedList) dimensions).addFirst(arrayAccess.getDimension());
            }
            property = arrayAccess.getDispatcher();
        }
        if (property instanceof Variable) {
            if (access == Access.STATIC) {
                Variable variable = (Variable) property;
                if (variable.isDollared() || variable instanceof ArrayAccess) {
                    dispatch = new StaticFieldAccess(dispatcher.getStartOffset(), property.getEndOffset(), dispatcher, (Variable) property);
                } else {
                    Expression varName = variable.getName();
                    // it should always be identifier
                    String name = varName instanceof Identifier ? ((Identifier) varName).getName() : "";
                    dispatch = new StaticConstantAccess(dispatcher.getStartOffset(), property.getEndOffset(), dispatcher,
                            new Identifier(variable.getStartOffset(), variable.getEndOffset(), name));
                }
            } else {
                boolean isNullsafe = access == Access.NULLSAFE;
                dispatch = new FieldAccess(dispatcher.getStartOffset(), property.getEndOffset(), dispatcher, (Variable)property, isNullsafe);
            }
        } else if (property instanceof FunctionInvocation) {
            if (access == Access.STATIC) {
                dispatch = new StaticMethodInvocation(dispatcher.getStartOffset(), property.getEndOffset(), dispatcher, (FunctionInvocation)property);
            } else {
                boolean isNullsafe = access == Access.NULLSAFE;
                dispatch = new MethodInvocation(dispatcher.getStartOffset(), property.getEndOffset(), dispatcher, (FunctionInvocation)property, isNullsafe);
            }
        } else if (property instanceof ExpressionArrayAccess || property instanceof Identifier) {
            dispatch = new StaticConstantAccess(dispatcher.getStartOffset(), property.getEndOffset(), dispatcher, property);
        } else {
            throw new IllegalArgumentException("Unexpected class: " + property.getClass().getName());
        }

        if (dimensions != null) {
            for (Object i : dimensions) {
                ArrayDimension index = (ArrayDimension) i;
                dispatch = new DereferencedArrayAccess(dispatch.getStartOffset(), index.getEndOffset(), dispatch, index);
            }
        }
        return dispatch;
    }

    public VariableBase createDispatch(VariableBase dispatcher, VariableBase property, List dimensions, Access access) {
        return createDispatch(dispatcher, Pair.of(property, access), null);
    }

    public VariableBase createDispatch(VariableBase dispatcher, VariableBase property, Access access) {
        return createDispatch(dispatcher, property, null, access);
    }

    public VariableBase createDispatch(Access access, VariableBase var, Expression memberProperty, int memberPropertyleft, int memberPropertyright,
            List<Expression> paramsList, int paramsListright, List propertyList, List aa) {
        Expression firstVarProperty = null;
        if (paramsList == null) {
            firstVarProperty = memberProperty;
        } else {
            FunctionName functionName = new FunctionName(memberPropertyleft, memberPropertyright, memberProperty);
            firstVarProperty = new FunctionInvocation(memberPropertyleft, paramsListright, functionName, paramsList);
        }

        // then get the aggregated list of properties ([->|?->|::]...[->|?->|::]...[->|?->|::]...)
        LinkedList list = (LinkedList) propertyList;
        list.addFirst(Pair.of(firstVarProperty, access));

        // now create the dispatch(es) nodes
        VariableBase dispatch = null;
        VariableBase dispatcher = var;
        List arrayDimensiones = aa;
        Iterator listIt = list.iterator();
        while (listIt.hasNext()) {
            Pair<Expression, Access> property = (Pair<Expression, Access>) listIt.next();
            dispatch = createDispatch(dispatcher, property, arrayDimensiones);
            dispatcher = dispatch;
            arrayDimensiones = new LinkedList();
        }
        return dispatch;
    }

    public Pair<Expression, Access> createDispatchProperty(Access access, Expression memberProperty, int memberPropertyleft, int memberPropertyright,
            List<Expression> paramsList, int paramsListright, List aa) {
        Expression result = null;
        if (paramsList == null) {
            result = memberProperty;
        } else {
            FunctionName functionName = new FunctionName(memberPropertyleft, memberPropertyright, memberProperty);
            result = new FunctionInvocation(memberPropertyleft, paramsListright, functionName, paramsList);
        }
        if (result instanceof VariableBase) {
            for (Object i : aa) {
                ArrayDimension index = (ArrayDimension) i;
                result = new DereferencedArrayAccess(result.getStartOffset(), index.getEndOffset(), (VariableBase) result, index);
            }
        }
        return Pair.of(result, access);
    }

    ClassName createClassName(VariableBase var, VariableBase firstVarProperty, List<Pair<VariableBase, Access>> propertyList, int varleft, int propertyListright, Access access) {
        // then get the aggregated list of properties ([->|?->]...[->|?->]...[->|?->]...)
        LinkedList<Pair<VariableBase, Access>> list = (LinkedList<Pair<VariableBase, Access>>) propertyList;
        list.addFirst(Pair.of(firstVarProperty, access));

        // now create the dispatch(es) nodes
        VariableBase dispatch = null;
        VariableBase dispatcher = var;
        Iterator<Pair<VariableBase, Access>> listIt = list.iterator();
        while (listIt.hasNext()) {
            Pair<VariableBase, Access> property = listIt.next();
            dispatch = createDispatch(dispatcher, property.first(), property.second());
            dispatcher = dispatch;
        }

        // create class name from the dispatch
        return new ClassName(varleft, propertyListright, dispatch);
    }

    Statement createAttributedStatement(Statement statement, List<Attribute> attributes) {
        Statement attributedStatement = statement;
        if (statement instanceof FunctionDeclaration) {
            attributedStatement = FunctionDeclaration.create((FunctionDeclaration) statement, attributes);
        } else if (statement instanceof ClassDeclaration) {
            attributedStatement = ClassDeclaration.create((ClassDeclaration) statement, attributes);
        } else if (statement instanceof InterfaceDeclaration) {
            attributedStatement = InterfaceDeclaration.create((InterfaceDeclaration) statement, attributes);
        } else if (statement instanceof TraitDeclaration) {
            attributedStatement = TraitDeclaration.create((TraitDeclaration) statement, attributes);
        } else if (statement instanceof FieldsDeclaration) {
            attributedStatement = FieldsDeclaration.create((FieldsDeclaration) statement, attributes);
        } else if (statement instanceof ConstantDeclaration) {
            attributedStatement = ConstantDeclaration.create((ConstantDeclaration) statement, attributes);
        } else if (statement instanceof MethodDeclaration) {
            attributedStatement = MethodDeclaration.create((MethodDeclaration) statement, attributes);
        } else if (statement instanceof EnumDeclaration) {
            attributedStatement = EnumDeclaration.create((EnumDeclaration) statement, attributes);
        } else if (statement instanceof CaseDeclaration) {
            attributedStatement = CaseDeclaration.create((CaseDeclaration) statement, attributes);
        } else {
            assert false;
        }
        return attributedStatement;
    }

    List<Identifier> createNamespaceNameSegments(int start, String name) {
        assert name != null;
        String[] names = name.split("\\\\"); // NOI18N
        int startSegment = start;
        List<Identifier> list = new ArrayList<>();
        for (String n : names) {
            if (n.equals("namespace") || n.isEmpty()) { // NOI18N
                startSegment += n.length() + 1; // length + \
                continue;
            }
            list.add(new Identifier(startSegment, startSegment + n.length(), n));
            startSegment += n.length() + 1; // length + \
        }
        return list;
    }

    interface ErrorStrategy {
        public boolean errorRecovery(boolean debug) throws Exception;
    }

    class DefaultErrorStrategy implements ErrorStrategy {

        public boolean errorRecovery(boolean debug) throws Exception {
            return ASTPHP5Parser.super.error_recovery(debug);
        }
    }

    /**
     * Attempt to recover from a syntax error.  This returns false if recovery fails,
     * true if it succeeds.
     * @param debug should we produce debugging messages as we parse.
     */
    protected boolean error_recovery(boolean debug) throws java.lang.Exception {
        return errorStrategy.errorRecovery(debug);
    }

    /**
     * Report a non fatal error (or warning).  This method takes a message
     * string and an additional object (to be used by specializations implemented in subclasses).
     * The super class prints the message to System.err.
     * @param message an error message.
     * @param info    an extra object reserved for use by specialized subclasses.
     */
    public void report_error(String message, Object info) {
        System.out.print("report_eror"  + message);
    }

    /**
     * This method is called when a syntax error has been detected and recovery is about to be invoked.
     * The super class just emit a "Syntax error" error message.
     * @param cur_token the current lookahead Symbol.
     */
    public void syntax_error(java_cup.runtime.Symbol cur_token) {
        java_cup.runtime.Symbol symbol = (java_cup.runtime.Symbol)stack.peek();
        int state = symbol.parse_state;
        short[] rowOfProbe = action_tab[state];
        if (errorHandler != null) {
            errorHandler.handleError(ParserErrorHandler.Type.SYNTAX_ERROR, rowOfProbe, cur_token, symbol);
        }
     }

    void syntax_error() {
        syntax_error(cur_token);
    }

    /**
     * Report a fatal error.  This method takes a message string and an additional object
     * (to be used by specializations implemented in subclasses).
     * The super class reports the error then throws an exception.
     * @param message an error message.
     * @param info    an extra object reserved for use by specialized subclasses.
     */
    public void report_fatal_error(String message, Object info) throws Exception {
        if (errorHandler != null) {
            errorHandler.handleError(ParserErrorHandler.Type.FATAL_PARSER_ERROR, null, cur_token, null);
        }
    }

    protected int error_sync_size() {
        return 1;
    }

:}

/*
 * terminals
 *
 * To keep existing values(see: ASTPHP5Symbols), add new one to end.
 * If the values are changed, it's incompatible.
 */

terminal String T_EXIT;
terminal String T_IF;
terminal String T_LNUMBER;
terminal String T_DNUMBER;
terminal String T_STRING;
terminal String T_STRING_VARNAME;
terminal String T_VARIABLE;
terminal String T_NUM_STRING;
terminal T_INLINE_HTML;
terminal String T_ENCAPSED_AND_WHITESPACE;
terminal String T_CONSTANT_ENCAPSED_STRING;
terminal String T_ECHO;
terminal String T_DO;
terminal String T_WHILE;
terminal String T_ENDWHILE;
terminal String T_FOR;
terminal String T_ENDFOR;
terminal String T_FOREACH;
terminal String T_ENDFOREACH;
terminal String T_DECLARE;
terminal String T_ENDDECLARE;
terminal String T_INSTANCEOF;
terminal String T_CLONE;
terminal String T_AS;
terminal String T_SWITCH;
terminal String T_ENDSWITCH;
terminal String T_MATCH;
terminal String T_CASE;
terminal String T_DEFAULT;
terminal String T_BREAK;
terminal String T_CONTINUE;
terminal String T_GOTO;
terminal String T_FN;
terminal String T_FUNCTION;
terminal String T_CONST;
terminal String T_RETURN;
terminal String T_YIELD;
terminal T_YIELD_FROM;
terminal String T_TRY;
terminal String T_CATCH;
terminal String T_THROW;
terminal String T_FINALLY;
terminal String T_USE;
terminal String T_GLOBAL;
terminal String T_VAR;
terminal String T_UNSET;
terminal String T_ISSET;
terminal String T_EMPTY;
terminal T_HALT_COMPILER;
terminal String T_CLASS;
terminal String T_INTERFACE;
terminal String T_EXTENDS;
terminal String T_IMPLEMENTS;
terminal T_OBJECT_OPERATOR;
terminal T_NULLSAFE_OBJECT_OPERATOR;
terminal T_DOUBLE_ARROW;
terminal String T_LIST;
terminal String T_ARRAY;
terminal String T_CALLABLE;
terminal String T_CLASS_C;
terminal String T_TRAIT_C;
terminal String T_METHOD_C;
terminal String T_FUNC_C;
terminal String T_LINE;
terminal String T_FILE;
terminal T_START_HEREDOC;
terminal T_END_HEREDOC;
terminal T_DOLLAR_OPEN_CURLY_BRACES;
terminal T_CURLY_OPEN_WITH_DOLAR;
terminal T_CURLY_OPEN;
terminal T_CURLY_CLOSE;
terminal T_PAAMAYIM_NEKUDOTAYIM;
terminal String T_NAMESPACE;
terminal String T_NS_C;
terminal String T_DIR;
terminal T_NS_SEPARATOR;
terminal String T_VAR_COMMENT;
terminal String T_DEFINE;

terminal String T_INCLUDE,T_INCLUDE_ONCE,T_EVAL,T_REQUIRE,T_REQUIRE_ONCE;
terminal T_COMMA;
terminal String T_LOGICAL_OR,T_LOGICAL_XOR,T_LOGICAL_AND,T_PRINT;
terminal T_EQUAL;
terminal T_PLUS_EQUAL,T_MINUS_EQUAL,T_MUL_EQUAL,T_DIV_EQUAL,T_CONCAT_EQUAL,T_MOD_EQUAL,T_AND_EQUAL,T_OR_EQUAL,T_XOR_EQUAL,T_SL_EQUAL,T_SR_EQUAL;
terminal T_QUESTION_MARK;
terminal T_SEMICOLON;
terminal T_BOOLEAN_OR, T_BOOLEAN_AND;
terminal T_OR;
terminal T_KOVA;
terminal T_REFERENCE;
terminal T_IS_EQUAL,T_IS_NOT_EQUAL,T_IS_IDENTICAL,T_IS_NOT_IDENTICAL;
terminal T_IS_SMALLER_OR_EQUAL,T_IS_GREATER_OR_EQUAL;
terminal T_SPACESHIP;
terminal T_RGREATER;
terminal T_LGREATER;
terminal T_SL,T_SR;
terminal T_PLUS;
terminal T_MINUS;
terminal T_TIMES;
terminal T_DIV;
terminal T_PRECENT;
terminal T_NOT;
terminal T_TILDA;
terminal T_NEKUDA;
terminal T_INC,T_DEC,T_INT_CAST,T_DOUBLE_CAST,T_STRING_CAST,T_ARRAY_CAST,T_OBJECT_CAST,T_BOOL_CAST,T_UNSET_CAST;
terminal T_AT;
terminal T_OPEN_RECT,T_CLOSE_RECT;
terminal String T_NEW;
terminal String T_ENDIF;
terminal String T_ELSEIF;
terminal String T_ELSE;
terminal String T_STATIC, T_ABSTRACT, T_FINAL, T_PRIVATE, T_PROTECTED, T_PUBLIC;
terminal T_OPEN_PARENTHESE,T_CLOSE_PARENTHESE;
terminal T_NEKUDOTAIM;
terminal T_DOLLAR;
terminal T_QUATE,T_BACKQUATE;
terminal T_START_NOWDOC, T_END_NOWDOC;
terminal String T_TRAIT;
terminal String T_INSTEADOF;
terminal T_POW;
terminal T_POW_EQUAL;
terminal T_ELLIPSIS;
terminal T_COALESCE;
terminal T_COALESCE_EQUAL;
terminal T_ATTRIBUTE;
terminal String T_READONLY;
terminal T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG; /* & */
terminal String T_ENUM; /* PHP 8.1 */
terminal String T_NAME_RELATIVE; /* PHP 8.0 namespace\Foo */
terminal String T_NAME_QUALIFIED; /* PHP 8.0 Foo\Bar */
terminal String T_NAME_FULLY_QUALIFIED; /* PHP 8.0 \Foo\Bar */
terminal String T_PUBLIC_SET, T_PROTECTED_SET, T_PRIVATE_SET; /* PHP 8.4 */

/* Non terminals */

non terminal Program thestart;
non terminal List namespace_name;
non terminal NamespaceName namespace_name_access;
non terminal NamespaceName legacy_namespace_name;
non terminal List namespace_declaration_name;
non terminal List group_namespace_parts;
non terminal List non_empty_group_namespace_parts;
non terminal SingleUseStatementPart group_namespace_part;
non terminal UseStatementPart use_declaration;
non terminal List use_declarations;
non terminal List top_statement_list;
non terminal Statement top_statement;
non terminal Statement statement;
non terminal List inner_statement_list;
non terminal Statement inner_statement;
non terminal Statement unticked_statement;
non terminal List unset_variables;
non terminal VariableBase unset_variable;
non terminal Expression use_filename;
non terminal Expression foreach_optional_arg;
non terminal Expression foreach_variable;
non terminal Statement for_statement;
non terminal Statement foreach_statement;
non terminal Statement declare_statement;
non terminal List[] declare_list;
non terminal Block switch_case_list;
non terminal List case_list;
non terminal case_separator;
non terminal Statement while_statement;
non terminal List[] elseif_list;
non terminal List[] new_elseif_list;
non terminal Statement else_single;
non terminal Statement new_else_single;
non terminal List parameter_list;
non terminal List lexical_vars;
non terminal List lexical_var_list;
non terminal List non_empty_parameter_list;
non terminal FormalParameter parameter;
non terminal List function_call_parameter_list;
non terminal List non_empty_function_call_parameter_list;
non terminal Expression argument;
non terminal Expression argument_expr;
non terminal List global_var_list;
non terminal VariableBase global_var;
non terminal List static_var_list;
non terminal List class_statement_list;
non terminal Statement class_statement;
non terminal Boolean is_reference;
non terminal Boolean is_variadic;
non terminal List echo_expr_list;
non terminal List for_expr;
non terminal List non_empty_for_expr;
non terminal Expression expr_without_variable;
non terminal Expression expr_without_variable_and_class_instance;
non terminal Expression callable_expr;
non terminal VariableBase function_call;
non terminal Expression exit_expr;
non terminal List ctor_arguments;
non terminal Expression common_scalar;
non terminal Expression static_scalar;
non terminal Expression static_scalar_with_class_instance;
non terminal Expression static_scalar_value;
non terminal Expression static_scalar_value_with_class_instance;
non terminal Expression static_operation;
non terminal Expression scalar;
non terminal List static_array_pair_list;
non terminal possible_comma;
non terminal List non_empty_static_array_pair_list;
non terminal Expression expr;
non terminal Expression expr_with_error;
non terminal Expression expr_with_yields;
non terminal Expression expr_with_yields_and_error;
non terminal Expression expr_without_class_instance;
non terminal Expression yield_expr;
non terminal Expression yield_from_expr;
non terminal Expression inline_function;
non terminal MatchExpression match;
non terminal List match_arm_list;
non terminal List non_empty_match_arm_list;
non terminal MatchArm match_arm;
non terminal List match_arm_condition_list;
non terminal ParenthesisExpression parenthesis_expr;
non terminal Variable reference_variable;
non terminal Variable variable_class_name;
non terminal Variable compound_variable;
non terminal Expression dim_offset;
non terminal ArrayDimension array_dimension;
non terminal ArrayDimension array_dimension_with_static_scalar_value;
non terminal List array_access_or_not;
non terminal Expression static_property;
non terminal VariableBase object_property;
non terminal VariableBase object_dim_list;
non terminal VariableBase variable_name;
non terminal Integer simple_indirect_reference;
non terminal List array_pair_list;
non terminal ArrayElement possible_array_pair;
non terminal ArrayElement array_pair;
non terminal List non_empty_array_pair_list;
non terminal List encaps_list;
non terminal VariableBase encaps_var;
non terminal Expression encaps_var_offset;
non terminal Expression internal_functions_in_yacc;
non terminal String string_st;
non terminal Integer interface_entry;
non terminal List interface_extends_list;
non terminal List use_traits;
non terminal Block use_traits_body;
non terminal List use_traits_body_statement_list;
non terminal Statement use_traits_body_statement;
non terminal Statement trait_conflict_resolution_declaration;
non terminal Statement trait_method_alias_declaration;
non terminal TraitMethodAliasDeclaration.Modifier traits_alias_modifier;
non terminal List trait_statement_list;
non terminal Statement trait_statement;
non terminal List interface_statement_list;
non terminal Statement interface_statement;
non terminal UseTraitStatementPart use_trait;
non terminal Quote heredoc;
non terminal VariableBase field_or_method_access;
non terminal Expression expression_array_access;
non terminal Expression constant_array_access;
non terminal Expression array_creation;
non terminal VariableBase array_creation_with_access;
non terminal ClassInstanceCreation anonymous_class;

non terminal VariableBase w_variable;
non terminal Expression class_name;
non terminal NamespaceName fully_qualified_class_name;
non terminal List class_variable_declaration;
non terminal Identifier reserved_non_modifiers_without_class;
non terminal Identifier semi_reserved_without_class;
non terminal Identifier identifier_without_class;
non terminal Identifier identifier;
non terminal Pair<Expression, List<ASTNode[]>> class_constant_declaration;
non terminal List constant_declaration;
non terminal Integer optional_property_modifiers;
non terminal Integer constant_modifiers;
non terminal Integer method_modifiers;
non terminal Block method_body;
non terminal List method_or_not;
non terminal List variable_properties;
non terminal FunctionDeclaration function_declaration_statement;
non terminal Statement class_declaration_statement;
non terminal VariableBase variable;
non terminal List additional_catches;
non terminal List non_empty_additional_catches;
non terminal CatchClause additional_catch;
non terminal List catch_class_names;
non terminal List additional_catch_class_names;
non terminal List non_empty_additional_catch_class_names;
non terminal Expression additional_catch_class_name;
non terminal FinallyClause additional_finally;
non terminal FunctionDeclaration unticked_function_declaration_statement;
non terminal Statement unticked_class_declaration_statement;
non terminal Map<ClassDeclaration.Modifier, Set<OffsetRange>> class_entry_type;
non terminal Map<ClassDeclaration.Modifier, Set<OffsetRange>> class_modifiers;
non terminal ClassDeclaration.Modifier class_modifier;
non terminal Expression extends_from;
non terminal List implements_list;
non terminal List interface_list;
non terminal Expression optional_class_type_without_static;
non terminal Expression type_expr;
non terminal Expression type_expr_without_static;
non terminal Expression class_type;
non terminal Expression class_type_without_static;
non terminal List<Expression> union_type;
non terminal Expression union_type_element;
non terminal List<Expression> union_type_without_static;
non terminal Expression union_type_without_static_element;
non terminal List<Expression> intersection_type;
non terminal List<Expression> intersection_type_without_static;
non terminal ampersand;
non terminal Expression optional_return_type;
non terminal VariableBase r_variable;
non terminal Integer variable_modifiers;
non terminal VariableBase rw_variable;
non terminal Variable variable_without_objects;
non terminal Pair<Expression, Boolean> variable_property;
non terminal VariableBase static_member;
non terminal List isset_variables;
non terminal Expression isset_variable;
non terminal Variable tracked_variable;
non terminal Variable optional_tracked_variable;
non terminal Integer abstract_modifier;
non terminal Integer final_modifier;
non terminal Integer readonly_modifier;
non terminal Integer static_modifier;
non terminal Integer ppp_modifiers;
non terminal Integer ppp_set_modifiers;
non terminal Integer af_modifiers;
non terminal Integer member_modifiers;
non terminal Integer non_empty_member_modifiers;

non terminal ClassName class_name_reference;
non terminal StaticConstantAccess class_constant;
non terminal StaticConstantAccess enum_constant;
non terminal ClassName dynamic_class_name_reference;
non terminal VariableBase dereferencable_variable;
non terminal VariableBase base_variable;
non terminal VariableBase base_variable_without_reference_variable;
non terminal List dynamic_class_name_variable_properties;
non terminal Pair<VariableBase, ASTPHP5Parser.Access> dynamic_class_name_variable_property;
non terminal VariableBase static_class_constant;
non terminal Expression static_reference_constant;
non terminal Expression static_array_creation_with_access;
non terminal Expression static_constant_array_access;
non terminal Expression static_class_constant_array_access;
non terminal Expression class_constant_array_access;
non terminal Expression static_array_creation;
non terminal VariableBase base_variable_with_function_calls;
non terminal Expression new_expr;
non terminal ASTPHP5Parser.Access access_operator;

non terminal Expression enum_backing_type;
non terminal Statement enum_case;
non terminal Expression enum_case_expr;

non terminal List class_name_list;
/*
 * NETBEANS-4443 PHP 8.0 Attribute Syntax
 * - https://wiki.php.net/rfc/attributes_v2
 * - https://wiki.php.net/rfc/shorter_attribute_syntax
 * - https://wiki.php.net/rfc/shorter_attribute_syntax_change
 */
non terminal Attribute attribute;
non terminal List<Attribute> attributes;
non terminal AttributeDeclaration attribute_decl;
non terminal List<AttributeDeclaration> attribute_group;
non terminal Statement attributed_statement;
non terminal Statement attributed_interface_statement;
non terminal Statement attributed_class_statement;
non terminal Statement attributed_trait_statement;
non terminal FormalParameter attributed_parameter;

precedence nonassoc T_THROW;
precedence left T_INCLUDE, T_INCLUDE_ONCE, T_EVAL, T_REQUIRE, T_REQUIRE_ONCE;
precedence left T_COMMA;
precedence left T_LOGICAL_OR;
precedence left T_LOGICAL_XOR;
precedence left T_LOGICAL_AND;
precedence right T_PRINT;
precedence right T_YIELD;
precedence right T_DOUBLE_ARROW;
precedence right T_YIELD_FROM;
precedence left T_EQUAL, T_PLUS_EQUAL,T_MINUS_EQUAL,T_MUL_EQUAL,T_DIV_EQUAL,T_CONCAT_EQUAL,T_MOD_EQUAL,T_AND_EQUAL,T_OR_EQUAL,T_XOR_EQUAL,T_SL_EQUAL,T_SR_EQUAL,T_POW_EQUAL,T_COALESCE_EQUAL;
precedence right T_POW;
precedence left T_QUESTION_MARK,T_SEMICOLON;
precedence left T_BOOLEAN_OR;
precedence left T_BOOLEAN_AND;
precedence left T_OR;
precedence left T_KOVA;
precedence left T_REFERENCE;
precedence left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG;
precedence left T_ELLIPSIS;
precedence left T_COALESCE;

precedence nonassoc T_IS_EQUAL,T_IS_NOT_EQUAL,T_IS_IDENTICAL,T_IS_NOT_IDENTICAL;
precedence nonassoc T_RGREATER,T_IS_SMALLER_OR_EQUAL,T_LGREATER,T_IS_GREATER_OR_EQUAL,T_SPACESHIP;
precedence left T_SL,T_SR;
precedence left T_PLUS,T_MINUS,T_NEKUDA;
precedence left T_TIMES,T_DIV,T_PRECENT;
precedence right T_NOT;
precedence nonassoc T_INSTANCEOF;
precedence right T_TILDA,T_INC,T_DEC,T_INT_CAST,T_DOUBLE_CAST,T_STRING_CAST,T_ARRAY_CAST,T_OBJECT_CAST,T_BOOL_CAST,T_UNSET_CAST,T_AT;
precedence right T_CLOSE_PARENTHESE;
precedence right T_OPEN_RECT, T_CURLY_OPEN, T_OPEN_PARENTHESE;
precedence nonassoc T_NEW, T_CLONE;
precedence left T_ELSEIF;
precedence left T_ELSE;
precedence left T_ENDIF;
precedence right T_STATIC, T_ABSTRACT, T_FINAL, T_PRIVATE, T_PROTECTED, T_PUBLIC, T_READONLY, T_PUBLIC_SET, T_PROTECTED_SET, T_PRIVATE_SET;

thestart ::=
top_statement_list:statementList
{:
    ASTPHP5Scanner phpAstLexer5 = (ASTPHP5Scanner) parser.getScanner();
    List commentList = phpAstLexer5.getCommentList();
    int endOfProgram = statementListright > phpAstLexer5.getWhitespaceEndPosition() || phpAstLexer5.isEndedPhp() ? statementListright : phpAstLexer5.getWhitespaceEndPosition();
    Program program = new Program(statementListleft, endOfProgram, statementList, commentList);
    RESULT = program;
:}
;

namespace_name ::=
T_STRING:n
{:
    List list = new LinkedList();
    list.add(new Identifier(nleft, nright, n));
    RESULT = list;
:}

|
T_DEFINE:n
{:
    List list = new LinkedList();
    list.add(new Identifier(nleft, nright, "define"));
    RESULT = list;
:}

| T_NAME_QUALIFIED:name
{:
    // e.g. Foo\Bar
    RESULT = parser.createNamespaceNameSegments(nameleft, name);
:}
;

namespace_declaration_name ::=
identifier: identifier
{:
    // e.g. Foo
    List list = new LinkedList();
    list.add(identifier);
    RESULT = list;

:}

| T_NAME_QUALIFIED:name
{:
    // e.g. Foo\Bar
    RESULT = parser.createNamespaceNameSegments(nameleft, name);
:}
;

legacy_namespace_name ::=
namespace_name:list
{:
    RESULT = new NamespaceName(listleft, listright, list, false, false);
:}

| T_NAME_FULLY_QUALIFIED:name
{:
    // e.g. \Foo\Bar
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}
;

namespace_name_access ::=
T_STRING:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_DEFINE:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_NAME_QUALIFIED:name
{:
    // e.g. Foo\Bar
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_NAME_FULLY_QUALIFIED:name
{:
    // e.g. \Foo\Bar
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_NAME_RELATIVE:name
{:
    // e.g. namespace\Foo\Bar
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}
;

reserved_non_modifiers_without_class ::=
T_INCLUDE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_INCLUDE_ONCE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_EVAL:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_REQUIRE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_REQUIRE_ONCE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_LOGICAL_OR:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_LOGICAL_XOR:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_LOGICAL_AND:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_INSTANCEOF:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_NEW:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_CLONE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_EXIT:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_IF:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ELSEIF:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ELSE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ENDIF:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ECHO:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_DO:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_WHILE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ENDWHILE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FOR:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ENDFOR:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FOREACH:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ENDFOREACH:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_DECLARE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ENDDECLARE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_AS:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_TRY:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_CATCH:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FINALLY:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_THROW:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_USE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_INSTEADOF:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_GLOBAL:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_VAR:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_UNSET:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ISSET:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_EMPTY:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_CONTINUE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_GOTO:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FN:reserved
{:
    // PHP 7.4
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FUNCTION:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_CONST:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_RETURN:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_PRINT:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_YIELD:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_LIST:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_MATCH:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_SWITCH:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ENDSWITCH:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_CASE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_DEFAULT:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_BREAK:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_ARRAY:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_CALLABLE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_EXTENDS:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_IMPLEMENTS:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_NAMESPACE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_TRAIT:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_INTERFACE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

/*
| T_CLASS:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}
*/

| T_CLASS_C:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_TRAIT_C:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FUNC_C:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_METHOD_C:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_LINE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_FILE:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_DIR:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}

| T_NS_C:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}
;

semi_reserved_without_class ::=
reserved_non_modifiers_without_class:reserved
{:
    RESULT = reserved;
:}

| T_STATIC:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}

| T_ABSTRACT:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}

| T_FINAL:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}

| T_PRIVATE:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}

| T_PROTECTED:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}

| T_PUBLIC:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}

| T_READONLY:modifier
{:
    RESULT = new Identifier(modifierleft, modifierright, modifier, true);
:}
;

identifier ::=
identifier_without_class:ident
{:
    RESULT = ident;
:}

| T_CLASS:reserved
{:
    RESULT = new Identifier(reservedleft, reservedright, reserved, true);
:}
;

identifier_without_class ::=
T_STRING:string
{:
    RESULT = new Identifier(stringleft, stringright, string);
:}

| T_DEFINE:define
{:
    RESULT = new Identifier(defineleft, defineright, define);
:}

| semi_reserved_without_class:reserved
{:
    RESULT = reserved;
:}
;

top_statement_list ::=
top_statement_list:sList top_statement:statement
{:
    if(statement != null) {
        if (!(statement instanceof NamespaceDeclaration) && sList.size() > 0) {
            Statement lastStatement = (Statement) ((LinkedList) sList).getLast();
            if (lastStatement instanceof NamespaceDeclaration) {
                NamespaceDeclaration namespaceDeclaration = (NamespaceDeclaration) lastStatement;
                // there should be NO statement outside bracketed namespaces - it's PHP FATAL ERROR
                // (that statement doesn't belong to last bracketed namespace)
                if (!namespaceDeclaration.isBracketed()) {
                    namespaceDeclaration.addStatement(statement);
                }
            } else {
                sList.add(statement);
            }
        } else {
            sList.add(statement);
        }
    }
    RESULT = sList;
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

attributed_statement ::=
function_declaration_statement:statement
{:
    RESULT = statement;
:}

| class_declaration_statement:statement
{:
    RESULT = statement;
:}
;

top_statement ::=
statement:statement
{:
    RESULT = statement;
:}

| attributed_statement:statement
{:
    RESULT = statement;
:}

| attributes:attributes attributed_statement:statement
{:
    RESULT = parser.createAttributedStatement(statement, attributes);
:}

| T_HALT_COMPILER:halt
{:
    RESULT = new HaltCompiler(haltleft, haltright);
:}

| T_NAMESPACE:s namespace_declaration_name:list T_SEMICOLON:e
{:
    RESULT = new NamespaceDeclaration(sleft, eright,
        new NamespaceName(listleft, listright, list, false, false), null, false);
:}

| T_NAMESPACE:s namespace_declaration_name:list T_CURLY_OPEN:token top_statement_list:sList T_CURLY_CLOSE:e
{:
    RESULT = new NamespaceDeclaration(sleft, eright,
        new NamespaceName(listleft, listright, list, false, false),
        new Block(tokenleft, eright, sList), true);
:}

| T_NAMESPACE:s T_CURLY_OPEN:token top_statement_list:sList T_CURLY_CLOSE:e
{:
    RESULT = new NamespaceDeclaration(sleft, eright, null,
        new Block(tokenleft, eright, sList), true);
:}

| T_USE:s use_declarations:list T_SEMICOLON:e
{:
    RESULT = new UseStatement(sleft, eright, list);
:}

| T_USE:use T_FUNCTION use_declarations:list T_SEMICOLON:e
{:
    RESULT = new UseStatement(useleft, eright, list, UseStatement.Type.FUNCTION);
:}

| T_USE:use T_CONST use_declarations:list T_SEMICOLON:e
{:
    RESULT = new UseStatement(useleft, eright, list, UseStatement.Type.CONST);
:}

| constant_declaration:list T_SEMICOLON:e
{:
    RESULT = new ConstantDeclaration(listleft, eright, ASTPHP5Parser.IMPLICIT_PUBLIC, list, true);
:}
;

attribute_decl ::=
class_name:name
{:
    RESULT = new AttributeDeclaration(nameleft, nameright, name, null);
:}

| class_name:name T_OPEN_PARENTHESE function_call_parameter_list:paramList T_CLOSE_PARENTHESE:e
{:
    RESULT = new AttributeDeclaration(nameleft, eright, name, paramList);
:}
;

attribute_group ::=
attribute_decl:decl
{:
    List list = new LinkedList();
    list.add(decl);
    RESULT = list;
:}

| attribute_group:list T_COMMA attribute_decl:decl
{:
    list.add(decl);
    RESULT = list;
:}
;

attribute ::=
T_ATTRIBUTE:start attribute_group:group possible_comma T_CLOSE_RECT:end
{:
    RESULT = new Attribute(startleft, endright, group);
:}
;

attributes ::=
attribute:attribute
{:
    List list = new LinkedList();
    list.add(attribute);
    RESULT = list;
:}

| attributes:list attribute:attribute
{:
    list.add(attribute);
    RESULT = list;
:}
;

use_declarations ::=
use_declarations:list T_COMMA use_declaration:useDecl
{:
    list.add(useDecl);
    RESULT = list;
:}

| use_declaration:useDecl
{:
    List list = new LinkedList();
    list.add(useDecl);
    RESULT = list;
:}
;

use_declaration ::=
legacy_namespace_name:name
{:
    RESULT = new SingleUseStatementPart(nameleft, nameright, name, null);
:}

| legacy_namespace_name:name T_AS T_STRING:aliasName
{:
    RESULT = new SingleUseStatementPart(nameleft, aliasNameright, name,
        new Identifier(aliasNameleft, aliasNameright, aliasName));
:}

| legacy_namespace_name:basens T_NS_SEPARATOR:s T_CURLY_OPEN:open group_namespace_parts:parts T_CURLY_CLOSE:close
{:
    RESULT = new GroupUseStatementPart(basensleft, closeright, basens, parts);
:}
;

// used only in group uses
group_namespace_parts ::=
non_empty_group_namespace_parts:list possible_comma
{:
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

non_empty_group_namespace_parts ::=
non_empty_group_namespace_parts:list T_COMMA group_namespace_part:part
{:
    list.add(part);
    RESULT = list;
:}

| group_namespace_part:part
{:
    List list = new LinkedList();
    list.add(part);
    RESULT = list;
:}
;

group_namespace_part ::=
namespace_name:part
{:
    RESULT = new SingleUseStatementPart(partleft, partright, new NamespaceName(partleft, partright, part, false, false), null);
:}

| namespace_name:part T_AS T_STRING:alias
{:
    RESULT = new SingleUseStatementPart(partleft, aliasright, new NamespaceName(partleft, partright, part, false, false), new Identifier(aliasleft, aliasright, alias));
:}

| T_FUNCTION:f namespace_name:part
{:
    RESULT = new SingleUseStatementPart(fleft, partright, UseStatement.Type.FUNCTION, new NamespaceName(partleft, partright, part, false, false), null);
:}

| T_FUNCTION:f namespace_name:part T_AS T_STRING:alias
{:
    RESULT = new SingleUseStatementPart(fleft, aliasright, UseStatement.Type.FUNCTION, new NamespaceName(partleft, partright, part, false, false), new Identifier(aliasleft, aliasright, alias));
:}

| T_CONST:c namespace_name:part
{:
    RESULT = new SingleUseStatementPart(cleft, partright, UseStatement.Type.CONST, new NamespaceName(partleft, partright, part, false, false), null);
:}

| T_CONST:c namespace_name:part T_AS T_STRING:alias
{:
    RESULT = new SingleUseStatementPart(cleft, aliasright, UseStatement.Type.CONST, new NamespaceName(partleft, partright, part, false, false), new Identifier(aliasleft, aliasright, alias));
:}
;

inner_statement_list ::=
inner_statement_list:statementList inner_statement:statement
{:
    // Ignore null statements
    if(statement != null) {
        statementList.add(statement);
    }
    RESULT = statementList;
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

inner_statement ::=
statement:statement
{:
    RESULT = statement;
:}

| attributed_statement:statement
{:
    RESULT = statement;
:}

| attributes:attributes attributed_statement:statement
{:
    RESULT = parser.createAttributedStatement(statement, attributes);
:}
;

statement ::=
unticked_statement:statement
{:
    RESULT = statement;
:}
| T_STRING:label T_NEKUDOTAIM:e
{:
    RESULT = new GotoLabel(labelleft, eright, new Identifier(labelleft, labelright, label));
:}
;

unticked_statement ::=
T_CURLY_OPEN:token inner_statement_list:statementList T_CURLY_CLOSE:end
{:
    Block block = new Block(tokenleft, endright, statementList);
    RESULT = block;
:}

| T_IF:token T_OPEN_PARENTHESE expr:condition T_CLOSE_PARENTHESE statement:iftrue elseif_list:elseif else_single:iffalse
{:
    Expression innerCondition = null;
    Statement trueStatement = null;
    Statement falseStatement = iffalse;

    for (int i=0 ; i < elseif[0].size() ; i++) {
        innerCondition = (Expression)elseif[0].get(i);
        trueStatement = (Statement)elseif[1].get(i);
        int start = ((Integer)elseif[2].get(i)).intValue();
        falseStatement = new IfStatement(start, iffalseright, innerCondition, trueStatement, falseStatement);
    }
    IfStatement ifStatement = new IfStatement(tokenleft, iffalseright, condition, iftrue, falseStatement);

    RESULT = ifStatement;
:}

| T_IF:token T_OPEN_PARENTHESE expr:condition T_CLOSE_PARENTHESE T_NEKUDOTAIM:colon inner_statement_list:ifTrueStatementList new_elseif_list:elseif new_else_single:iffalse T_ENDIF T_SEMICOLON:end
{:
    Expression innerCondition = null;
    Statement trueStatement = null;
    Statement falseStatement = iffalse;

    for (int i=0 ; i < elseif[0].size() ; i++) {
        innerCondition = (Expression)elseif[0].get(i);
        trueStatement = (Statement)elseif[1].get(i);
        int start = ((Integer)elseif[2].get(i)).intValue();
        falseStatement = new IfStatement(start, iffalseright, innerCondition, trueStatement, falseStatement);
    }
    Block block = new Block(colonleft, ifTrueStatementListright, ifTrueStatementList, false);
    IfStatement ifStatement = new IfStatement(tokenleft, iffalseright, condition, block, falseStatement);

    RESULT = ifStatement;
:}

| T_WHILE:token T_OPEN_PARENTHESE expr:expr T_CLOSE_PARENTHESE while_statement:statement
{:
    WhileStatement whileStatement = new WhileStatement(tokenleft, statementright, expr, statement);
    RESULT = whileStatement;
:}

| T_DO:token statement:statement T_WHILE T_OPEN_PARENTHESE expr:expr T_CLOSE_PARENTHESE T_SEMICOLON:end
{:
    DoStatement doStatement = new DoStatement(tokenleft, endright, expr, statement);
    RESULT = doStatement;
:}

| T_FOR:token T_OPEN_PARENTHESE for_expr:initializations T_SEMICOLON for_expr:conditions T_SEMICOLON for_expr:increasements T_CLOSE_PARENTHESE for_statement:statement
{:
    ForStatement forStatement = new ForStatement(tokenleft, statementright, initializations, conditions, increasements, statement);
    RESULT = forStatement;
:}

| T_SWITCH:token T_OPEN_PARENTHESE expr:expr T_CLOSE_PARENTHESE switch_case_list:caseBlock
{:
    SwitchStatement switchStatement = new SwitchStatement(tokenleft, caseBlockright, expr, caseBlock);
    RESULT = switchStatement;
:}

| T_BREAK:token T_SEMICOLON:end
{:
    RESULT = new BreakStatement(tokenleft, endright);
:}

| T_BREAK:token expr:expr T_SEMICOLON:end
{:
    RESULT = new BreakStatement(tokenleft, endright, expr);
:}

| T_CONTINUE:token T_SEMICOLON:end
{:
    RESULT = new ContinueStatement(tokenleft, endright);
:}

| T_CONTINUE:token expr:expr T_SEMICOLON:end
{:
    RESULT = new ContinueStatement(tokenleft, endright, expr);
:}

| T_RETURN:token T_SEMICOLON:end
{:
    RESULT = new ReturnStatement(tokenleft, endright);
:}

| T_RETURN:token expr_without_variable:expr T_SEMICOLON:end
{:
    RESULT = new ReturnStatement(tokenleft, endright, expr);
:}

| T_RETURN:token variable:expr T_SEMICOLON:end
{:
    RESULT = new ReturnStatement(tokenleft, endright, expr);
:}

| T_RETURN:token yield_from_expr:expr T_SEMICOLON:end
{:
    RESULT = new ReturnStatement(tokenleft, endright, expr);
:}

| T_GLOBAL:start global_var_list:list T_SEMICOLON:end
{:
    GlobalStatement global = new GlobalStatement(startleft, endright, list);
    RESULT = global;
:}

| T_STATIC:start static_var_list:list T_SEMICOLON:end
{:
    StaticStatement s = new StaticStatement(startleft, endright, list);
    RESULT = s;
:}

| T_ECHO:start echo_expr_list:exprList T_SEMICOLON:end
{:
    RESULT = new EchoStatement(startleft, endright, exprList);
:}

| T_INLINE_HTML:html
{:
    InLineHtml inLineHtml = new InLineHtml(htmlleft, htmlright);
    RESULT = inLineHtml;
:}

| expr_with_yields:expr T_SEMICOLON:end
{:
    ExpressionStatement expressionStatement = new ExpressionStatement(exprleft, endright, expr);
    RESULT = expressionStatement;
:}

| T_USE:start use_filename:expr T_SEMICOLON:end
{:
    List list = new LinkedList();
    list.add(expr);
    Identifier id = new Identifier(startleft, startright, "use");
    FunctionName functionName = new FunctionName(startleft, startright, id);
    FunctionInvocation functionInvocation = new FunctionInvocation(startleft, exprright, functionName, list);
    ExpressionStatement expressionStatement = new ExpressionStatement(startleft, endright, functionInvocation);
    RESULT = expressionStatement;
:}

| T_UNSET:start T_OPEN_PARENTHESE unset_variables:list possible_comma T_CLOSE_PARENTHESE:closePar T_SEMICOLON:end
{:
    Identifier id = new Identifier(startleft, startright, "unset");
    FunctionName functionName = new FunctionName(startleft, startright, id);
    FunctionInvocation functionInvocation = new FunctionInvocation(startleft, closeParright, functionName, list);
    ExpressionStatement expressionStatement = new ExpressionStatement(startleft, endright, functionInvocation);
    RESULT = expressionStatement;
:}

| T_FOREACH:token T_OPEN_PARENTHESE variable:expr T_AS foreach_variable:var foreach_optional_arg:arg T_CLOSE_PARENTHESE foreach_statement:statement
{:
    ForEachStatement forEachStatement = null;
    if (arg == null) {
        forEachStatement = new ForEachStatement(tokenleft, statementright, expr, var, statement);
    } else {
        forEachStatement = new ForEachStatement(tokenleft, statementright, expr, var, arg, statement);
    }
    RESULT = forEachStatement;
:}

| T_FOREACH:token T_OPEN_PARENTHESE expr_without_variable:expr T_AS foreach_variable:var foreach_optional_arg:arg T_CLOSE_PARENTHESE foreach_statement:statement
{:
    ForEachStatement forEachStatement = null;
    if (arg == null) {
        forEachStatement = new ForEachStatement(tokenleft, statementright, expr, var, statement);
    } else {
        forEachStatement = new ForEachStatement(tokenleft, statementright, expr, var, arg, statement);
    }
    RESULT = forEachStatement;
:}

| T_DECLARE:start T_OPEN_PARENTHESE declare_list:lists T_CLOSE_PARENTHESE declare_statement:statement
{:
    DeclareStatement declare = new DeclareStatement(startleft, statementright, lists[0], lists[1], statement);
    RESULT = declare;
:}

| T_SEMICOLON:token /* empty statement */
{:
    RESULT = new EmptyStatement(tokenleft, tokenright);
:}

| T_TRY:start T_CURLY_OPEN:tryBlockStart inner_statement_list:tryList T_CURLY_CLOSE:tryBlockEnd T_FINALLY:finally_word T_CURLY_OPEN:finallyBlockStart inner_statement_list:finallyList T_CURLY_CLOSE:finallyBlockEnd
{:
    Block tryBlock = new Block(tryBlockStartleft, tryBlockEndright, tryList);
    Block finallyBlock = new Block(finallyBlockStartleft, finallyBlockEndright, finallyList);
    FinallyClause finallyClause = new FinallyClause(finally_wordleft, finallyBlockEndright, finallyBlock);
    TryStatement tryStatement = new TryStatement(startleft, finallyBlockEndright, tryBlock, null, finallyClause);
    RESULT = tryStatement;
:}

| T_TRY:start T_CURLY_OPEN:tryBlockStart inner_statement_list:tryList T_CURLY_CLOSE:tryBlockEnd T_CATCH:catch_word T_OPEN_PARENTHESE catch_class_names:classNames optional_tracked_variable:var T_CLOSE_PARENTHESE
T_CURLY_OPEN:catchBlockStart inner_statement_list:catchList T_CURLY_CLOSE:catchBlockEnd additional_catches:catchesList additional_finally:finallyBlock
{:
    Block tryBlock = new Block(tryBlockStartleft, tryBlockEndright, tryList);
    Block catchBlock = new Block(catchBlockStartleft, catchBlockEndright, catchList);
    CatchClause catchClause = new CatchClause(catch_wordleft, catchBlockEndright, classNames, var, catchBlock);
    ((LinkedList) catchesList).addFirst(catchClause);
    int end = finallyBlock == null ? catchesListright : finallyBlockright;
    TryStatement tryStatement = new TryStatement(startleft, end, tryBlock, catchesList, finallyBlock);
    RESULT = tryStatement;
:}

| T_GOTO:s T_STRING:label T_SEMICOLON:e
{:
    RESULT = new GotoStatement(sleft, eright, new Identifier(labelleft, labelright, label));
:}

| error:theError /* error statement */
{:
    ASTError error = new ASTError(theErrorleft, theErrorright);
    RESULT = error;
:}

| T_VAR_COMMENT:varComment
{:
    // TODO: var comment should be added as parser.ast node
:}
;

additional_catches ::=
non_empty_additional_catches:list
{:
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

non_empty_additional_catches ::=
additional_catch:catch_statement
{:
    List list = new LinkedList();
    list.add(catch_statement);
    RESULT = list;
:}
| non_empty_additional_catches:list additional_catch:catch_statement
{:
    list.add(catch_statement);
    RESULT = list;
:}
;

additional_catch ::=
T_CATCH:catch_word T_OPEN_PARENTHESE catch_class_names:classNames optional_tracked_variable:variable T_CLOSE_PARENTHESE
 T_CURLY_OPEN:catchBlockStart inner_statement_list:catchList T_CURLY_CLOSE:catchBlockEnd
{:
    Block catchBlock = new Block(catchBlockStartleft, catchBlockEndright, catchList);
    CatchClause catchClause = new CatchClause(catch_wordleft, catchBlockEndright, classNames, variable, catchBlock);
    RESULT = catchClause;
:}
;

additional_finally ::=
 /* empty */
{:
    RESULT = null;
:}

| T_FINALLY:finally_word T_CURLY_OPEN:finallyBlockStart inner_statement_list:finallyList T_CURLY_CLOSE:finallyBlockEnd
{:
    Block finallyBlock = new Block(finallyBlockStartleft, finallyBlockEndright, finallyList);
    FinallyClause finallyClause = new FinallyClause(finally_wordleft, finallyBlockEndright, finallyBlock);
    RESULT = finallyClause;
:}
;

catch_class_names ::=
fully_qualified_class_name:className additional_catch_class_names:list
{:
    ((LinkedList) list).addFirst(className);
    RESULT = list;
:}
;

additional_catch_class_names ::=
non_empty_additional_catch_class_names:list
{:
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

non_empty_additional_catch_class_names ::=
additional_catch_class_name:className
{:
    List list = new LinkedList();
    list.add(className);
    RESULT = list;
:}

| non_empty_additional_catch_class_names:list additional_catch_class_name:className
{:
    list.add(className);
    RESULT = list;
:}
;

additional_catch_class_name ::=
T_OR fully_qualified_class_name:className
{:
    RESULT = className;
:}
;

unset_variables ::=
unset_variable:var
{:
    List list = new LinkedList();
    list.add(var);
    RESULT = list;
:}

| unset_variables:list T_COMMA unset_variable:var
{:
    list.add(var);
    RESULT = list;
:}
;

unset_variable ::=
variable:var
{:
    RESULT = var;
:}
;

use_filename ::=
T_CONSTANT_ENCAPSED_STRING:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, scalar, Scalar.Type.STRING);
    RESULT = s;
:}

| T_OPEN_PARENTHESE:start T_CONSTANT_ENCAPSED_STRING:scalar T_CLOSE_PARENTHESE:end
{:
    Scalar s = new Scalar(startleft, endright, scalar, Scalar.Type.STRING);
    RESULT = s;
:}
;

function_declaration_statement ::=
unticked_function_declaration_statement:functionDeclaration
{:
    RESULT = functionDeclaration;
:}
;

class_declaration_statement ::=
unticked_class_declaration_statement:classDeclaration
{:
    RESULT = classDeclaration;
:}
;

is_reference ::=
/* empty */
{:
    RESULT = Boolean.FALSE;
:}

| ampersand
{:
    RESULT = Boolean.TRUE;
:}
;

is_variadic ::=
/* empty */
{:
    RESULT = Boolean.FALSE;
:}

| T_ELLIPSIS
{:
    RESULT = Boolean.TRUE;
:}
;

unticked_function_declaration_statement ::=
T_FUNCTION:start is_reference:isReference string_st:functionName
T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE
optional_return_type:returnType
T_CURLY_OPEN:blockStart inner_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    Identifier functionId = new Identifier(functionNameleft, functionNameright, functionName);
    Block block = new Block(blockStartleft, blockEndright, statementList);
    FunctionDeclaration functionDeclaration = new FunctionDeclaration(startleft, blockEndright, functionId, paramList, returnType, block, isReference.booleanValue());
    RESULT = functionDeclaration;
:}
;

unticked_class_declaration_statement ::=
class_entry_type:modifiers T_STRING:className
extends_from:superClass implements_list:interfaces
T_CURLY_OPEN:blockStart class_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    Identifier classId = new Identifier(classNameleft, classNameright, className);
    Block block = new Block(blockStartleft, blockEndright, statementList);
    ClassDeclaration classDeclaration = new ClassDeclaration(modifiersleft ,blockEndright, modifiers, classId, superClass, interfaces, block);
    RESULT = classDeclaration;
:}

|
interface_entry:start T_STRING:className
interface_extends_list:interfaces
T_CURLY_OPEN:blockStart interface_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    Identifier classId = new Identifier(classNameleft, classNameright, className);
    Block block = new Block(blockStartleft, blockEndright, statementList);
    InterfaceDeclaration interfaceDeclaration = new InterfaceDeclaration(startleft ,blockEndright, classId, interfaces, block);
    RESULT = interfaceDeclaration;
:}

|
T_TRAIT:start T_STRING:traitName
T_CURLY_OPEN:blockStart trait_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    Identifier traitId = new Identifier(traitNameleft, traitNameright, traitName);
    Block block = new Block(blockStartleft, blockEndright, statementList);
    TraitDeclaration traitDeclaration = new TraitDeclaration(startleft, blockEndright, traitId, block);
    RESULT = traitDeclaration;
:}

|
T_ENUM:start T_STRING:enumName enum_backing_type:type implements_list:interfaces
T_CURLY_OPEN:blockStart class_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    Identifier name = new Identifier(enumNameleft, enumNameright, enumName);
    Block block = new Block(blockStartleft, blockEndright, statementList);
    EnumDeclaration enumDeclaration = new EnumDeclaration(startleft, blockEndright, name, type, interfaces, block);
    RESULT = enumDeclaration;
:}
;

interface_statement_list ::=
interface_statement_list:list interface_statement:interfaceStatement
{:
    list.add(interfaceStatement);
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

attributed_interface_statement ::=
constant_modifiers:modifier class_constant_declaration:list T_SEMICOLON:end
{:
    int constantStart = modifier == null ? listleft : modifierleft;
    modifier = modifier == null ? ASTPHP5Parser.IMPLICIT_PUBLIC : modifier;
    ConstantDeclaration classConstantDeclaration = ConstantDeclaration.create(constantStart, endright, modifier, list.first(), list.second(), false);
    RESULT = classConstantDeclaration;
:}

| method_modifiers:modifier T_FUNCTION:start is_reference:isReference identifier:functionId
T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE
optional_return_type:returnType
T_SEMICOLON:end
{:
    int methodStart = modifier == null ? startleft : modifierleft;
    modifier = modifier == null ? ASTPHP5Parser.PUBLIC : modifier;
    Block block = new Block(endleft, endright, Collections.EMPTY_LIST, false);
    FunctionDeclaration functionDeclaration = new FunctionDeclaration(startleft, endright, functionId, paramList, returnType, block, isReference.booleanValue());
    MethodDeclaration methodDeclaration = new MethodDeclaration(methodStart, endright, modifier.intValue(), functionDeclaration, true);
    RESULT = methodDeclaration;
:}
;

interface_statement ::=
attributed_interface_statement:statement
{:
    RESULT = statement;
:}

| attributes:attributes attributed_interface_statement:statement
{:
    RESULT = parser.createAttributedStatement(statement, attributes);
:}

| T_VAR_COMMENT:varComment
{:

:}
;

trait_statement_list ::=
trait_statement_list:list trait_statement:traitStatement
{:
    list.add(traitStatement);
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

attributed_trait_statement ::=
attributed_class_statement:statement
{:
    RESULT = statement;
:}
;

trait_statement ::=
attributed_trait_statement:statement
{:
    RESULT = statement;
:}

| attributes:attributes attributed_trait_statement:statement
{:
    RESULT = parser.createAttributedStatement(statement, attributes);
:}

| T_VAR_COMMENT:varComment
{:

:}

| T_USE:s use_traits:list use_traits_body:body
{:
    RESULT = new UseTraitStatement(sleft, bodyright, list, body);
:}
;

class_modifiers ::=
class_modifier:modifier
{:
    Map<ClassDeclaration.Modifier, Set<OffsetRange>> modifiers = new EnumMap<>(ClassDeclaration.Modifier.class);
    Set<OffsetRange> offsetRanges = new HashSet<>();
    offsetRanges.add(new OffsetRange(modifierleft, modifierright));
    modifiers.put(modifier, offsetRanges);
    RESULT = modifiers;
:}

| class_modifiers:modifiers class_modifier:modifier
{:
    Set<OffsetRange> offsetRanges = modifiers.get(modifier);
    if (offsetRanges == null) {
        offsetRanges = new HashSet<>();
    }
    offsetRanges.add(new OffsetRange(modifierleft, modifierright));
    modifiers.put(modifier, offsetRanges);
    RESULT = modifiers;
:}
;

class_modifier ::=
T_ABSTRACT
{:
    RESULT = ClassDeclaration.Modifier.ABSTRACT;
:}

| T_FINAL
{:
    RESULT = ClassDeclaration.Modifier.FINAL;
:}

| T_READONLY
{:
    // PHP 8.2 gh-4725
    RESULT = ClassDeclaration.Modifier.READONLY;
:}
;

class_entry_type ::=
T_CLASS
{:
    Map<ClassDeclaration.Modifier, Set<OffsetRange>> modifiers = new EnumMap<>(ClassDeclaration.Modifier.class);
    modifiers.put(ClassDeclaration.Modifier.NONE, Collections.singleton(OffsetRange.NONE));
    RESULT = modifiers;
:}

| class_modifiers:modifiers T_CLASS
{:
    RESULT = modifiers;
:}
;

extends_from ::=
/* empty */
{:
    RESULT = null;
:}

| T_EXTENDS fully_qualified_class_name:className
{:
    RESULT = className;
:}
;

/* do nothing */
interface_entry ::=
    T_INTERFACE
;

interface_extends_list ::=
/* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}

| T_EXTENDS interface_list:list
{:
    RESULT = list;
:}
;

implements_list ::=
/* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}

| T_IMPLEMENTS interface_list:list
{:
    RESULT = list;
:}
;

interface_list ::=
fully_qualified_class_name:className
{:
    List list = new LinkedList();
    list.add(className);
    RESULT = list;
:}

| interface_list:list T_COMMA fully_qualified_class_name:className
{:
    list.add(className);
    RESULT = list;
:}
;

foreach_optional_arg ::=
/* empty */
{:
    RESULT = null;
:}

| T_DOUBLE_ARROW foreach_variable:var
{:
    RESULT = var;
:}
;

foreach_variable ::=
variable:var
{:
    RESULT = var;
:}

| ampersand:start variable:var
{:
    Reference reference = new Reference (startleft, varright, var);
    RESULT = reference;
:}

| T_LIST:start T_OPEN_PARENTHESE array_pair_list:varList T_CLOSE_PARENTHESE:end
{:
    ListVariable vars = new ListVariable(startleft, endright, varList, ListVariable.SyntaxType.OLD);
    RESULT = vars;
:}

| T_OPEN_RECT:start array_pair_list:varList T_CLOSE_RECT:end
{:
    ListVariable vars = new ListVariable(startleft, endright, varList, ListVariable.SyntaxType.NEW);
    RESULT = vars;
:}
;

for_statement ::=
statement:statement
{:
    RESULT = statement;
:}

| T_NEKUDOTAIM:start inner_statement_list:statementList T_ENDFOR T_SEMICOLON
{:
    Block block = new Block(startleft, statementListright, statementList, false);
    RESULT = block;
:}
;

foreach_statement ::=
statement:statement
{:
    RESULT = statement;
:}

| T_NEKUDOTAIM:start inner_statement_list:statementList T_ENDFOREACH T_SEMICOLON
{:
    Block block = new Block(startleft, statementListright, statementList, false);
    RESULT = block;
:}
;

declare_statement ::=
statement:statement
{:
    RESULT = statement;
:}

| T_NEKUDOTAIM:start inner_statement_list:statementList T_ENDDECLARE T_SEMICOLON
{:
    Block block = new Block(startleft, statementListright, statementList, false);
    RESULT = block;
:}
;

// this rule returns a pair of keys and values of directives to the declare statement
declare_list ::=
string_st:key T_EQUAL static_scalar:value
{:
    List listKeys = new LinkedList();
    List listValues = new LinkedList();

    Identifier id = new Identifier(keyleft, keyright, key);
    listKeys.add(id);
    listValues.add(value);

    List[] returnList = new List[] { listKeys, listValues };
    RESULT = returnList;
:}

| declare_list:lists T_COMMA string_st:key T_EQUAL static_scalar:value
{:
    Identifier id = new Identifier(keyleft, keyright, key);
    lists[0].add(id);
    lists[1].add(value);
    RESULT = lists;
:}
;

switch_case_list ::=
T_CURLY_OPEN:start case_list:caseList T_CURLY_CLOSE:end
{:
    Block block = new Block(startleft, endright, caseList);
    RESULT = block;
:}

| T_CURLY_OPEN:start T_SEMICOLON case_list:caseList T_CURLY_CLOSE:end
{:
    Block block = new Block(startleft, endright, caseList);
    RESULT = block;
:}

| T_NEKUDOTAIM:start case_list:caseList T_ENDSWITCH T_SEMICOLON:end
{:
    Block block = new Block(startleft, endright, caseList, false);
    RESULT = block;
:}

| T_NEKUDOTAIM:start T_SEMICOLON case_list:caseList T_ENDSWITCH T_SEMICOLON:end
{:
    Block block = new Block(startleft, endright, caseList, false);
    RESULT = block;
:}
;

case_list ::=
/* empty */
{:
    RESULT = new LinkedList(); // of SwitchCase
:}

| case_list:caseList T_CASE:token expr:expr case_separator inner_statement_list:statements
{:
    SwitchCase switchCase = new SwitchCase(tokenleft, statementsright, expr, statements, false);
    if (caseList == null) {
        caseList = new LinkedList(); // of switchCase
    }
    caseList.add(switchCase);
    RESULT = caseList;
:}

| case_list:caseList T_DEFAULT:token case_separator inner_statement_list:statements
{:
    SwitchCase switchCase = new SwitchCase(tokenleft, statementsright, null, statements, true);
    if (caseList == null) {
        caseList = new LinkedList(); // of SwitchCase
    }
    caseList.add(switchCase);
    RESULT = caseList;
:}
;

/* Note: we don't capture seperator type */
case_separator ::=
T_NEKUDOTAIM
| T_SEMICOLON
;

while_statement ::=
statement:statement
{:
    RESULT = statement;
:}

| T_NEKUDOTAIM:colon inner_statement_list:statementList T_ENDWHILE T_SEMICOLON
{:
    Block block = new Block(colonleft, statementListright, statementList, false);
    RESULT = block;
:}
;

elseif_list ::=
/* empty */
{:
    List listConditions = new LinkedList();
    List listStatements = new LinkedList();
    List listTokens = new LinkedList();

    List[] returnList = new List[] { listConditions, listStatements, listTokens };

    RESULT = returnList;
:}

| elseif_list:elseifList T_ELSEIF:token T_OPEN_PARENTHESE expr:condition T_CLOSE_PARENTHESE statement:iftrue
{:
    ((LinkedList)elseifList[0]).addFirst(condition);
    ((LinkedList)elseifList[1]).addFirst(iftrue);
    ((LinkedList)elseifList[2]).addFirst(Integer.valueOf(tokenleft));

    RESULT = elseifList;
:}
;

new_elseif_list ::=
/* empty */
{:
    List listConditions = new LinkedList();
    List listStatements = new LinkedList();
    List listTokens = new LinkedList();

    List[] returnList = new List[] { listConditions, listStatements, listTokens };

    RESULT = returnList;
:}

| new_elseif_list:elseifList T_ELSEIF:token T_OPEN_PARENTHESE expr:condition T_CLOSE_PARENTHESE T_NEKUDOTAIM:colon inner_statement_list:statementList
{:
    Block block = new Block(colonleft, statementListright, statementList, false);
    ((LinkedList)elseifList[0]).addFirst(condition);
    ((LinkedList)elseifList[1]).addFirst(block);
    ((LinkedList)elseifList[2]).addFirst(Integer.valueOf(tokenleft));

    RESULT = elseifList;
:}
;

else_single ::=
/* empty */
{:
    RESULT = null;
:}

| T_ELSE statement:statement
{:
    RESULT = statement;
:}
;

new_else_single ::=
/* empty */
{:
    RESULT = null;
:}

| T_ELSE T_NEKUDOTAIM:colon inner_statement_list:statementList
{:
    Block block = new Block(colonleft, statementListright, statementList, false);
    RESULT = block;
:}
;

parameter_list ::=
non_empty_parameter_list:list possible_comma
{:
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

non_empty_parameter_list ::=
attributed_parameter:parameter
{:
    List list = new LinkedList();
    list.add(parameter);
    RESULT = list;
:}

| non_empty_parameter_list:list T_COMMA attributed_parameter:parameter
{:
    list.add(parameter);
    RESULT = list;
:}
;

attributed_parameter ::=
attributes:attributes parameter:param
{:
    RESULT = FormalParameter.create(param, attributes);
:}

| parameter:param
{:
    RESULT = param;
:}
;

optional_property_modifiers ::=
/* empty */
{:
    RESULT = null;
:}

| ppp_modifiers:pModifier
{:
    RESULT = pModifier;
:}

| ppp_set_modifiers:pModifier
{:
    RESULT = pModifier;
:}

| readonly_modifier:modifier
{:
    /* e.g. readonly int $property = 1, */
    int result = 0;
    result |= ASTPHP5Parser.IMPLICIT_PUBLIC.intValue();
    result |= modifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_modifiers:pModifier readonly_modifier:rModifier
{:
    int result = 0;
    result |= pModifier.intValue();
    result |= rModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| readonly_modifier:rModifier ppp_modifiers:pModifier
{:
    int result = 0;
    result |= rModifier.intValue();
    result |= pModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_modifiers:pppModifier ppp_set_modifiers:pppSetModifier
{:
    int result = 0;
    result |= pppModifier.intValue();
    result |= pppSetModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_set_modifiers:pppSetModifier ppp_modifiers:pppModifier
{:
    int result = 0;
    result |= pppSetModifier.intValue();
    result |= pppModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_set_modifiers:pppSetModifier readonly_modifier:rModifier
{:
    int result = 0;
    result |= pppSetModifier.intValue();
    result |= rModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| readonly_modifier:rModifier ppp_set_modifiers:pppSetModifier
{:
    int result = 0;
    result |= rModifier.intValue();
    result |= pppSetModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_modifiers:pppModifier ppp_set_modifiers:pppSetModifier readonly_modifier:rModifier
{:
    int result = 0;
    result |= pppModifier.intValue();
    result |= pppSetModifier.intValue();
    result |= rModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_modifiers:pppModifier readonly_modifier:rModifier ppp_set_modifiers:pppSetModifier
{:
    int result = 0;
    result |= pppModifier.intValue();
    result |= rModifier.intValue();
    result |= pppSetModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_set_modifiers:pppSetModifier ppp_modifiers:pppModifier readonly_modifier:rModifier
{:
    int result = 0;
    result |= pppSetModifier.intValue();
    result |= pppModifier.intValue();
    result |= rModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| ppp_set_modifiers:pppSetModifier readonly_modifier:rModifier ppp_modifiers:pppModifier
{:
    int result = 0;
    result |= pppSetModifier.intValue();
    result |= rModifier.intValue();
    result |= pppModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| readonly_modifier:rModifier ppp_modifiers:pppModifier ppp_set_modifiers:pppSetModifier
{:
    int result = 0;
    result |= rModifier.intValue();
    result |= pppModifier.intValue();
    result |= pppSetModifier.intValue();
    RESULT = Integer.valueOf(result);
:}

| readonly_modifier:rModifier ppp_set_modifiers:pppSetModifier ppp_modifiers:pppModifier
{:
    int result = 0;
    result |= rModifier.intValue();
    result |= pppSetModifier.intValue();
    result |= pppModifier.intValue();
    RESULT = Integer.valueOf(result);
:}
;

readonly_modifier ::=
T_READONLY
{:
    RESULT = ASTPHP5Parser.READONLY;
:}
;

parameter ::=
optional_property_modifiers:modifier optional_class_type_without_static:classType is_reference:isReference is_variadic:isVariadic T_VARIABLE:var
{:
    int start = varleft;
    if (isVariadic) {
        start = isVariadicleft;
    }
    if (isReference) {
        start = isReferenceleft;
    }
    if (classType != null) {
        start = classTypeleft;
    }
    if (modifier != null) {
        start = modifierleft;
    }
    Variable v = new Variable(varleft, varright, var);
    Expression argument = v;
    if (isVariadic) {
        argument = new Variadic(isVariadicleft, varright, v);
    }
    if (isReference) {
        if (isVariadic) {
            argument = new Reference(isReferenceleft, varright, (Variadic) argument);
        } else {
            argument = new Reference(isReferenceleft, varright, v);
        }
    }
    FormalParameter parameter = new FormalParameter(start, varright, modifier, classType, argument);
    RESULT = parameter;
:}

| optional_property_modifiers:modifier optional_class_type_without_static:classType is_reference:isReference is_variadic:isVariadic T_VARIABLE:var T_EQUAL static_scalar_with_class_instance:expr
{:
    int start = varleft;
    if (isVariadic) {
        start = isVariadicleft;
    }
    if (isReference) {
        start = isReferenceleft;
    }
    if (classType != null) {
        start = classTypeleft;
    }
    if (modifier != null) {
        start = modifierleft;
    }
    Variable v = new Variable(varleft, varright, var);
    Expression argument = v;
    if (isVariadic) {
        argument = new Variadic(isVariadicleft, varright, v);
    }
    if (isReference) {
        if (isVariadic) {
            argument = new Reference(isReferenceleft, varright, (Variadic) argument);
        } else {
            argument = new Reference(isReferenceleft, varright, v);
        }
    }
    FormalParameter parameter = new FormalParameter(start, exprright, modifier, classType, argument, expr);
    RESULT = parameter;
:}
;

optional_return_type ::=
/* empty */
{:
    RESULT = null;
:}

| T_NEKUDOTAIM:e type_expr:type
{:
    RESULT = type;
:}
;

optional_class_type_without_static ::=
/* empty */
{:
    RESULT = null;
:}

| type_expr_without_static:type
{:
    RESULT = type;
:}
;

type_expr ::=
class_type:type
{:
    RESULT = type;
:}

| T_QUESTION_MARK:start class_type:type
{:
    RESULT = new NullableType(startleft, typeright, type);
:}

| union_type:list
{:
    RESULT = new UnionType(listleft, listright, list);
:}

| intersection_type:list
{:
    RESULT = new IntersectionType(listleft, listright, list);
:}
;

class_type ::=
class_type_without_static:type
{:
    RESULT = type;
:}

| T_STATIC:s
{:
    Identifier classId = new Identifier(sleft, sright, "static");
    RESULT = classId;
:}
;

class_type_without_static ::=
fully_qualified_class_name:className
{:
    RESULT = className;
:}

| T_ARRAY:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_CALLABLE:callable
{:
    Identifier classId = new Identifier(callableleft, callableright, "callable");
    RESULT = classId;
:}
;

union_type_element ::=
class_type:type
{:
    RESULT = type;
:}

| T_OPEN_PARENTHESE intersection_type:type T_CLOSE_PARENTHESE
{:
    RESULT = new IntersectionType(typeleft, typeright, type);
:}
;

union_type ::=
union_type_element:type1 T_OR union_type_element:type2
{:
    List<Expression> list = new ArrayList<>();
    list.add(type1);
    list.add(type2);
    RESULT = list;
:}

| union_type:list T_OR union_type_element:type
{:
    list.add(type);
    RESULT = list;
:}
;

intersection_type ::=
class_type:type1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG class_type:type2
{:
    List list = new ArrayList<>();
    list.add(type1);
    list.add(type2);
    RESULT = list;
:}

| intersection_type:list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG class_type:type
{:
    list.add(type);
    RESULT = list;
:}
;

type_expr_without_static ::=
class_type_without_static:type
{:
    RESULT = type;
:}

| T_QUESTION_MARK:start class_type_without_static:type
{:
    RESULT = new NullableType(startleft, typeright, type);
:}

| union_type_without_static:list
{:
    RESULT = new UnionType(listleft, listright, list);
:}

| intersection_type_without_static:list
{:
    RESULT = new IntersectionType(listleft, listright, list);
:}
;

union_type_without_static_element ::=
class_type_without_static:type
{:
    RESULT = type;
:}

| T_OPEN_PARENTHESE intersection_type_without_static:type T_CLOSE_PARENTHESE
{:
    RESULT = new IntersectionType(typeleft, typeright, type);
:}
;

union_type_without_static ::=
union_type_without_static_element:type1 T_OR union_type_without_static_element:type2
{:
    List<Expression> list = new ArrayList<>();
    list.add(type1);
    list.add(type2);
    RESULT = list;
:}

| union_type_without_static:list T_OR union_type_without_static_element:type
{:
    list.add(type);
    RESULT = list;
:}
;

intersection_type_without_static ::=
class_type_without_static:type1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG class_type_without_static:type2
{:
    List list = new ArrayList<>();
    list.add(type1);
    list.add(type2);
    RESULT = list;
:}

| intersection_type_without_static:list T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG class_type_without_static:type
{:
    list.add(type);
    RESULT = list;
:}
;

function_call_parameter_list ::=
non_empty_function_call_parameter_list:paramsList possible_comma
{:
    RESULT = paramsList;
:}

| T_ELLIPSIS:e
{:
    // NETBEANS-5899 PHP 8.1: First class callable syntax
    // e.g. strlen(...);
    RESULT = Collections.singletonList(new FirstClassCallableArg(eleft, eright));
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

non_empty_function_call_parameter_list ::=
argument:arg
{:
    List paramsList = new LinkedList();
    paramsList.add(arg);
    RESULT = paramsList;
:}

| non_empty_function_call_parameter_list:paramsList T_COMMA argument:arg
{:
    paramsList.add(arg);
    RESULT = paramsList;
:}
;

argument ::=
argument_expr:var
{:
    RESULT = var;
:}

| T_ELLIPSIS:e argument_expr:var
{:
    Expression param = var;
    param = new Variadic(eleft, varright, var);
    RESULT = param;
:}

/*
 * if identifier is used instead of string_st, T_CLASS, semi_reserved_without_class,
 * conflict with namespace_name (identifier:ident T_NEKUDOTAIM argument_expr:var)
 */
| string_st:ident T_NEKUDOTAIM argument_expr:var
{:
    Identifier identifier = new Identifier(identleft, identright, ident);
    Expression param = new NamedArgument(identleft, varright, identifier, var);
    RESULT = param;
:}

| T_CLASS:ident T_NEKUDOTAIM argument_expr:var
{:
    Identifier identifier = new Identifier(identleft, identright, ident);
    Expression param = new NamedArgument(identleft, varright, identifier, var);
    RESULT = param;
:}

| semi_reserved_without_class:ident T_NEKUDOTAIM argument_expr:var
{:
    Expression param = new NamedArgument(identleft, varright, ident, var);
    RESULT = param;
:}

| T_REFERENCE:start w_variable:var
{:
    // Call-time pass-by-reference has been removed since PHP 5.4.0
    Expression var_ref = new Reference(startleft, varright, var);
    RESULT = var_ref;
:}

| error:expr
{:
    RESULT = new ASTErrorExpression(exprleft, exprright);
:}
;

argument_expr ::=
expr_without_variable:var
{:
    RESULT = var;
:}

| variable:var
{:
    RESULT = var;
:}
;

global_var_list ::=
global_var_list:list T_COMMA global_var:var
{:
    list.add(var);
    RESULT = list;
:}

| global_var:var
{:
    List list = new LinkedList();
    list.add(var);
    RESULT = list;
:}
;

global_var ::=
T_VARIABLE:var
{:
    Variable variable = new Variable(varleft, varright, var);
    RESULT = variable;
:}

| T_DOLLAR:start r_variable:var
{:
    ReflectionVariable ref = new ReflectionVariable(startleft, varright, var);
    RESULT = ref;
:}

| T_DOLLAR:start T_CURLY_OPEN expr:varName T_CURLY_CLOSE:end
{:
    ReflectionVariable var = new ReflectionVariable(startleft, endright, varName);
    RESULT = var;
:}
;

static_var_list ::=
static_var_list:list T_COMMA T_VARIABLE:var
{:
    Variable v = new Variable(varleft, varright, var);
    list.add(v);
    RESULT = list;
:}

| static_var_list:list T_COMMA T_VARIABLE:var T_EQUAL expr:expr
{:
    Variable v = new Variable(varleft, varright, var);
    Assignment assignment = new Assignment(varleft, exprright, v, Assignment.Type.EQUAL, expr);
    list.add(assignment);
    RESULT = list;
:}

| T_VARIABLE:var
{:
    Variable v = new Variable(varleft, varright, var);
    List list = new LinkedList();
    list.add(v);
    RESULT = list;
:}

| T_VARIABLE:var T_EQUAL expr:expr
{:
    Variable v = new Variable(varleft, varright, var);
    Assignment assignment = new Assignment(varleft, exprright, v, Assignment.Type.EQUAL, expr);
    List list = new LinkedList();
    list.add(assignment);
    RESULT = list;
:}
;

class_statement_list ::=
class_statement_list:list class_statement:classStatement
{:
    list.add(classStatement);
    RESULT = list;
:}

| /* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}
;

attributed_class_statement ::=
variable_modifiers:modifier optional_class_type_without_static:fieldType class_variable_declaration:decList T_SEMICOLON:end
{:
    if (!BodyDeclaration.Modifier.isVisibilityModifier(modifier.intValue())) {
        // implicit public
        int mod = modifier.intValue();
        mod |= ASTPHP5Parser.IMPLICIT_PUBLIC.intValue();
        modifier = Integer.valueOf(mod);
    }
    FieldsDeclaration fieldsDeclaration = new FieldsDeclaration(modifierleft, endright, modifier.intValue(), fieldType, decList);
    RESULT = fieldsDeclaration;
:}

| constant_modifiers:modifier class_constant_declaration:list T_SEMICOLON:end
{:
    int constantStart = modifier == null ? listleft : modifierleft;
    modifier = modifier == null ? ASTPHP5Parser.IMPLICIT_PUBLIC : modifier;
    ConstantDeclaration classConstantDeclaration = ConstantDeclaration.create(constantStart, endright, modifier, list.first(), list.second(), false);
    RESULT = classConstantDeclaration;
:}

| method_modifiers:modifier T_FUNCTION:start is_reference:isReference identifier:functionId
T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE
optional_return_type:returnType
method_body:body
{:
    int methodStart = modifier == null ? startleft : modifierleft;
    modifier = modifier == null ? ASTPHP5Parser.PUBLIC : modifier;
    FunctionDeclaration functionDeclaration = new FunctionDeclaration(startleft, bodyright, functionId, paramList, returnType, body, isReference.booleanValue());
    MethodDeclaration methodDeclaration = new MethodDeclaration(methodStart, bodyright, modifier.intValue(), functionDeclaration, true);
    RESULT = methodDeclaration;
:}

| enum_case:e
{:
    RESULT = e;
:}
;

enum_backing_type ::=
/* empty */
{:
    RESULT = null;
:}

| T_NEKUDOTAIM:e type_expr:type
{:
    RESULT = type;
:}
;

enum_case ::=
T_CASE:s identifier:name enum_case_expr:expr T_SEMICOLON:end
{:
    RESULT = new CaseDeclaration(sleft, endright, name, expr);
:}
;

enum_case_expr ::=
/* empty */
{:
    RESULT = null;
:}

| T_EQUAL:e expr:expr
{:
    RESULT = expr;
:}

| T_EQUAL:e error:expr
{:
    RESULT = new ASTErrorExpression(exprleft, exprright);
:}
;

class_statement ::=
attributed_class_statement:statement
{:
    RESULT = statement;
:}

| attributes:attributes attributed_class_statement:statement
{:
    RESULT = parser.createAttributedStatement(statement, attributes);
:}

| T_VAR_COMMENT:varComment
{:

:}

| T_USE:s use_traits:list use_traits_body:body
{:
    RESULT = new UseTraitStatement(sleft, bodyright, list, body);
:}
;

use_traits ::=
use_traits:list T_COMMA use_trait:useDecl
{:
    list.add(useDecl);
    RESULT = list;
:}

| use_trait:useDecl
{:
    List list = new LinkedList();
    list.add(useDecl);
    RESULT = list;
:}
;

use_trait ::=
legacy_namespace_name:name
{:
    RESULT = new UseTraitStatementPart(nameleft, nameright, name);
:}
;

use_traits_body ::=
T_SEMICOLON:e
{:
    RESULT = null;
:}

| T_CURLY_OPEN:start use_traits_body_statement_list:statementList T_CURLY_CLOSE:end
{:
    Block block = new Block(startleft, endright, statementList);
    RESULT = block;
:}
;

use_traits_body_statement_list ::=
use_traits_body_statement_list:statementList use_traits_body_statement:statement
{:
    // Ignore null statements
    if(statement != null) {
        statementList.add(statement);
    }
    RESULT = statementList;
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

use_traits_body_statement ::=
trait_conflict_resolution_declaration:statement
{:
    RESULT = statement;
:}

| trait_method_alias_declaration:statement
{:
    RESULT = statement;
:}
;

class_name_list ::=
class_name_list:classNameList T_COMMA class_name:className
{:
    classNameList.add(className);
    RESULT = classNameList;
:}
| class_name:className
{:
    List classNameList = new LinkedList();
    classNameList.add(className);
    RESULT = classNameList;
:}
;

trait_conflict_resolution_declaration ::=
class_name:preferredTraitName T_PAAMAYIM_NEKUDOTAYIM string_st:methodName T_INSTEADOF class_name_list:suppressedTraitNames T_SEMICOLON:end
{:
    Identifier methodId = new Identifier(methodNameleft, methodNameright, methodName);
    RESULT = new TraitConflictResolutionDeclaration(preferredTraitNameleft, endright, preferredTraitName, methodId, suppressedTraitNames);
:}
;

trait_method_alias_declaration ::=
class_name:traitName T_PAAMAYIM_NEKUDOTAYIM string_st:oldMethodName T_AS traits_alias_modifier:modifier string_st:newMethodName T_SEMICOLON:end
{:
    Identifier oldMethodId = new Identifier(oldMethodNameleft, oldMethodNameright, oldMethodName);
    Identifier newMethodId = new Identifier(newMethodNameleft, newMethodNameright, newMethodName);
    RESULT = new TraitMethodAliasDeclaration(traitNameleft, endright, oldMethodId, newMethodId, traitName, modifier);
:}

| class_name:traitName T_PAAMAYIM_NEKUDOTAYIM string_st:oldMethodName T_AS string_st:newMethodName T_SEMICOLON:end
{:
    Identifier oldMethodId = new Identifier(oldMethodNameleft, oldMethodNameright, oldMethodName);
    Identifier newMethodId = new Identifier(newMethodNameleft, newMethodNameright, newMethodName);
    RESULT = new TraitMethodAliasDeclaration(traitNameleft, endright, oldMethodId, newMethodId, traitName, null);
:}

| string_st:oldMethodName T_AS traits_alias_modifier:modifier T_SEMICOLON:end
{:
    Identifier oldMethodId = new Identifier(oldMethodNameleft, oldMethodNameright, oldMethodName);
    Identifier newMethodId = new Identifier(oldMethodNameleft, oldMethodNameright, oldMethodName);
    RESULT = new TraitMethodAliasDeclaration(oldMethodNameleft, endright, oldMethodId, newMethodId, null, modifier);
:}

| string_st:oldMethodName T_AS traits_alias_modifier:modifier string_st:newMethodName T_SEMICOLON:end
{:
    Identifier oldMethodId = new Identifier(oldMethodNameleft, oldMethodNameright, oldMethodName);
    Identifier newMethodId = new Identifier(newMethodNameleft, newMethodNameright, newMethodName);
    RESULT = new TraitMethodAliasDeclaration(oldMethodNameleft, endright, oldMethodId, newMethodId, null, modifier);
:}

| string_st:oldMethodName T_AS string_st:newMethodName T_SEMICOLON:end
{:
    Identifier oldMethodId = new Identifier(oldMethodNameleft, oldMethodNameright, oldMethodName);
    Identifier newMethodId = new Identifier(newMethodNameleft, newMethodNameright, newMethodName);
    RESULT = new TraitMethodAliasDeclaration(oldMethodNameleft, endright, oldMethodId, newMethodId, null, null);
:}
;

traits_alias_modifier ::=
T_PUBLIC
{:
    RESULT = TraitMethodAliasDeclaration.Modifier.PUBLIC;
:}

| T_PROTECTED
{:
    RESULT = TraitMethodAliasDeclaration.Modifier.PROTECTED;
:}

| T_PRIVATE
{:
    RESULT = TraitMethodAliasDeclaration.Modifier.PRIVATE;
:}
;

method_body ::=
T_SEMICOLON /* abstract method */
{:
    RESULT = null;
:}

| T_CURLY_OPEN:start inner_statement_list:statementList T_CURLY_CLOSE:end
{:
    Block block = new Block(startleft, endright, statementList);
    RESULT = block;
:}
;

constant_modifiers ::=
/* empty */
{:
    RESULT = null;
:}

| non_empty_member_modifiers:modifier
{:
    RESULT = modifier;
:}
;

variable_modifiers ::=
non_empty_member_modifiers:modifier
{:
    RESULT = modifier;
:}

| T_VAR
{:
    RESULT = ASTPHP5Parser.PUBLIC;
:}
;

method_modifiers ::=
/* empty */
{:
    RESULT = null;
:}

| non_empty_member_modifiers:modifier
{:
    RESULT = modifier;
:}
;

member_modifiers ::=
ppp_modifiers:modifier
{:
    RESULT = modifier;
:}

| ppp_set_modifiers:modifier
{:
    RESULT = modifier;
:}

| static_modifier:modifier
{:
    RESULT = modifier;
:}

| readonly_modifier:modifier
{:
    RESULT = modifier;
:}

| final_modifier:modifier
{:
    RESULT = modifier;
:}

| abstract_modifier:modifier
{:
    RESULT = modifier;
:}
;

non_empty_member_modifiers ::=
member_modifiers:modifier
{:
    RESULT = modifier;
:}

| non_empty_member_modifiers:nonEmptyModifier member_modifiers:modifier
{:
    int result = nonEmptyModifier.intValue();
    result |= modifier.intValue();
    RESULT = Integer.valueOf(result);
:}
;

abstract_modifier ::=
T_ABSTRACT
{:
    RESULT = ASTPHP5Parser.ABSTRACT;
:}
;

final_modifier ::=
T_FINAL
{:
    RESULT = ASTPHP5Parser.FINAL;
:}
;

static_modifier ::=
T_STATIC
{:
    RESULT = ASTPHP5Parser.STATIC;
:}
;

ppp_modifiers ::=
T_PUBLIC
{:
    RESULT = ASTPHP5Parser.PUBLIC;
:}

| T_PROTECTED
{:
    RESULT = ASTPHP5Parser.PROTECTED;
:}

| T_PRIVATE
{:
    RESULT = ASTPHP5Parser.PRIVATE;
:}
;

ppp_set_modifiers ::=
T_PUBLIC_SET
{:
    RESULT = ASTPHP5Parser.PUBLIC_SET;
:}

| T_PROTECTED_SET
{:
    RESULT = ASTPHP5Parser.PROTECTED_SET;
:}

| T_PRIVATE_SET
{:
    RESULT = ASTPHP5Parser.PRIVATE_SET;
:}
;

af_modifiers ::=
abstract_modifier:modifier
{:
    RESULT = modifier;
:}

| final_modifier:modifier
{:
    RESULT = modifier;
:}
;

class_variable_declaration ::=
class_variable_declaration:list T_COMMA T_VARIABLE:var
{:
    Variable varId = new Variable(varleft, varright, var);
    list.add(new ASTNode[] {varId, null});
    RESULT = list;
:}

| class_variable_declaration:list T_COMMA T_VARIABLE:var T_EQUAL static_scalar:expr
{:
    Variable varId = new Variable(varleft, varright, var);
    list.add(new ASTNode[] {varId, expr});
    RESULT = list;
:}

| T_VARIABLE:var
{:
    List list = new LinkedList();
    Variable varId = new Variable(varleft, varright, var);
    list.add(new ASTNode[] {varId, null});
    RESULT = list;
:}

| T_VARIABLE:var T_EQUAL static_scalar:expr
{:
    List list = new LinkedList();
    Variable varId = new Variable(varleft, varright, var);
    list.add(new ASTNode[] {varId, expr});
    RESULT = list;
:}
;

constant_declaration ::=
constant_declaration:list T_COMMA string_st:constName T_EQUAL static_scalar_value:expr
{:
    Identifier constId = new Identifier(constNameleft, constNameright, constName);
    list.add(new ASTNode[] {constId, expr});
    RESULT = list;
:}

| T_CONST string_st:constName T_EQUAL static_scalar_value_with_class_instance:expr
{:
    List list = new LinkedList();
    Identifier constId = new Identifier(constNameleft, constNameright, constName);
    list.add(new ASTNode[] {constId, expr});
    RESULT = list;
:}
;

class_constant_declaration ::=
class_constant_declaration:list T_COMMA identifier_without_class:constName T_EQUAL static_scalar_value:expr
{:
    list.second().add(new ASTNode[] {constName, expr});
    RESULT = list;
:}

| T_CONST identifier_without_class:constId T_EQUAL static_scalar_value:expr
{:
    List<ASTNode[]> list = new LinkedList<>();
    list.add(new ASTNode[] {constId, expr});
    RESULT = Pair.of(null, list);
:}

| T_CONST type_expr:constType identifier_without_class:constId T_EQUAL static_scalar_value:expr
{:
    List<ASTNode[]> list = new LinkedList<>();
    list.add(new ASTNode[] {constId, expr});
    RESULT = Pair.of(constType, list);
:}
;

echo_expr_list ::=
echo_expr_list:exprList T_COMMA expr:expr
{:
    exprList.add(expr);
    RESULT = exprList;
:}
| expr:expr
{:
    List exprList = new LinkedList();
    exprList.add(expr);
    RESULT = exprList;
:}
;

for_expr ::=
/* empty */
{:
    RESULT = new LinkedList();
:}

| non_empty_for_expr:exprList
{:
    RESULT = exprList;
:}
;

non_empty_for_expr ::=
non_empty_for_expr:exprList T_COMMA expr:expr
{:
    exprList.add(expr);
    RESULT = exprList;
:}

| expr:expr
{:
    List exprList = new LinkedList();
    exprList.add(expr);
    RESULT = exprList;
:}
;

expr_without_variable ::=
expr_without_variable_and_class_instance:ex
{:
    RESULT = ex;
:}

| anonymous_class:ex
{:
    RESULT = ex;
:}

| T_NEW:start class_name_reference:className ctor_arguments:ctor
{:
    ClassInstanceCreation classInstanceCreation = new ClassInstanceCreation(startleft, ctorright, className, ctor);
    RESULT = classInstanceCreation;
:}

| T_CLONE:start expr:expr
{:
    CloneExpression clone = new CloneExpression(startleft, exprright, expr);
    RESULT = clone;
:}
;

expr_without_variable_and_class_instance ::=
T_LIST:start T_OPEN_PARENTHESE array_pair_list:varList T_CLOSE_PARENTHESE:close T_EQUAL expr:expr
{:
    ListVariable vars = new ListVariable(startleft, closeright, varList, ListVariable.SyntaxType.OLD);
    Assignment list = new Assignment(startleft, exprright, vars, Assignment.Type.EQUAL, expr);
    RESULT = list;
:}

| T_OPEN_RECT:start array_pair_list:varList T_CLOSE_RECT:close T_EQUAL expr:expr
{:
    ListVariable vars = new ListVariable(startleft, closeright, varList, ListVariable.SyntaxType.NEW);
    Assignment list = new Assignment(startleft, exprright, vars, Assignment.Type.EQUAL, expr);
    RESULT = list;
:}

| variable:var T_EQUAL expr_with_yields:expr
{:
    RESULT = new Assignment(varleft, exprright, var, Assignment.Type.EQUAL, expr);
:}

| variable:var T_EQUAL ampersand:reftoken variable:refvar
{:
    RESULT = new Assignment(varleft, refvarright, var, Assignment.Type.EQUAL, new Reference(reftokenleft, refvarright, refvar));
:}

| variable:var T_EQUAL ampersand:reftoken T_NEW:start class_name_reference:className ctor_arguments:ctor
{:
    ClassInstanceCreation classInstanceCreation = new ClassInstanceCreation(startleft, ctorright, className, ctor);
    Reference reference = new Reference(reftokenleft, ctorright, classInstanceCreation);
    Assignment assignment = new Assignment(varleft, ctorright, var, Assignment.Type.EQUAL, reference);
    RESULT = assignment;
:}

| variable:var T_POW_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.POW_EQUAL, expr);
:}

| variable:var T_PLUS_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.PLUS_EQUAL, expr);
:}

| variable:var T_MINUS_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.MINUS_EQUAL, expr);
:}

| variable:var T_MUL_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.MUL_EQUAL, expr);
:}

| variable:var T_DIV_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.DIV_EQUAL, expr);
:}

| variable:var T_CONCAT_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.CONCAT_EQUAL, expr);
:}

| variable:var T_MOD_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.MOD_EQUAL, expr);
:}

| variable:var T_AND_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.AND_EQUAL, expr);
:}

| variable:var T_OR_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.OR_EQUAL, expr);
:}

| variable:var T_XOR_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.XOR_EQUAL, expr);
:}

| variable:var T_SL_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.SL_EQUAL, expr);
:}

| variable:var T_SR_EQUAL expr:expr
{:
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.SR_EQUAL, expr);
:}

| variable:var T_COALESCE_EQUAL expr:expr
{:
    // PHP 7.4 Null Coalescing Assignment Operator
    // https://wiki.php.net/rfc/null_coalesce_equal_operator
    RESULT = new Assignment(varleft, exprright, var , Assignment.Type.COALESCE_EQUAL, expr);
:}

| rw_variable:var T_INC:token
{:
    RESULT = new PostfixExpression(varleft, tokenright, var , PostfixExpression.Operator.INC);
:}

| T_INC:token rw_variable:var
{:
    RESULT = new PrefixExpression(tokenleft, varright, var , PrefixExpression.Operator.INC);
:}

| rw_variable:var T_DEC:token
{:
    RESULT = new PostfixExpression(varleft, tokenright, var , PostfixExpression.Operator.DEC);
:}

| T_DEC:token rw_variable:var
{:
    RESULT = new PrefixExpression(tokenleft, varright, var , PrefixExpression.Operator.DEC);
:}

| expr:expr1 T_BOOLEAN_OR expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.BOOL_OR, expr2);
:}

| expr:expr1 T_BOOLEAN_AND expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.BOOL_AND, expr2);
:}

| expr:expr1 T_LOGICAL_OR expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.STRING_OR, expr2);
:}

| expr:expr1 T_LOGICAL_AND expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.STRING_AND, expr2);
:}

| expr:expr1 T_LOGICAL_XOR expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.STRING_XOR, expr2);
:}

| expr:expr1 T_OR expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.OR, expr2);
:}

| expr:expr1 T_REFERENCE expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.AND, expr2);
:}

| expr:expr1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.AND, expr2);
:}

| expr:expr1 T_KOVA expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.XOR, expr2);
:}

| expr:expr1 T_NEKUDA expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.CONCAT, expr2);
:}

| expr:expr1 T_POW expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.POW, expr2);
:}

| expr:expr1 T_PLUS expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.PLUS, expr2);
:}

| expr:expr1 T_MINUS expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.MINUS, expr2);
:}

| expr:expr1 T_TIMES expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.MUL, expr2);
:}

| expr:expr1 T_DIV expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.DIV, expr2);
:}

| expr:expr1 T_PRECENT expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.MOD, expr2);
:}

| expr:expr1 T_SL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.SL, expr2);
:}

| expr:expr1 T_SR expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.SR, expr2);
:}

| T_PLUS:token expr:expr
{:
    RESULT = new UnaryOperation(tokenleft, exprright, expr , UnaryOperation.Operator.PLUS);
:}

| T_MINUS:token expr:expr
{:
    RESULT = new UnaryOperation(tokenleft, exprright, expr , UnaryOperation.Operator.MINUS);
:}

| T_NOT:token expr:expr
{:
    RESULT = new UnaryOperation(tokenleft, exprright, expr , UnaryOperation.Operator.NOT);
:}

| T_TILDA:token expr:expr
{:
    RESULT = new UnaryOperation(tokenleft, exprright, expr , UnaryOperation.Operator.TILDA);
:}

| expr:expr1 T_IS_IDENTICAL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_IDENTICAL, expr2);
:}

| expr:expr1 T_IS_NOT_IDENTICAL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_NOT_IDENTICAL, expr2);
:}

| expr:expr1 T_IS_EQUAL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_EQUAL, expr2);
:}

| expr:expr1 T_IS_NOT_EQUAL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_NOT_EQUAL, expr2);
:}

| expr:expr1 T_RGREATER expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.RGREATER, expr2);
:}

| expr:expr1 T_IS_SMALLER_OR_EQUAL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_SMALLER_OR_EQUAL, expr2);
:}

| expr:expr1 T_LGREATER expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.LGREATER, expr2);
:}

| expr:expr1 T_IS_GREATER_OR_EQUAL expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_GREATER_OR_EQUAL, expr2);
:}

| expr:expr1 T_SPACESHIP expr:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.SPACESHIP, expr2);
:}

| expr:expr T_INSTANCEOF class_name_reference:className
{:
    RESULT = new InstanceOfExpression(exprleft, classNameright, expr , className);
:}

| parenthesis_expr:expr
{:
    RESULT = expr;
:}

| expr:condition T_QUESTION_MARK expr:ifTrue T_NEKUDOTAIM expr:ifFalse
{:
    RESULT = new ConditionalExpression(conditionleft, ifFalseright, condition, ConditionalExpression.OperatorType.QUESTION_MARK, ifTrue, ifFalse);
:}

| expr:cond T_QUESTION_MARK T_NEKUDOTAIM expr:ifFalse
{:
    RESULT = new ConditionalExpression(condleft, ifFalseright, cond, ConditionalExpression.OperatorType.ELVIS, null, ifFalse);
:}

| expr:cond T_COALESCE expr:ifFalse
{:
    RESULT = new ConditionalExpression(condleft, ifFalseright, cond, ConditionalExpression.OperatorType.COALESCE, null, ifFalse);
:}

| internal_functions_in_yacc:expr
{:
    RESULT = expr;
:}

| T_INT_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.INT);
:}

| T_DOUBLE_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.FLOAT);
:}

| T_STRING_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.STRING);
:}

| T_ARRAY_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.ARRAY);
:}

| T_OBJECT_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.OBJECT);
:}

| T_BOOL_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.BOOL);
:}

| T_UNSET_CAST:token expr:expr
{:
    RESULT = new CastExpression(tokenleft, exprright, expr , CastExpression.Type.UNSET);
:}

| T_EXIT:start exit_expr:expr
{:
    List expList = new LinkedList();
    if (expr != null) {
        expList.add(expr);
    }
    Identifier id = new Identifier(startleft, startright, start);
    FunctionName name = new FunctionName(startleft, startright, id);
    FunctionInvocation result = new FunctionInvocation(startleft, exprright, name, expList);
    RESULT = result;
:}

| T_AT:start expr:expr
{:
    IgnoreError ignoreError = new IgnoreError(startleft, exprright, expr);
    RESULT = ignoreError;
:}

| scalar:scalar
{:
    RESULT = scalar;
:}

| array_creation:array
{:
    RESULT = array;
:}

| T_BACKQUATE:start encaps_list:list T_BACKQUATE:end
{:
    BackTickExpression backTickExpression = new BackTickExpression(startleft, endright, list);
    RESULT = backTickExpression;
:}

| T_PRINT:start expr:expr
{:
    List expList = new LinkedList();
    if (expr != null) {
        expList.add(expr);
    }
    Identifier id = new Identifier(startleft, startright, "print");
    FunctionName name = new FunctionName(startleft, startright, id);
    FunctionInvocation result = new FunctionInvocation(startleft, exprright, name, expList);
    RESULT = result;
:}

| inline_function:inline
{:
    RESULT = inline;
:}

| attributes:attributes inline_function:inline
{:
    Expression attributedExpression = inline;
    if (inline instanceof ArrowFunctionDeclaration) {
        attributedExpression = ArrowFunctionDeclaration.create((ArrowFunctionDeclaration) inline, attributes);
    } else if (inline instanceof LambdaFunctionDeclaration) {
        attributedExpression = LambdaFunctionDeclaration.create((LambdaFunctionDeclaration) inline, attributes);
    } else {
        assert false;
    }
    RESULT = attributedExpression;
:}

| expression_array_access:eaa
{:
    RESULT = eaa;
:}

| T_THROW:token expr:expr
{:
    // PHP 8.0: https://wiki.php.net/rfc/throw_expression
    RESULT = new ThrowExpression(tokenleft, exprright, expr);
:}

| match:match
{:
    // PHP 8.0: https://wiki.php.net/rfc/match_expression_v2
    RESULT = match;
:}
;

inline_function ::=
T_FUNCTION:s is_reference:isReference
T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE
lexical_vars:varsList
optional_return_type:returnType
T_CURLY_OPEN:blockStart inner_statement_list:list T_CURLY_CLOSE:blockEnd
{:
    RESULT = new LambdaFunctionDeclaration(sleft, blockEndright, paramList, returnType, varsList,
        new Block(blockStartleft, blockEndright, list), isReference.booleanValue(), false);
:}

| T_STATIC:st T_FUNCTION:s is_reference:isReference
T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE
lexical_vars:varsList
optional_return_type:returnType
T_CURLY_OPEN:blockStart inner_statement_list:list T_CURLY_CLOSE:blockEnd
{:
    RESULT = new LambdaFunctionDeclaration(sleft, blockEndright, paramList, returnType, varsList,
        new Block(blockStartleft, blockEndright, list), isReference.booleanValue(), true);
:}

| T_FN:s is_reference:isReference T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE optional_return_type:returnType T_DOUBLE_ARROW expr_with_yields_and_error:expr
{:
    // PHP 7.4
    RESULT = new ArrowFunctionDeclaration(sleft, exprright, paramList, returnType, expr, isReference.booleanValue(), false);
:}

| T_STATIC:st T_FN:s is_reference:isReference T_OPEN_PARENTHESE parameter_list:paramList T_CLOSE_PARENTHESE optional_return_type:returnType T_DOUBLE_ARROW expr_with_yields_and_error:expr
{:
    // PHP 7.4
    RESULT = new ArrowFunctionDeclaration(stleft, exprright, paramList, returnType, expr, isReference.booleanValue(), true);
:}
;

match ::=
T_MATCH:token T_OPEN_PARENTHESE expr:expr T_CLOSE_PARENTHESE T_CURLY_OPEN:curly match_arm_list:list T_CURLY_CLOSE:end
{:
    RESULT = new MatchExpression(tokenleft, endright, new OffsetRange(curlyleft, endright), expr, list);
:}
;

match_arm_list ::=
/* empty */
{:
    RESULT = new ArrayList<>();
:}

| non_empty_match_arm_list:list possible_comma
{:
    RESULT = list;
:}
;

non_empty_match_arm_list ::=
match_arm:arm
{:
    List<MatchArm> list = new ArrayList<>();
    list.add(arm);
    RESULT = list;
:}

| non_empty_match_arm_list:list T_COMMA match_arm:arm
{:
    list.add(arm);
    RESULT = list;
:}
;

match_arm ::=
match_arm_condition_list:conditions possible_comma T_DOUBLE_ARROW expr_with_error:expr
{:
    RESULT = new MatchArm(conditionsleft, exprright, conditions, expr, false);
:}

| T_DEFAULT:token possible_comma T_DOUBLE_ARROW expr_with_error:expr
{:
    List<Expression> conditions = new ArrayList<>();
    conditions.add(new Identifier(tokenleft, tokenright, "default"));
    RESULT = new MatchArm(tokenleft, exprright, conditions, expr, true);
:}
;

match_arm_condition_list ::=
expr_with_error:expr
{:
    List<Expression> list = new ArrayList<>();
    list.add(expr);
    RESULT = list;
:}

| match_arm_condition_list:list T_COMMA expr_with_error:expr
{:
    list.add(expr);
    RESULT = list;
:}
;

lexical_vars ::=
/* empty */
{:
    RESULT = null;
:}

| T_USE:s T_OPEN_PARENTHESE lexical_var_list:list possible_comma T_CLOSE_PARENTHESE:e
{:
    RESULT = list;
:}
;

lexical_var_list ::=
lexical_var_list:list T_COMMA T_VARIABLE:var
{:
    Variable v = new Variable(varleft, varright, var);
    list.add(v);
    RESULT = list;
:}

| lexical_var_list:list T_COMMA ampersand:ref T_VARIABLE:var
{:
    list.add(new Reference (refleft, varright, new Variable(varleft, varright, var)));
    RESULT = list;
:}

| T_VARIABLE:var
{:
    List list = new LinkedList();
    list.add(new Variable(varleft, varright, var));
    RESULT = list;
:}

| ampersand:ref T_VARIABLE:var
{:
    List list = new LinkedList();
    list.add(new Reference (refleft, varright, new Variable(varleft, varright, var)));
    RESULT = list;
:}
;

function_call ::=
fully_qualified_class_name:list T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new FunctionInvocation(listleft, eright,
        new FunctionName(listleft, listright, list), parameters);
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new StaticMethodInvocation(classNameleft, eright, className,
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters));
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM variable_without_objects:reflectionName T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new StaticMethodInvocation(classNameleft, eright, className,
        new FunctionInvocation(reflectionNameleft, eright,
            new FunctionName(reflectionNameleft, reflectionNameright, reflectionName), parameters));
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:o expr:expr T_CURLY_CLOSE:c T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    ReflectionVariable reflectionVariable = new ReflectionVariable(oleft, cright, expr);
    RESULT = new StaticMethodInvocation(classNameleft, eright, className,
        new FunctionInvocation(oleft, eright,
            new FunctionName(oleft, cright, reflectionVariable), parameters));
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_OBJECT_OPERATOR identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // Name::CASE1->method();
    RESULT = new MethodInvocation(classNameleft, eright, new StaticConstantAccess(classNameleft, constantright, className, constant),
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters), false);
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_NULLSAFE_OBJECT_OPERATOR identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // Name::CASE1?->method();
    RESULT = new MethodInvocation(classNameleft, eright, new StaticConstantAccess(classNameleft, constantright, className, constant),
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters), true);
:}

| enum_constant:enumConst T_PAAMAYIM_NEKUDOTAYIM identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // Name::CASE1::CASE2::staticMethod();
    RESULT = new StaticMethodInvocation(enumConstleft, eright, enumConst,
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters));
:}

| enum_constant:enumConst T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_OBJECT_OPERATOR identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // Name::CASE1::CASE2->method();
    RESULT = new MethodInvocation(enumConstleft, eright, new StaticConstantAccess(enumConstleft, constantright, enumConst, constant),
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters), false);
:}

| enum_constant:enumConst T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_NULLSAFE_OBJECT_OPERATOR identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // Name::CASE1::CASE2?->method();
    RESULT = new MethodInvocation(enumConstleft, eright, new StaticConstantAccess(enumConstleft, constantright, enumConst, constant),
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters), true);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new StaticMethodInvocation(classNameleft, eright, className,
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters));
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_OBJECT_OPERATOR identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // $i::CONSTANT->method();
    RESULT = new MethodInvocation(classNameleft, eright, new StaticConstantAccess(classNameleft, constantright, className, constant),
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters), false);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_NULLSAFE_OBJECT_OPERATOR identifier:fn T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    // $i::CONSTANT?->method();
    RESULT = new MethodInvocation(classNameleft, eright, new StaticConstantAccess(classNameleft, constantright, className, constant),
        new FunctionInvocation(fnleft, eright,
            new FunctionName(fnleft, fnright, fn), parameters), true);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM variable_without_objects:reflectionName T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new StaticMethodInvocation(classNameleft, eright, className,
        new FunctionInvocation(reflectionNameleft, eright,
            new FunctionName(reflectionNameleft, reflectionNameright, reflectionName), parameters));
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:o expr:expr T_CURLY_CLOSE:c T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    ReflectionVariable reflectionVariable = new ReflectionVariable(oleft, cright, expr);
    RESULT = new StaticMethodInvocation(classNameleft, eright, className,
        new FunctionInvocation(oleft, eright,
            new FunctionName(oleft, cright, reflectionVariable), parameters));
:}

| variable_without_objects:reflectionName T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new FunctionInvocation(reflectionNameleft, eright,
        new FunctionName(reflectionNameleft, reflectionNameright, reflectionName), parameters);
:}

| callable_expr:start T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new FunctionInvocation(startleft, eright,
        new FunctionName(startleft, startright, start), parameters);
:}

| function_call:var array_dimension:ad
{:
    RESULT = new DereferencedArrayAccess(varleft, adright, var, ad);
:}

| function_call:call T_OPEN_PARENTHESE function_call_parameter_list:parameters T_CLOSE_PARENTHESE:e
{:
    RESULT = new FunctionInvocation(callleft, eright,
        new FunctionName(callleft, callright, call), parameters);
:}
;

callable_expr ::=
parenthesis_expr:expr
{:
    RESULT = expr;
:}

| dereferencable_variable:dereferencableVariable
{:
    RESULT = dereferencableVariable;
:}

| field_or_method_access:fma
{:
    RESULT = fma;
:}

| T_OPEN_PARENTHESE:start anonymous_class:cls T_CLOSE_PARENTHESE:end
{:
    AnonymousObjectVariable anonymous= new AnonymousObjectVariable(startleft, endright, cls);
    RESULT = anonymous;
:}

| T_OPEN_PARENTHESE:start T_NEW:n class_name_reference:className ctor_arguments:ctor T_CLOSE_PARENTHESE:end
{:
    ClassInstanceCreation classInstanceCreation = new ClassInstanceCreation(nleft, ctorright, className, ctor);
    AnonymousObjectVariable anonymous = new AnonymousObjectVariable(startleft, endright, classInstanceCreation);
    RESULT = anonymous;
:}

// PHP 8.4 new MyClass()->method() without parentheses
// https://wiki.php.net/rfc/new_without_parentheses
| new_expr:expr
{:
    // new Example()();
    // new class(){}();
    RESULT = new ClassInstanceCreationVariable(expr);
:}

| T_CONSTANT_ENCAPSED_STRING:scalar
{:
    RESULT = new Scalar(scalarleft, scalarright, scalar, Scalar.Type.STRING);
:}

| array_creation:array
{:
    RESULT = array;
:}
;

class_name ::=
T_STATIC:s
{:
    RESULT = new Identifier(sleft, sright, "static");
:}

| fully_qualified_class_name:name
{:
    RESULT = name;
:}
;


fully_qualified_class_name ::=
T_STRING:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_DEFINE:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_NAME_QUALIFIED:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_NAME_FULLY_QUALIFIED:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}

| T_NAME_RELATIVE:name
{:
    RESULT = NamespaceName.create(nameleft, nameright, name);
:}
;

class_name_reference ::=
class_name:className
{:
    RESULT = new ClassName(classNameleft, classNameright, className);
:}

| dynamic_class_name_reference:className
{:
    RESULT = className;
:}
;

dynamic_class_name_reference ::=
base_variable:var T_OBJECT_OPERATOR object_property:firstVarProperty dynamic_class_name_variable_properties:propertyList
{:
    ClassName name = parser.createClassName(var, firstVarProperty, propertyList, varleft, propertyListright, ASTPHP5Parser.Access.NON_STATIC);
    RESULT = name;
:}

| base_variable:var T_NULLSAFE_OBJECT_OPERATOR object_property:firstVarProperty dynamic_class_name_variable_properties:propertyList
{:
    ClassName name = parser.createClassName(var, firstVarProperty, propertyList, varleft, propertyListright, ASTPHP5Parser.Access.NULLSAFE);
    RESULT = name;
:}

| base_variable:var
{:
     ClassName name = new ClassName(varleft, varright, var);
     RESULT = name;
:}

| parenthesis_expr:expr
{:
    ClassName name = new ClassName(exprleft, exprright, expr);
    RESULT = name;
:}

;

dynamic_class_name_variable_properties ::=
dynamic_class_name_variable_properties:variables dynamic_class_name_variable_property:var
{:
    variables.add(var);
    RESULT = variables;
:}

| /* empty */
{:
    RESULT = new LinkedList<Pair<VariableBase, ASTPHP5Parser.Access>>();
:}
;

dynamic_class_name_variable_property ::=
T_OBJECT_OPERATOR object_property:var
{:
    RESULT = Pair.of(var, ASTPHP5Parser.Access.NON_STATIC);
:}

| T_NULLSAFE_OBJECT_OPERATOR object_property:var
{:
    RESULT = Pair.of(var, ASTPHP5Parser.Access.NULLSAFE);
:}
;

exit_expr ::=
/* empty */
{:
    RESULT = null;
:}

| T_OPEN_PARENTHESE T_CLOSE_PARENTHESE
{:
    RESULT = null;
:}

| T_OPEN_PARENTHESE expr:expr T_CLOSE_PARENTHESE
{:
    RESULT = expr;
:}
;

ctor_arguments ::=
/* empty */
{:
    RESULT = new LinkedList();
:}

| T_OPEN_PARENTHESE function_call_parameter_list:paramsList T_CLOSE_PARENTHESE
{:
    RESULT = paramsList;
:}
;

common_scalar ::=
T_LNUMBER:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, scalar, Scalar.Type.INT);
    RESULT = s;
:}

| T_DNUMBER:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, scalar, Scalar.Type.FLOAT);
    RESULT = s;
:}

| T_CONSTANT_ENCAPSED_STRING:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, scalar, Scalar.Type.STRING);
    RESULT = s;
:}

| T_LINE:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, "__LINE__", Scalar.Type.SYSTEM);
    RESULT = s;
:}

| T_FILE:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, "__FILE__", Scalar.Type.SYSTEM);
    RESULT = s;
:}

| T_DIR:scalar
{:
    RESULT = new Scalar(scalarleft, scalarright, "__DIR__", Scalar.Type.SYSTEM);
:}

| T_CLASS_C:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, "__CLASS__", Scalar.Type.SYSTEM);
    RESULT = s;
:}

| T_TRAIT_C:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, "__TRAIT__", Scalar.Type.SYSTEM);
    RESULT = s;
:}

| T_METHOD_C:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, "__METHOD__", Scalar.Type.SYSTEM);
    RESULT = s;
:}

| T_FUNC_C:scalar
{:
    Scalar s = new Scalar(scalarleft, scalarright, "__FUNCTION__", Scalar.Type.SYSTEM);
    RESULT = s;
:}

| T_NS_C:scalar
{:
    RESULT = new Scalar(scalarleft, scalarright, "__NAMESPACE__", Scalar.Type.SYSTEM);
:}

| T_START_NOWDOC:start T_ENCAPSED_AND_WHITESPACE:scalar T_END_NOWDOC:end
{:
    Scalar s = new Scalar(startleft, endright, scalar, Scalar.Type.STRING);
    RESULT = s;
:}

| T_START_NOWDOC:start T_END_NOWDOC:end
{:
    Scalar s = new Scalar(startleft, endright, "", Scalar.Type.STRING); //NOI18N
    RESULT = s;
:}
;

static_scalar_value ::=
common_scalar:scalar
{:
    RESULT = scalar;
:}

| namespace_name_access:nsn
{:
    List<Identifier> list = nsn.getSegments();
    if (!nsn.isGlobal() && list.size() == 1) {
        String itemName = ((Identifier) list.get(0)).getName();
        String itemNameLower = itemName.toLowerCase();
        if ("true".equals(itemNameLower) || "false".equals(itemNameLower) || "null".equals(itemNameLower)) { // NOI18N
            RESULT = new Scalar(nsnleft, nsnright, itemName, Scalar.Type.STRING);
        } else {
            RESULT = nsn;
        }
    } else {
        RESULT = nsn;
    }
:}

| static_class_constant:classConstant
{:
    RESULT = classConstant;
:}

| heredoc:doc
{:
    RESULT = doc;
:}

| static_operation:operation
{:
    RESULT = operation;
:}
;

/* PHP 8.1: New in initializer */
static_scalar_value_with_class_instance ::=
static_scalar_value:scalar
{:
    RESULT = scalar;
:}

| T_NEW:n class_name_reference:className ctor_arguments:ctor
{:
    RESULT = new ClassInstanceCreation(nleft, ctorright, className, ctor);
:}
;

static_operation ::=
/*static_scalar_value T_OPEN_RECT static_scalar_value T_CLOSE_RECT
{:
    RESULT = ???;
:}

| */

static_array_creation:arrayCreation
{:
    RESULT = arrayCreation;
:}

| static_array_creation_with_access:arrayAccess
{:
    RESULT = arrayAccess;
:}

| static_constant_array_access:arrayAccess
{:
    RESULT = arrayAccess;
:}

| T_ENCAPSED_AND_WHITESPACE:str array_dimension_with_static_scalar_value:ad
{:
    RESULT = new ExpressionArrayAccess(strleft, adright, new Identifier(strleft, strright, str), ad);
:}

| T_CONSTANT_ENCAPSED_STRING:str array_dimension_with_static_scalar_value:ad
{:
    RESULT = new ExpressionArrayAccess(strleft, adright, new Identifier(strleft, strright, str), ad);
:}

| static_scalar_value:expr1 T_PLUS static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1, InfixExpression.OperatorType.PLUS, expr2);
:}

| static_scalar_value:expr1 T_MINUS static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1, InfixExpression.OperatorType.MINUS, expr2);
:}

| static_scalar_value:expr1 T_TIMES static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1, InfixExpression.OperatorType.MUL, expr2);
:}

| static_scalar_value:expr1 T_POW static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1, InfixExpression.OperatorType.POW, expr2);
:}

| static_scalar_value:expr1 T_DIV static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1, InfixExpression.OperatorType.DIV, expr2);
:}

| static_scalar_value:expr1 T_PRECENT static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1, InfixExpression.OperatorType.MOD, expr2);
:}

| T_NOT:token static_scalar_value:expr
{:
    RESULT = new UnaryOperation(tokenleft, exprright, expr , UnaryOperation.Operator.NOT);
:}

| T_TILDA:token static_scalar_value:expr
{:
    RESULT = new UnaryOperation(tokenleft, exprright, expr , UnaryOperation.Operator.TILDA);
:}

| static_scalar_value:expr1 T_OR static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.OR, expr2);
:}

| static_scalar_value:expr1 T_REFERENCE static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.AND, expr2);
:}

| static_scalar_value:expr1 T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.AND, expr2);
:}

| static_scalar_value:expr1 T_KOVA static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.XOR, expr2);
:}

| static_scalar_value:expr1 T_SL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.SL, expr2);
:}

| static_scalar_value:expr1 T_SR static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.SR, expr2);
:}

| static_scalar_value:expr1 T_NEKUDA static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.CONCAT, expr2);
:}

| static_scalar_value:expr1 T_LOGICAL_XOR static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.STRING_XOR, expr2);
:}

| static_scalar_value:expr1 T_LOGICAL_AND static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.STRING_AND, expr2);
:}

| static_scalar_value:expr1 T_LOGICAL_OR static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.STRING_OR, expr2);
:}

| static_scalar_value:expr1 T_BOOLEAN_AND static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.BOOL_AND, expr2);
:}

| static_scalar_value:expr1 T_BOOLEAN_OR static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.BOOL_OR, expr2);
:}

| static_scalar_value:expr1 T_IS_IDENTICAL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_IDENTICAL, expr2);
:}

| static_scalar_value:expr1 T_IS_NOT_IDENTICAL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_NOT_IDENTICAL, expr2);
:}

| static_scalar_value:expr1 T_IS_EQUAL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_EQUAL, expr2);
:}

| static_scalar_value:expr1 T_IS_NOT_EQUAL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_NOT_EQUAL, expr2);
:}

| static_scalar_value:expr1 T_RGREATER static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.RGREATER, expr2);
:}

| static_scalar_value:expr1 T_LGREATER static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.LGREATER, expr2);
:}

| static_scalar_value:expr1 T_IS_SMALLER_OR_EQUAL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_SMALLER_OR_EQUAL, expr2);
:}

| static_scalar_value:expr1 T_IS_GREATER_OR_EQUAL static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.IS_GREATER_OR_EQUAL, expr2);
:}

| static_scalar_value:expr1 T_SPACESHIP static_scalar_value:expr2
{:
    RESULT = new InfixExpression(expr1left, expr2right, expr1 , InfixExpression.OperatorType.SPACESHIP, expr2);
:}

| static_scalar_value:cond T_QUESTION_MARK T_NEKUDOTAIM static_scalar_value:ifFalse
{:
    RESULT = new ConditionalExpression(condleft, ifFalseright, cond, ConditionalExpression.OperatorType.ELVIS, null, ifFalse);
:}

| static_scalar_value:condition T_QUESTION_MARK static_scalar_value:ifTrue T_NEKUDOTAIM static_scalar_value:ifFalse
{:
    RESULT = new ConditionalExpression(conditionleft, ifFalseright, condition, ConditionalExpression.OperatorType.QUESTION_MARK, ifTrue, ifFalse);
:}

| static_scalar_value:cond T_COALESCE static_scalar_value:ifFalse
{:
    RESULT = new ConditionalExpression(condleft, ifFalseright, cond, ConditionalExpression.OperatorType.COALESCE, null, ifFalse);
:}

| T_PLUS:start static_scalar_value:expr
{:
    UnaryOperation op = new UnaryOperation(startleft, exprright, expr, UnaryOperation.Operator.PLUS);
    RESULT = op;
:}

| T_MINUS:start static_scalar_value:expr
{:
    UnaryOperation op = new UnaryOperation(startleft, exprright, expr, UnaryOperation.Operator.MINUS);
    RESULT = op;
:}

| T_OPEN_PARENTHESE static_scalar_value:expr T_CLOSE_PARENTHESE
{:
    RESULT = expr;
:}
;

static_scalar ::=  /* compile-time evaluated scalars */
static_scalar_value:scalar
{:
    RESULT = scalar;
:}
;

/* PHP 8.1: New in initializer */
static_scalar_with_class_instance ::=
static_scalar_value_with_class_instance:scalar
{:
    RESULT = scalar;
:}
;

static_class_constant ::=
class_name:className T_PAAMAYIM_NEKUDOTAYIM static_class_constant_array_access:constant
{:
    RESULT = new StaticConstantAccess(classNameleft, constantright, className, constant);
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant
{:
    RESULT = new StaticConstantAccess(classNameleft, constantright, className, constant);
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    ReflectionVariable reflectionVariable = new ReflectionVariable(startleft, endright, expr);
    RESULT = new StaticConstantAccess(classNameleft, endright, className, reflectionVariable, true);
:}

// GH-4725 PHP 8.2: Fetch properties of enums in const expressions
// https://wiki.php.net/rfc/fetch_property_in_const_expressions
// e.g.
// enum E {
//     case Foo;
// }
// const C1 = E::Foo->name;
// const C2 = E::Foo;
// const C3 = C2->name;
| class_name:className T_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(classNameleft, varNameright, new ConstantVariable(className),new Variable(varNameleft, varNameright, varName.getName()), false);
:}

| class_name:className T_NULLSAFE_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(classNameleft, varNameright, new ConstantVariable(className),new Variable(varNameleft, varNameright, varName.getName()), true);
:}

| static_class_constant:constant T_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(constantleft, varNameright, constant,
            new Variable(varNameleft, varNameright, varName.getName()), false);
:}

| static_class_constant:constant T_NULLSAFE_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(constantleft, varNameright, constant,
            new Variable(varNameleft, varNameright, varName.getName()), true);
:}

| static_class_constant:constant T_OBJECT_OPERATOR T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    RESULT = new FieldAccess(constantleft, endright, constant,
            new ReflectionVariable(startleft, endright, expr), false);
:}

| static_class_constant:constant T_NULLSAFE_OBJECT_OPERATOR T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    RESULT = new FieldAccess(constantleft, endright, constant,
            new ReflectionVariable(startleft, endright, expr), true);
:}

| static_class_constant:constant T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    RESULT = new StaticConstantAccess(constantleft, endright, constant,
            new ReflectionVariable(startleft, endright, expr), true);
:}
;

static_reference_constant ::=
class_constant_array_access:arrayAccess
{:
    RESULT = arrayAccess;
:}

| identifier:constantName
{:
    RESULT = constantName;
:}
;

class_constant_array_access ::=
class_constant_array_access:arrayAccess T_OPEN_RECT:o expr:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(arrayAccessleft, endright, arrayAccess, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}

| identifier:constantName T_OPEN_RECT:o expr:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(constantNameleft, endright, constantName, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}
;

static_class_constant_array_access ::=
static_class_constant_array_access:arrayAccess T_OPEN_RECT:o static_scalar_value:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(arrayAccessleft, endright, arrayAccess, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}

| identifier:constantName T_OPEN_RECT:o static_scalar_value:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(constantNameleft, endright, constantName, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}
;

static_constant_array_access ::=
static_constant_array_access:arrayAccess T_OPEN_RECT:o static_scalar_value:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(arrayAccessleft, endright, arrayAccess, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}

| T_STRING:constantName T_OPEN_RECT:o static_scalar_value:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(constantNameleft, endright, new Identifier(constantNameleft, constantNameright, constantName), new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}

| namespace_name_access:namespace T_OPEN_RECT:o static_scalar_value:index T_CLOSE_RECT:end
{:
    RESULT = new ExpressionArrayAccess(namespaceleft, endright, namespace, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
:}
;

static_array_creation_with_access ::=
static_array_creation:arr array_dimension_with_static_scalar_value:ad
{:
    RESULT = new ExpressionArrayAccess(arrleft, adright, arr, ad);
:}

| static_array_creation_with_access:acc array_dimension_with_static_scalar_value:ad
{:
    RESULT = new ExpressionArrayAccess(accleft, adright, acc, ad);
:}
;

static_array_creation ::=
T_ARRAY:start T_OPEN_PARENTHESE:o static_array_pair_list:list T_CLOSE_PARENTHESE:end
{:
    Expression expr = new ArrayCreation(startleft, endright, list, ArrayCreation.Type.OLD);
    RESULT = expr;
:}

| T_OPEN_RECT:start static_array_pair_list:list T_CLOSE_RECT:end
{:
    Expression expr = new ArrayCreation(startleft, endright, list, ArrayCreation.Type.NEW);
    RESULT = expr;
:}
;

scalar ::=
T_STRING_VARNAME:scalar
{:
    RESULT = new Scalar(scalarleft, scalarright, scalar, Scalar.Type.STRING);
:}

| class_constant:classConstant
{:
    RESULT = classConstant;
:}

| namespace_name_access:nsn
{:
    List<Identifier> list = nsn.getSegments();
    if (!nsn.isGlobal() && list.size() == 1) {
        String itemName = ((Identifier) list.get(0)).getName();
        String itemNameLower = itemName.toLowerCase();
        if ("true".equals(itemNameLower) || "false".equals(itemNameLower) || "null".equals(itemNameLower)) { // NOI18N
            RESULT = new Scalar(nsnleft, nsnright, itemName, Scalar.Type.STRING);
        } else {
            RESULT = nsn;
        }
    } else {
        RESULT = nsn;
    }
:}

| common_scalar:scalar
{:
    RESULT = scalar;
:}

| T_QUATE:start encaps_list:list T_QUATE:end
{:
    Quote quote = new Quote(startleft, endright, list, Quote.Type.QUOTE);
    RESULT = quote;
:}

| heredoc:doc
{:
    RESULT = doc;
:}
;

heredoc ::=
T_START_HEREDOC:start encaps_list:list T_END_HEREDOC:end
{:
    Quote hereDoc = new Quote(startleft, endright, list, Quote.Type.HEREDOC);
    RESULT = hereDoc;
:}
;

static_array_pair_list ::=
/* empty */
{:
    List list = new LinkedList();
    RESULT = list;
:}

| non_empty_static_array_pair_list:list possible_comma
{:
    RESULT = list;
:}
;

/* do nothing */
possible_comma ::=
/* empty */
| T_COMMA
;

ampersand ::=
T_REFERENCE
| T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG
;

non_empty_static_array_pair_list ::=
non_empty_static_array_pair_list:list T_COMMA static_scalar:key T_DOUBLE_ARROW static_scalar:value
{:
    ArrayElement element = new ArrayElement(keyleft, valueright, key, value);
    list.add(element);
    RESULT = list;
:}

| non_empty_static_array_pair_list:list T_COMMA static_scalar:value
{:
    ArrayElement element = new ArrayElement(valueleft, valueright, value);
    list.add(element);
    RESULT = list;
:}

| non_empty_static_array_pair_list:list T_COMMA T_ELLIPSIS:ell static_scalar:value
{:
    // PHP 7.4 Spread Operator In Array Expression
    UnpackableArrayElement element = new UnpackableArrayElement(ellleft, valueright, value);
    list.add(element);
    RESULT = list;
:}

| static_scalar:key T_DOUBLE_ARROW static_scalar:value
{:
    List list = new LinkedList();
    ArrayElement element = new ArrayElement(keyleft, valueright, key, value);
    list.add(element);
    RESULT = list;
:}

| T_ELLIPSIS:ell static_scalar:value
{:
    // PHP 7.4 Spread Operator In Array Expression
    List list = new LinkedList();
    UnpackableArrayElement element = new UnpackableArrayElement(ellleft, valueright, value);
    list.add(element);
    RESULT = list;
:}

| static_scalar:value
{:
    List list = new LinkedList();
    ArrayElement element = new ArrayElement(valueleft, valueright, value);
    list.add(element);
    RESULT = list;
:}
;

parenthesis_expr ::=
T_OPEN_PARENTHESE:start expr_without_class_instance:expr T_CLOSE_PARENTHESE:end
{:
    ParenthesisExpression parenthesisExpression = new ParenthesisExpression(startleft, endright, expr);
    RESULT = parenthesisExpression;
:}
;

yield_expr ::=
T_YIELD:s
{:
    RESULT = new YieldExpression(sleft, sright, null);
:}

| T_YIELD:s yield_expr:expr
{:
    RESULT = new YieldExpression(sleft, exprright, expr);
:}

| T_YIELD:s yield_from_expr:expr
{:
    RESULT = new YieldExpression(sleft, exprright, expr);
:}

| T_YIELD:s expr:expr
{:
    RESULT = new YieldExpression(sleft, exprright, expr);
:}

| T_YIELD:s expr:expr1 T_DOUBLE_ARROW expr:expr2
{:
    RESULT = new YieldExpression(sleft, expr2right, expr1, expr2);
:}

| T_OPEN_PARENTHESE:start yield_expr:expr T_CLOSE_PARENTHESE:end
{:
    ParenthesisExpression parenthesisExpression = new ParenthesisExpression(startleft, endright, expr);
    RESULT = parenthesisExpression;
:}
;

yield_from_expr ::=
T_YIELD_FROM:s expr:expr
{:
    RESULT = new YieldFromExpression(sleft, exprright, expr);
:}

| T_OPEN_PARENTHESE:start yield_from_expr:expr T_CLOSE_PARENTHESE:end
{:
    ParenthesisExpression parenthesisExpression = new ParenthesisExpression(startleft, endright, expr);
    RESULT = parenthesisExpression;
:}
;

expr_without_class_instance ::=
r_variable:var
{: RESULT = var; :}

| expr_without_variable_and_class_instance:ewv
{: RESULT = ewv; :}
;

expr ::=
r_variable:var
{: RESULT = var; :}

| expr_without_variable:ewv
{: RESULT = ewv; :}
;

expr_with_error ::=
expr:expr
{:
    RESULT = expr;
:}

| error:expr
{:
    RESULT = new ASTErrorExpression(exprleft, exprright);
:}
;

expr_with_yields ::=
expr:expr
{:
    RESULT = expr;
:}

| yield_expr:expr
{:
    RESULT = expr;
:}

| yield_from_expr:expr
{:
    RESULT = expr;
:}
;

expr_with_yields_and_error ::=
expr_with_yields:expr
{:
    RESULT = expr;
:}

| error:expr
{:
    RESULT = new ASTErrorExpression(exprleft, exprright);
:}
;

r_variable ::=
variable:var
{: RESULT = var; :}
;

w_variable ::=
variable:var
{: RESULT = var; :}
;

rw_variable ::=
variable:var
{: RESULT = var; :}
;

field_or_method_access ::=
base_variable_with_function_calls:var T_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.NON_STATIC, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| base_variable_with_function_calls:var T_NULLSAFE_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.NULLSAFE, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

// PHP 8.1: New in initializers
// const C = new Example();
// C->field;
// C->method();
| class_name:className T_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.NON_STATIC, new ConstantVariable(className), memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| class_name:className T_NULLSAFE_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.NULLSAFE, new ConstantVariable(className), memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

//| base_variable_with_function_calls:var T_PAAMAYIM_NEKUDOTAYIM object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
| function_call:var T_PAAMAYIM_NEKUDOTAYIM static_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.STATIC, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| base_variable_without_reference_variable:var T_PAAMAYIM_NEKUDOTAYIM static_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.STATIC, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| parenthesis_expr:pe T_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    // e.g. ($uvs = new UVS())->method();
    DereferencableVariable var = new DereferencableVariable(peleft, peright, pe.getExpression());
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.NON_STATIC, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| parenthesis_expr:pe T_NULLSAFE_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    // e.g. ($uvs = new UVS())?->method();
    DereferencableVariable var = new DereferencableVariable(peleft, peright, pe.getExpression());
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.NULLSAFE, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| parenthesis_expr:pe T_PAAMAYIM_NEKUDOTAYIM static_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    // e.g. ($uvs = new UVS())::staticMethod();
    DereferencableVariable var = new DereferencableVariable(peleft, peright, pe.getExpression());
    RESULT = parser.createDispatch(ASTPHP5Parser.Access.STATIC, var, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

// Backed cases have an additional read-only property (value)
// e.g. EnumName::CASE_NAME->value;
// see https://www.php.net/manual/en/language.enumerations.backed.php
| class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(classNameleft, varNameright, new StaticConstantAccess(classNameleft, constantright, className, constant),
            new Variable(varNameleft, varNameright, varName.getName()), false);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(classNameleft, varNameright, new StaticConstantAccess(classNameleft, constantright, className, constant),
            new Variable(varNameleft, varNameright, varName.getName()), false);
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_NULLSAFE_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(classNameleft, varNameright, new StaticConstantAccess(classNameleft, constantright, className, constant),
            new Variable(varNameleft, varNameright, varName.getName()), true);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant T_NULLSAFE_OBJECT_OPERATOR identifier:varName
{:
    RESULT = new FieldAccess(classNameleft, varNameright, new StaticConstantAccess(classNameleft, constantright, className, constant),
            new Variable(varNameleft, varNameright, varName.getName()), true);
:}

// PHP 8.4 new MyClass()->method() without parentheses
// https://wiki.php.net/rfc/new_without_parentheses
| new_expr:expr access_operator:operator object_property:memberProperty method_or_not:paramsList array_access_or_not:aa variable_properties:propertyList
{:
    // new Example()->method()
    // new Example()->method()::staticMethod();
    // new class(){}->method();
    // new class(){}->method()::staticMethod();
    // new Example()?->method()
    // new Example()?->method()::staticMethod();
    // new class(){}?->method();
    // new class(){}?->method()::staticMethod();
    // new Example()::CONSTANT;
    // new class(){}::CONSTANT;
    RESULT = parser.createDispatch(operator, new ClassInstanceCreationVariable(expr), memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, propertyList, aa);
:}

| new_expr:expr access_operator:operator
{:
    // for code completion
    // e.g. new class() {}::
    // PHP Parse error:  syntax error, unexpected token ";", expecting identifier or variable or "{" or "$"
    parser.syntax_error();
    RESULT = parser.createDispatch(operator, new ClassInstanceCreationVariable(expr), new Identifier(operatorright, operatorright, ""), // NOI18N
            operatorright, operatorright, null, operatorright, new LinkedList(), new LinkedList());
:}
;

variable ::=
field_or_method_access:acc
{:
    RESULT = acc;
:}

| base_variable_with_function_calls:var
{:
    RESULT = var;
:}
;

variable_properties ::=
variable_properties:variables variable_property:variableProperty
{:
    variables.add(variableProperty);
    RESULT = variables;
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

variable_property ::=
T_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa
{:
    RESULT = parser.createDispatchProperty(ASTPHP5Parser.Access.NON_STATIC, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, aa);
:}

| T_NULLSAFE_OBJECT_OPERATOR object_property:memberProperty method_or_not:paramsList array_access_or_not:aa
{:
    RESULT = parser.createDispatchProperty(ASTPHP5Parser.Access.NULLSAFE, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, aa);
:}

| T_PAAMAYIM_NEKUDOTAYIM static_property:memberProperty method_or_not:paramsList array_access_or_not:aa
{:
    RESULT = parser.createDispatchProperty(ASTPHP5Parser.Access.STATIC, memberProperty, memberPropertyleft, memberPropertyright, paramsList, paramsListright, aa);
:}
;

method_or_not ::=
T_OPEN_PARENTHESE function_call_parameter_list:paramsList T_CLOSE_PARENTHESE
{:
    RESULT = paramsList;
:}

| /* empty */
{:
    RESULT = null;
:}
;

array_dimension ::=
T_OPEN_RECT:o dim_offset:index T_CLOSE_RECT:end
{:
    RESULT = new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY);
:}

| T_CURLY_OPEN:o dim_offset:index T_CURLY_CLOSE:end
{:
    RESULT = new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_HASHTABLE);
:}
;

array_dimension_with_static_scalar_value ::=
T_OPEN_RECT:o static_scalar_value:index T_CLOSE_RECT:end
{:
    RESULT = new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY);
:}

| T_CURLY_OPEN:o static_scalar_value:index T_CURLY_CLOSE:end
{:
    RESULT = new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_HASHTABLE);
:}
;

array_access_or_not ::=
array_dimension:ad
{:
    List list = new LinkedList();
    list.add(ad);
    RESULT = list;
:}

| array_access_or_not:list array_dimension:ad
{:
    list.add(ad);
    RESULT = list;
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

variable_without_objects ::=
reference_variable:var
{:
    RESULT = var;
:}

| simple_indirect_reference:ref_count reference_variable:var
{:
    // the ref_count counts the number of reflection (DOLLAR sign) so now we should
    // accomulate the dolars into reflection variables
    Variable finalVar = var;
    for (int i=0; i<ref_count.intValue(); i++) {
        finalVar = new ReflectionVariable(ref_countright - i - 1, varright, finalVar);
    }
    RESULT = finalVar;
:}
;

static_member ::=
class_name:className T_PAAMAYIM_NEKUDOTAYIM variable_without_objects:var
{:
    RESULT = new StaticFieldAccess(classNameleft, varright, className, var);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM variable_without_objects:var
{:
    RESULT = new StaticFieldAccess(classNameleft, varright, className, var);
:}
;


variable_class_name ::=
reference_variable:var
{:
    RESULT = var;
:}
;







base_variable_with_function_calls ::=
base_variable:var
{: RESULT = var; :}

| function_call:var
{: RESULT = var; :}
;

expression_array_access ::=
constant_array_access:arrayAccess
{:
    RESULT = arrayAccess;
:}

| T_ENCAPSED_AND_WHITESPACE:str array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(strleft, adright, new Identifier(strleft, strright, str), ad);
:}

| T_CONSTANT_ENCAPSED_STRING:str array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(strleft, adright, new Identifier(strleft, strright, str), ad);
:}
;

constant_array_access ::=
constant_array_access:arrayAccess array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(arrayAccessleft, adright, arrayAccess, ad);
:}

| T_STRING:constantName array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(constantNameleft, adright, new Identifier(constantNameleft, constantNameright, constantName), ad);
:}

| namespace_name_access:namespace array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(namespaceleft, adright, namespace, ad);
:}
;

array_creation_with_access ::=
array_creation:arr array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(arrleft, adright, arr, ad);
:}

| array_creation_with_access:acc array_dimension:ad
{:
    RESULT = new ExpressionArrayAccess(accleft, adright, acc, ad);
:}
;

array_creation ::=
T_ARRAY:start T_OPEN_PARENTHESE array_pair_list:list T_CLOSE_PARENTHESE:end
{:
    Expression expr = new ArrayCreation(startleft, endright, list, ArrayCreation.Type.OLD);
    RESULT = expr;
:}

| T_OPEN_RECT:start array_pair_list:list T_CLOSE_RECT:end
{:
    Expression expr = new ArrayCreation(startleft, endright, list, ArrayCreation.Type.NEW);
    RESULT = expr;
:}
;

base_variable ::=
reference_variable:var
{:
    RESULT = var;
:}

| base_variable_without_reference_variable:variable
{:
    RESULT = variable;
:}
;

base_variable_without_reference_variable ::=
T_OPEN_PARENTHESE:start anonymous_class:cls T_CLOSE_PARENTHESE:end
{:
    RESULT = new AnonymousObjectVariable(startleft, endright, cls);
:}

| T_OPEN_PARENTHESE:start T_NEW:n class_name_reference:className ctor_arguments:ctor T_CLOSE_PARENTHESE:end
{:
    ClassInstanceCreation classInstanceCreation = new ClassInstanceCreation(nleft, ctorright, className, ctor);
    RESULT = new AnonymousObjectVariable(startleft, endright, classInstanceCreation);
:}

| T_OPEN_PARENTHESE:start T_CLONE:c expr:expr T_CLOSE_PARENTHESE:end
{:
    CloneExpression clone = new CloneExpression(cleft, exprright, expr);
    RESULT = new AnonymousObjectVariable(startleft, endright, clone);
:}

| simple_indirect_reference:ref_count reference_variable:var
{:
    // the ref_count counts the number of reflection (DOLLAR sign) so now we should
    // accomulate the dolars into reflection variables
    VariableBase finalVar = var;
    for (int i=0; i<ref_count.intValue(); i++) {
        finalVar = new ReflectionVariable(ref_countright - i - 1, varright, finalVar);
    }
    RESULT = finalVar;
:}

| static_member:staticFieldAccess
{:
    RESULT = staticFieldAccess;
:}

| array_creation_with_access:arrayCreationWithAccess
{:
    RESULT = arrayCreationWithAccess;
:}

| dereferencable_variable:dereferencableVariable
{:
    RESULT = dereferencableVariable;
:}
;

dereferencable_variable ::=
T_OPEN_PARENTHESE:start variable:var T_CLOSE_PARENTHESE:end
{:
    RESULT = new DereferencableVariable(startleft, endright, var);
:}

| parenthesis_expr:pe array_dimension:ad
{:
    // e.g. ((string) $variable->something)[0];
    DereferencableVariable dereferencableVariable = new DereferencableVariable(peleft, peright, pe.getExpression());
    RESULT = new DereferencedArrayAccess(peleft, adright, dereferencableVariable, ad);
:}

| T_OPEN_PARENTHESE:start T_NEW:n class_name_reference:className ctor_arguments:ctor T_CLOSE_PARENTHESE:end array_dimension:ad
{:
    // (new Example())['key'];
    ClassInstanceCreation classInstanceCreation = new ClassInstanceCreation(nleft, ctorright, className, ctor);
    RESULT = new DereferencedArrayAccess(startleft, adright, new AnonymousObjectVariable(startleft, endright, classInstanceCreation), ad);
:}

// PHP 8.4 new MyClass()->method() without parentheses
// https://wiki.php.net/rfc/new_without_parentheses
| new_expr:expr array_dimension:ad
{:
    // new Example()['key'];
    // new class(){}['key'];
    RESULT = new DereferencedArrayAccess(exprleft, adright, new ClassInstanceCreationVariable(expr), ad);
:}

| dereferencable_variable:var array_dimension:ad
{:
    RESULT = new DereferencedArrayAccess(varleft, adright, var, ad);
:}
;

reference_variable ::=
reference_variable:varName array_dimension:ad
{:
    Variable var = new ArrayAccess(varNameleft, adright, varName, ad);
    RESULT = var;
:}

| compound_variable:comp_var
{: RESULT = comp_var; :}
;

compound_variable ::=
tracked_variable:var
{: RESULT = var; :}

| T_DOLLAR:start T_CURLY_OPEN expr:expr T_CURLY_CLOSE:end
{:
    ReflectionVariable var = new ReflectionVariable(startleft, endright, expr);
    RESULT = var;
:}
;

dim_offset ::=
/* empty */
{:
    RESULT = null;
:}

| expr:expr
{:
    RESULT = expr;
:}
;

static_property ::=
variable_without_objects:var
{:
    RESULT = var;
:}

| static_reference_constant:var
{:
    RESULT = var;
:}

| T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    // e.g. Example::{method('foo')}()::{method('bar')}();
    RESULT = new ReflectionVariable(startleft, endright, expr);
:}
;

object_property ::=
object_dim_list:var
{:
    RESULT = var;
:}

| variable_without_objects:var
{:
    RESULT = var;
:}
;

object_dim_list ::=
object_dim_list:var array_dimension:ad
{:
    Variable varArray = new ArrayAccess(varleft, adright, var, ad);
    RESULT = varArray;
:}

| variable_name:var
{: RESULT = var; :}
;

variable_name ::=
string_st:varName
{:
    RESULT = new Variable(varNameleft, varNameright, varName);
:}

| T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    RESULT = new ReflectionVariable(startleft, endright, expr);
:}
;

simple_indirect_reference ::=
T_DOLLAR
{:
    RESULT = Integer.valueOf(1);
:}

| simple_indirect_reference:ref T_DOLLAR
{:
    RESULT = Integer.valueOf(1 + ref.intValue());
:}
;

array_pair_list ::=
non_empty_array_pair_list:list
{:
    RESULT = list;
:}
;

non_empty_array_pair_list ::=
non_empty_array_pair_list:list T_COMMA possible_array_pair:pair
{:
    if(pair != null) {
        list.add(pair);
    }
    RESULT = list;
:}

| possible_array_pair:pair
{:
    List list = new LinkedList();
    if(pair != null) {
        list.add(pair);
    }
    RESULT = list;
:}
;

possible_array_pair ::=
/* empty */
{:
    RESULT = null;
:}

| array_pair:pair
{:
    RESULT = pair;
:}
;

array_pair ::=
expr:key T_DOUBLE_ARROW expr:value
{:
    ArrayElement element = new ArrayElement(keyleft, valueright, key, value);
    RESULT = element;
:}

| expr:expr
{:
    ArrayElement element = new ArrayElement(exprleft, exprright, expr);
    RESULT = element;
:}

| T_ELLIPSIS:ell expr:expr
{:
    // PHP 7.4 Spread Operator In Array Expression
    // https://wiki.php.net/rfc/spread_operator_for_array
    UnpackableArrayElement unpack = new UnpackableArrayElement(ellleft, exprright, expr);
    RESULT = unpack;
:}

| expr:expr T_DOUBLE_ARROW ampersand:start w_variable:var
{:
    Reference value = new Reference(startleft, varright, var);
    ArrayElement element = new ArrayElement(exprleft, varright, expr, value);
    RESULT = element;
:}


| ampersand:start w_variable:var
{:
    Reference ref = new Reference(startleft, varright, var);
    ArrayElement element = new ArrayElement(startleft, varright, ref);
    RESULT = element;
:}

| expr:expr T_DOUBLE_ARROW T_LIST:start T_OPEN_PARENTHESE array_pair_list:varList T_CLOSE_PARENTHESE:end
{:
    ListVariable value = new ListVariable(startleft, endright, varList, ListVariable.SyntaxType.OLD);
    ArrayElement element = new ArrayElement(exprleft, endright, expr, value);
    RESULT = element;
:}

| T_LIST:start T_OPEN_PARENTHESE array_pair_list:varList T_CLOSE_PARENTHESE:end
{:
    ListVariable vars = new ListVariable(startleft, endright, varList, ListVariable.SyntaxType.OLD);
    ArrayElement element = new ArrayElement(startleft, endright, vars);
    RESULT = element;
:}
;

encaps_list ::=
encaps_list:list encaps_var:var
{:
    list.add(var);
    RESULT = list;
:}

| encaps_list:list T_ENCAPSED_AND_WHITESPACE:string
{:
    Scalar scalar = new Scalar(stringleft, stringright, string, string == null ? Scalar.Type.UNKNOWN : Scalar.Type.STRING);
    list.add(scalar);
    RESULT = list;
:}

| /* empty */
{:
    RESULT = new LinkedList();
:}
;

encaps_var ::=
tracked_variable:var
{:
    RESULT = var;
:}

| tracked_variable:varName T_OPEN_RECT:o encaps_var_offset:index T_CLOSE_RECT:end
{:
    Variable var = new ArrayAccess(varNameleft, endright, varName, new ArrayDimension(oleft, endright, index, ArrayDimension.Type.VARIABLE_ARRAY));
    RESULT = var;
:}

| tracked_variable:var T_OBJECT_OPERATOR string_st:string
{:
    Variable property = new Variable(stringleft, stringright, string);
    VariableBase dispatch = parser.createDispatch(var, property, ASTPHP5Parser.Access.NON_STATIC);
    RESULT = dispatch;
:}

| tracked_variable:var T_NULLSAFE_OBJECT_OPERATOR string_st:string
{:
    Variable property = new Variable(stringleft, stringright, string);
    VariableBase dispatch = parser.createDispatch(var, property, ASTPHP5Parser.Access.NULLSAFE);
    RESULT = dispatch;
:}

| T_DOLLAR_OPEN_CURLY_BRACES:start expr:expr T_CURLY_CLOSE:end
{:
    ReflectionVariable var = new ReflectionVariable(startleft, endright, expr);
    RESULT = var;
:}

| T_DOLLAR_OPEN_CURLY_BRACES:start T_STRING_VARNAME:varName T_OPEN_RECT:o expr:index T_CLOSE_RECT:c T_CURLY_CLOSE:end
{:
    Variable var = new Variable(varNameleft, varNameright, varName);
    Variable indexedVar = new ArrayAccess(startleft, endright, var, new ArrayDimension(oleft, cright, index, ArrayDimension.Type.VARIABLE_ARRAY));
    RESULT = indexedVar;
:}

| T_CURLY_OPEN_WITH_DOLAR:start variable:var T_CURLY_CLOSE:end
{:
    ReflectionVariable ref = new ReflectionVariable(startleft, endright, var);
    RESULT = ref;
:}
;

encaps_var_offset ::=
string_st:string
{:
    Identifier id = new Identifier(stringleft, stringright, string);
    RESULT = id;
:}

| T_NUM_STRING:num
{:
    Scalar scalar = new Scalar(numleft,numright, num, Scalar.Type.FLOAT);
    RESULT = scalar;
:}

| tracked_variable:var
{:
    RESULT = var;
:}
;

internal_functions_in_yacc ::=
T_ISSET:start T_OPEN_PARENTHESE isset_variables:varList possible_comma T_CLOSE_PARENTHESE:end
{:
    Identifier id = new Identifier(startleft, startright, "isset");
    FunctionName name = new FunctionName(startleft, startright, id);
    FunctionInvocation result = new FunctionInvocation(startleft, endright, name, varList);
    RESULT = result;
:}

| T_EMPTY:start T_OPEN_PARENTHESE variable:var T_CLOSE_PARENTHESE:end
{:
    Identifier id = new Identifier(startleft, startright, "empty");
    FunctionName name = new FunctionName(startleft, startright, id);
    LinkedList varList = new LinkedList();
    varList.add(var);
    FunctionInvocation result = new FunctionInvocation(startleft, endright, name, varList);
    RESULT = result;
:}

| T_EMPTY:start T_OPEN_PARENTHESE expr_without_variable:expr T_CLOSE_PARENTHESE:end
{:
    Identifier id = new Identifier(startleft, startright, "empty");
    FunctionName name = new FunctionName(startleft, startright, id);
    LinkedList exprList = new LinkedList();
    exprList.add(expr);
    FunctionInvocation result = new FunctionInvocation(startleft, endright, name, exprList);
    RESULT = result;
:}

| T_INCLUDE:include expr:expr
{:
    Include result = new Include(includeleft, exprright, expr, Include.Type.INCLUDE);
    RESULT = result;
:}

| T_INCLUDE_ONCE:include expr:expr
{:
    Include result = new Include(includeleft, exprright, expr, Include.Type.INCLUDE_ONCE);
    RESULT = result;
:}

| T_EVAL:start T_OPEN_PARENTHESE expr:expr T_CLOSE_PARENTHESE:end
{:
    Identifier id = new Identifier(startleft, startright, "eval");
    FunctionName name = new FunctionName(startleft, startright, id);
    LinkedList exprList = new LinkedList();
    exprList.add(expr);
    FunctionInvocation result = new FunctionInvocation(startleft, endright, name, exprList);
    RESULT = result;
:}

| T_REQUIRE:include expr:expr
{:
    Include result = new Include(includeleft, exprright, expr, Include.Type.REQUIRE);
    RESULT = result;
:}

| T_REQUIRE_ONCE:include expr:expr
{:
    Include result = new Include(includeleft, exprright, expr, Include.Type.REQUIRE_ONCE);
    RESULT = result;
:}
;

isset_variables ::=
isset_variable:var
{:
    List list = new LinkedList();
    list.add(var);
    RESULT = list;
:}

| isset_variables:varList T_COMMA isset_variable:var
{:
    varList.add(var);
    RESULT = varList;
:}
;

isset_variable ::=
variable:var
{:
    RESULT = var;
:}

| expression_array_access:arrayAccess
{:
    RESULT = arrayAccess;
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM constant_array_access:arrayAccess
{:
    RESULT = new StaticConstantAccess(classNameleft, arrayAccessright, className, arrayAccess);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM constant_array_access:arrayAccess
{:
    RESULT = new StaticConstantAccess(classNameleft, arrayAccessright, className, arrayAccess);
:}
;

class_constant ::=
enum_constant:constant
{:
    RESULT = constant;
:}

| class_constant:constant array_dimension:ad
{:
    // Name::CONSTANT[1][2];
    // Name::ENUM_CASE::CONSTANT[1];
    RESULT = new StaticConstantAccess(constantleft, adright, constant.getDispatcher(),
            new ExpressionArrayAccess(constant.getConstant().getStartOffset(), adright, constant.getConstant(), ad));
:}
;

enum_constant ::=
class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant
{:
    RESULT = new StaticConstantAccess(classNameleft, constantright, className, constant);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM identifier:constant
{:
    RESULT = new StaticConstantAccess(classNameleft, constantright, className, constant);
:}

| class_name:className T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    ReflectionVariable reflectionVariable = new ReflectionVariable(startleft, endright, expr);
    RESULT = new StaticConstantAccess(classNameleft, endright, className, reflectionVariable, true);
:}

| variable_class_name:className T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    ReflectionVariable reflectionVariable = new ReflectionVariable(startleft, endright, expr);
    RESULT = new StaticConstantAccess(classNameleft, endright, className, reflectionVariable, true);
:}

| enum_constant:enumConst T_PAAMAYIM_NEKUDOTAYIM identifier:constant
{:
    // Name::ENUM_CASE::CONSTANT;
    RESULT = new StaticConstantAccess(enumConstleft, constantright, enumConst, constant);
:}
| enum_constant:enumConst T_PAAMAYIM_NEKUDOTAYIM T_CURLY_OPEN:start expr:expr T_CURLY_CLOSE:end
{:
    ReflectionVariable reflectionVariable = new ReflectionVariable(startleft, endright, expr);
    RESULT = new StaticConstantAccess(enumConstleft, endright, enumConst, reflectionVariable, true);
:}
;

tracked_variable ::=
T_VARIABLE:varName
{:
    RESULT = new Variable(varNameleft, varNameright, varName);
:}
;

optional_tracked_variable ::=
/* empty */
{:
    RESULT = null;
:}

| tracked_variable:var
{:
    RESULT = var;
:}
;

string_st ::=
T_STRING:value
{: RESULT = value; :}

| T_DEFINE:value
{: RESULT = value; :}
;

anonymous_class ::=
T_NEW:start T_CLASS:c ctor_arguments:ctor
extends_from:superClass implements_list:interfaces
T_CURLY_OPEN:blockStart class_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    final int counter = parser.incrementAndGetAnonymousClassCounter();
    Block block = new Block(blockStartleft, blockEndright, statementList);
    ClassInstanceCreation classInstance = ClassInstanceCreation.anonymous(parser.getFileName(), counter, startleft, blockEndright, cleft, ctor, superClass, interfaces, block);
    RESULT = classInstance;
:}

| T_NEW:start attributes:attributes T_CLASS:c ctor_arguments:ctor
extends_from:superClass implements_list:interfaces
T_CURLY_OPEN:blockStart class_statement_list:statementList T_CURLY_CLOSE:blockEnd
{:
    final int counter = parser.incrementAndGetAnonymousClassCounter();
    Block block = new Block(blockStartleft, blockEndright, statementList);
    ClassInstanceCreation classInstance = ClassInstanceCreation.anonymous(parser.getFileName(), counter, startleft, blockEndright, cleft, ctor, superClass, interfaces, block, attributes);
    RESULT = classInstance;
:}
;

new_expr ::=
T_NEW:n class_name_reference:className T_OPEN_PARENTHESE function_call_parameter_list:paramsList T_CLOSE_PARENTHESE:end
{:
    RESULT = new ClassInstanceCreation(nleft, endright, className, paramsList);
:}

| anonymous_class:anon
{:
    RESULT = anon;
:}
;

access_operator ::=
T_OBJECT_OPERATOR
{:
    RESULT = ASTPHP5Parser.Access.NON_STATIC;
:}

| T_NULLSAFE_OBJECT_OPERATOR
{:
    RESULT = ASTPHP5Parser.Access.NULLSAFE;
:}

| T_PAAMAYIM_NEKUDOTAYIM
{:
    RESULT = ASTPHP5Parser.Access.STATIC;
:}
;

[0.86QuellennavigatorsProjekt 2026-04-27]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge