private function parseVariableRule(TokenStream $tokens, AstNode $currNode): void
    {
        // Expect a rule name
        $tokens->expect(TokenTypes::T_TEXT, null, 'Expected rule name, got %s');

        if (($token = $tokens->getCurrent()) === null) {
            return;
        }

        $variableRuleNode = new AstNode(AstNodeTypes::VARIABLE_RULE, $token->value);
        $tokens->next();

        // Check for a parameter list for this rule
        if ($tokens->nextIfType(TokenTypes::T_PUNCTUATION, '(')) {
            $parameters = [];
            $currentToken = $tokens->getCurrent();

            while ($currentToken !== null && !$tokens->test(TokenTypes::T_PUNCTUATION, ')')) {
                if (!$tokens->test(TokenTypes::T_PUNCTUATION, ',')) {
                    $parameters[] = $currentToken->value;
                }

                $currentToken = $tokens->next();
            }

            $variableRuleNode->addChild(new AstNode(AstNodeTypes::VARIABLE_RULE_PARAMETERS, $parameters));
            $tokens->expect(
                TokenTypes::T_PUNCTUATION,
                ')',
                'Expected closing parenthesis after rule parameters, got %s'
            );
            $tokens->next();
        }

        $currNode->addChild($variableRuleNode);
    }