Skip to main content
Drupal API
User account menu
  • Log in

Breadcrumb

  1. Drupal Core 11.1.x

Parser.php

Same filename in this branch
  1. 11.1.x vendor/open-telemetry/api/Baggage/Propagation/Parser.php
  2. 11.1.x vendor/phpunit/phpunit/src/Metadata/Parser/Parser.php
  3. 11.1.x vendor/sebastian/cli-parser/src/Parser.php
  4. 11.1.x vendor/sebastian/diff/src/Parser.php
  5. 11.1.x vendor/egulias/email-validator/src/Parser.php
  6. 11.1.x vendor/nikic/php-parser/lib/PhpParser/Parser.php
  7. 11.1.x vendor/twig/twig/src/Parser.php
  8. 11.1.x vendor/symfony/css-selector/Parser/Parser.php
  9. 11.1.x vendor/symfony/yaml/Parser.php
  10. 11.1.x vendor/mck89/peast/lib/Peast/Selector/Parser.php
  11. 11.1.x vendor/mck89/peast/lib/Peast/Syntax/Parser.php

Namespace

Peast\Syntax\JSX

File

vendor/mck89/peast/lib/Peast/Syntax/JSX/Parser.php

View source
<?php


/**
 * This file is part of the Peast package
 *
 * (c) Marco Marchiò <marco.mm89@gmail.com>
 *
 * For the full copyright and license information refer to the LICENSE file
 * distributed with this source code
 */
namespace Peast\Syntax\JSX;

use Peast\Syntax\Token;

/**
 * JSX parser trait
 * 
 * @author Marco Marchiò <marco.mm89@gmail.com>
 */
trait Parser {
    
    /**
     * Creates a JSX node
     * 
     * @param string $nodeType Node's type
     * @param mixed  $position Node's start position
     * 
     * @return \Peast\Syntax\Node\Node
     */
    protected function createJSXNode($nodeType, $position) {
        return $this->createNode("JSX\\{$nodeType}", $position);
    }
    
    /**
     * Parses a jsx fragment
     * 
     * @return \Peast\Syntax\Node\JSX\JSXFragment|null
     */
    protected function parseJSXFragment() {
        $startOpeningToken = $this->scanner
            ->getToken();
        if (!$startOpeningToken || $startOpeningToken->value !== "<") {
            return null;
        }
        $endOpeningToken = $this->scanner
            ->getNextToken();
        if (!$endOpeningToken || $endOpeningToken->value !== ">") {
            return null;
        }
        $this->scanner
            ->consumeToken();
        $this->scanner
            ->consumeToken();
        $children = $this->parseJSXChildren();
        if (!($startClosingToken = $this->scanner
            ->consume("<")) || !$this->scanner
            ->consume("/") || !$this->scanner
            ->reconsumeCurrentTokenInJSXMode() || $endOpeningToken->value !== ">") {
            $this->error();
        }
        $this->scanner
            ->consumeToken();
        
        //Opening tag
        $openingNode = $this->createJSXNode("JSXOpeningFragment", $startOpeningToken);
        $this->completeNode($openingNode, $endOpeningToken->location->end);
        
        //Closing tag
        $closingNode = $this->createJSXNode("JSXClosingFragment", $startClosingToken);
        $this->completeNode($closingNode);
        
        //Fragment
        $node = $this->createJSXNode("JSXFragment", $startOpeningToken);
        $node->setOpeningFragment($openingNode);
        $node->setClosingFragment($closingNode);
        if ($children) {
            $node->setChildren($children);
        }
        return $this->completeNode($node);
    }
    
    /**
     * Parses a group of jsx children
     * 
     * @return \Peast\Syntax\Node\Node[]|null
     */
    protected function parseJSXChildren() {
        $children = array();
        while ($child = $this->parseJSXChild()) {
            $children[] = $child;
        }
        return count($children) ? $children : null;
    }
    
    /**
     * Parses a jsx child
     * 
     * @return \Peast\Syntax\Node\Node|null
     */
    protected function parseJSXChild() {
        if ($node = $this->parseJSXText()) {
            return $node;
        }
        elseif ($node = $this->parseJSXFragment()) {
            return $node;
        }
        elseif ($node = $this->parseJSXElement()) {
            return $node;
        }
        elseif ($startToken = $this->scanner
            ->consume("{")) {
            $spread = $this->scanner
                ->consume("...");
            $exp = $this->parseAssignmentExpression();
            $midPos = $this->scanner
                ->getPosition();
            if ($spread && !$exp || !$this->scanner
                ->consume("}")) {
                $this->error();
            }
            $node = $this->createJSXNode($spread ? "JSXSpreadChild" : "JSXExpressionContainer", $startToken);
            if (!$exp) {
                $exp = $this->createJSXNode("JSXEmptyExpression", $midPos);
                $this->completeNode($exp, $midPos);
            }
            $node->setExpression($exp);
            return $this->completeNode($node);
        }
        return null;
    }
    
    /**
     * Parses a jsx text
     * 
     * @return \Peast\Syntax\Node\JSX\JSXText|null
     */
    protected function parseJSXText() {
        if (!($token = $this->scanner
            ->reconsumeCurrentTokenAsJSXText())) {
            return null;
        }
        $this->scanner
            ->consumeToken();
        $node = $this->createJSXNode("JSXText", $token);
        $node->setRaw($token->value);
        return $this->completeNode($node, $token->location->end);
    }
    
    /**
     * Parses a jsx element
     * 
     * @return \Peast\Syntax\Node\JSX\JSXElement|null
     */
    protected function parseJSXElement() {
        $startOpeningToken = $this->scanner
            ->getToken();
        if (!$startOpeningToken || $startOpeningToken->value !== "<") {
            return null;
        }
        $nextToken = $this->scanner
            ->getNextToken();
        if ($nextToken && $nextToken->value === "/") {
            return null;
        }
        $this->scanner
            ->consumeToken();
        if (!($name = $this->parseJSXIdentifierOrMemberExpression())) {
            $this->error();
        }
        $attributes = $this->parseJSXAttributes();
        $selfClosing = $this->scanner
            ->consume("/");
        $endOpeningToken = $this->scanner
            ->reconsumeCurrentTokenInJSXMode();
        if (!$endOpeningToken || $endOpeningToken->value !== ">") {
            $this->error();
        }
        $this->scanner
            ->consumeToken();
        if (!$selfClosing) {
            $children = $this->parseJSXChildren();
            if (($startClosingToken = $this->scanner
                ->consume("<")) && $this->scanner
                ->consume("/") && ($closingName = $this->parseJSXIdentifierOrMemberExpression()) && ($endClosingToken = $this->scanner
                ->reconsumeCurrentTokenInJSXMode()) && $endClosingToken->value === ">") {
                $this->scanner
                    ->consumeToken();
                if (!$this->isSameJSXElementName($name, $closingName)) {
                    $this->error("Closing tag does not match opening tag");
                }
            }
            else {
                $this->error();
            }
        }
        
        //Opening tag
        $openingNode = $this->createJSXNode("JSXOpeningElement", $startOpeningToken);
        $openingNode->setName($name);
        $openingNode->setSelfClosing($selfClosing);
        if ($attributes) {
            $openingNode->setAttributes($attributes);
        }
        $this->completeNode($openingNode, $endOpeningToken->location->end);
        
        //Closing tag
        $closingNode = null;
        if (!$selfClosing) {
            $closingNode = $this->createJSXNode("JSXClosingElement", $startClosingToken);
            $closingNode->setName($closingName);
            $this->completeNode($closingNode);
        }
        
        //Element
        $node = $this->createJSXNode("JSXElement", $startOpeningToken);
        $node->setOpeningElement($openingNode);
        if ($closingNode) {
            $node->setClosingElement($closingNode);
            if ($children) {
                $node->setChildren($children);
            }
        }
        return $this->completeNode($node);
    }
    
    /**
     * Parses a jsx identifier, namespaced identifier or member expression
     * 
     * @param bool $allowMember True to allow member expressions
     * 
     * @return \Peast\Syntax\Node\Node|null
     */
    protected function parseJSXIdentifierOrMemberExpression($allowMember = true) {
        $idToken = $this->scanner
            ->reconsumeCurrentTokenInJSXMode();
        if (!$idToken || $idToken->type !== Token::TYPE_JSX_IDENTIFIER) {
            return null;
        }
        $this->scanner
            ->consumeToken();
        $idNode = $this->createJSXNode("JSXIdentifier", $idToken);
        $idNode->setName($idToken->value);
        $idNode = $this->completeNode($idNode);
        
        //Namespaced identifier
        if ($this->scanner
            ->consume(":")) {
            $idToken2 = $this->scanner
                ->reconsumeCurrentTokenInJSXMode();
            if (!$idToken2 || $idToken2->type !== Token::TYPE_JSX_IDENTIFIER) {
                $this->error();
            }
            $this->scanner
                ->consumeToken();
            $idNode2 = $this->createJSXNode("JSXIdentifier", $idToken2);
            $idNode2->setName($idToken2->value);
            $idNode2 = $this->completeNode($idNode2);
            $node = $this->createJSXNode("JSXNamespacedName", $idToken);
            $node->setNamespace($idNode);
            $node->setName($idNode2);
            return $this->completeNode($node);
        }
        
        //Get following identifiers
        $nextIds = array();
        if ($allowMember) {
            while ($this->scanner
                ->consume(".")) {
                $nextId = $this->scanner
                    ->reconsumeCurrentTokenInJSXMode();
                if (!$nextId || $nextId->type !== Token::TYPE_JSX_IDENTIFIER) {
                    $this->error();
                }
                $this->scanner
                    ->consumeToken();
                $nextIds[] = $nextId;
            }
        }
        
        //Create the member expression if required
        $objectNode = $idNode;
        foreach ($nextIds as $nid) {
            $propEnd = $nid->location->end;
            $propNode = $this->createJSXNode("JSXIdentifier", $nid);
            $propNode->setName($nid->value);
            $propNode = $this->completeNode($propNode, $propEnd);
            $node = $this->createJSXNode("JSXMemberExpression", $objectNode);
            $node->setObject($objectNode);
            $node->setProperty($propNode);
            $objectNode = $this->completeNode($node, $propEnd);
        }
        return $objectNode;
    }
    
    /**
     * Parses a jsx attributes list
     * 
     * @return \Peast\Syntax\Node\Node[]|null
     */
    protected function parseJSXAttributes() {
        $attributes = array();
        while (($attr = $this->parseJSXSpreadAttribute()) || ($attr = $this->parseJSXAttribute())) {
            $attributes[] = $attr;
        }
        return count($attributes) ? $attributes : null;
    }
    
    /**
     * Parses a jsx spread attribute
     * 
     * @return \Peast\Syntax\Node\JSX\JSXSpreadAttribute|null
     */
    protected function parseJSXSpreadAttribute() {
        if (!($openToken = $this->scanner
            ->consume("{"))) {
            return null;
        }
        if ($this->scanner
            ->consume("...") && ($exp = $this->parseAssignmentExpression()) && $this->scanner
            ->consume("}")) {
            $node = $this->createJSXNode("JSXSpreadAttribute", $openToken);
            $node->setArgument($exp);
            return $this->completeNode($node);
        }
        $this->error();
    }
    
    /**
     * Parses a jsx spread attribute
     * 
     * @return \Peast\Syntax\Node\JSX\JSXSpreadAttribute|null
     */
    protected function parseJSXAttribute() {
        if (!($name = $this->parseJSXIdentifierOrMemberExpression(false))) {
            return null;
        }
        $value = null;
        if ($this->scanner
            ->consume("=")) {
            $strToken = $this->scanner
                ->reconsumeCurrentTokenInJSXMode();
            if ($strToken && $strToken->type === Token::TYPE_STRING_LITERAL) {
                $this->scanner
                    ->consumeToken();
                $value = $this->createNode("StringLiteral", $strToken);
                $value->setRaw($strToken->value);
                $value = $this->completeNode($value);
            }
            elseif ($startExp = $this->scanner
                ->consume("{")) {
                if (($exp = $this->parseAssignmentExpression()) && $this->scanner
                    ->consume("}")) {
                    $value = $this->createJSXNode("JSXExpressionContainer", $startExp);
                    $value->setExpression($exp);
                    $value = $this->completeNode($value);
                }
                else {
                    $this->error();
                }
            }
            elseif (!($value = $this->parseJSXFragment()) && !($value = $this->parseJSXElement())) {
                $this->error();
            }
        }
        $node = $this->createJSXNode("JSXAttribute", $name);
        $node->setName($name);
        if ($value) {
            $node->setValue($value);
        }
        return $this->completeNode($node);
    }
    
    /**
     * Checks that 2 tag names are equal
     * 
     * @param \Peast\Syntax\Node\Node   $n1 First name
     * @param \Peast\Syntax\Node\Node   $n2 Second name
     * 
     * @return bool
     */
    protected function isSameJSXElementName($n1, $n2) {
        $type = $n1->getType();
        if ($type !== $n2->getType()) {
            return false;
        }
        elseif ($type === "JSXNamespacedName") {
            return $this->isSameJSXElementName($n1->getNamespace(), $n2->getNamespace()) && $this->isSameJSXElementName($n1->getName(), $n2->getName());
        }
        elseif ($type === "JSXMemberExpression") {
            return $this->isSameJSXElementName($n1->getObject(), $n2->getObject()) && $this->isSameJSXElementName($n1->getProperty(), $n2->getProperty());
        }
        return $type === "JSXIdentifier" && $n1->getName() === $n2->getName();
    }

}

Traits

Title Deprecated Summary
Parser JSX parser trait

API Navigation

  • Drupal Core 11.1.x
  • Topics
  • Classes
  • Functions
  • Constants
  • Globals
  • Files
  • Namespaces
  • Deprecated
  • Services
RSS feed
Powered by Drupal