Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/Java/Netbeans/php/php.editor/tools/   (Apache JAVA IDE Version 28©)  Datei vom 3.10.2025 mit Größe 166 kB image not shown  

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]