class Parser
Same name in this branch
- 11.1.x vendor/open-telemetry/api/Baggage/Propagation/Parser.php \OpenTelemetry\API\Baggage\Propagation\Parser
- 11.1.x vendor/sebastian/cli-parser/src/Parser.php \SebastianBergmann\CliParser\Parser
- 11.1.x vendor/sebastian/diff/src/Parser.php \SebastianBergmann\Diff\Parser
- 11.1.x vendor/egulias/email-validator/src/Parser.php \Egulias\EmailValidator\Parser
- 11.1.x vendor/twig/twig/src/Parser.php \Twig\Parser
- 11.1.x vendor/symfony/css-selector/Parser/Parser.php \Symfony\Component\CssSelector\Parser\Parser
- 11.1.x vendor/symfony/yaml/Parser.php \Symfony\Component\Yaml\Parser
- 11.1.x vendor/mck89/peast/lib/Peast/Selector/Parser.php \Peast\Selector\Parser
Parser class
@author Marco Marchiò <marco.mm89@gmail.com>
Hierarchy
- class \Peast\Syntax\ParserAbstract
- class \Peast\Syntax\Parser extends \Peast\Syntax\ParserAbstract uses \JSX\Parser
Expanded class hierarchy of Parser
File
-
vendor/
mck89/ peast/ lib/ Peast/ Syntax/ Parser.php, line 17
Namespace
Peast\SyntaxView source
class Parser extends ParserAbstract {
use JSX\Parser;
//Identifier parsing mode constants
/**
* Everything is allowed as identifier, including keywords, null and booleans
*/
const ID_ALLOW_ALL = 1;
/**
* Keywords, null and booleans are not allowed in any situation
*/
const ID_ALLOW_NOTHING = 2;
/**
* Keywords, null and booleans are not allowed in any situation, future
* reserved words are allowed if not in strict mode. Keywords that depend on
* parser context are evaluated only if the parser context allows them.
*/
const ID_MIXED = 3;
/**
* Binding identifier parsing rule
*
* @var int
*/
protected static $bindingIdentifier = self::ID_MIXED;
/**
* Labelled identifier parsing rule
*
* @var int
*/
protected static $labelledIdentifier = self::ID_MIXED;
/**
* Identifier reference parsing rule
*
* @var int
*/
protected static $identifierReference = self::ID_MIXED;
/**
* Identifier name parsing rule
*
* @var int
*/
protected static $identifierName = self::ID_ALLOW_ALL;
/**
* Imported binding parsing rule
*
* @var int
*/
protected static $importedBinding = self::ID_ALLOW_NOTHING;
/**
* Assignment operators
*
* @var array
*/
protected $assignmentOperators = array(
"=",
"+=",
"-=",
"*=",
"/=",
"%=",
"<<=",
">>=",
">>>=",
"&=",
"^=",
"|=",
"**=",
"&&=",
"||=",
"??=",
);
/**
* Logical and binary operators
*
* @var array
*/
protected $logicalBinaryOperators = array(
"??" => 0,
"||" => 0,
"&&" => 1,
"|" => 2,
"^" => 3,
"&" => 4,
"===" => 5,
"!==" => 5,
"==" => 5,
"!=" => 5,
"<=" => 6,
">=" => 6,
"<" => 6,
">" => 6,
"instanceof" => 6,
"in" => 6,
">>>" => 7,
"<<" => 7,
">>" => 7,
"+" => 8,
"-" => 8,
"*" => 9,
"/" => 9,
"%" => 9,
"**" => 10,
);
/**
* Unary operators
*
* @var array
*/
protected $unaryOperators = array(
"delete",
"void",
"typeof",
"++",
"--",
"+",
"-",
"~",
"!",
);
/**
* Postfix operators
*
* @var array
*/
protected $postfixOperators = array(
"--",
"++",
);
/**
* Array of keywords that depends on a context property
*
* @var array
*/
protected $contextKeywords = array(
"yield" => "allowYield",
"await" => "allowAwait",
);
/**
* Initializes parser context
*
* @return void
*/
protected function initContext() {
$context = array(
"allowReturn" => false,
"allowIn" => false,
"allowYield" => false,
"allowAwait" => false,
);
//If async/await is not enabled remove the
//relative context properties
if (!$this->features->asyncAwait) {
unset($context["allowAwait"]);
unset($this->contextKeywords["await"]);
}
$this->context = (object) $context;
}
/**
* Post initialize operations
*
* @return void
*/
protected function postInit() {
//Remove exponentiation operator if the feature
//is not enabled
if (!$this->features->exponentiationOperator) {
Utils::removeArrayValue($this->assignmentOperators, "**=");
unset($this->logicalBinaryOperators["**"]);
}
//Remove coalescing operator if the feature
//is not enabled
if (!$this->features->coalescingOperator) {
unset($this->logicalBinaryOperators["??"]);
}
//Remove logical assignment operators if the
//feature is not enabled
if (!$this->features->logicalAssignmentOperators) {
foreach (array(
"&&=",
"||=",
"??=",
) as $op) {
Utils::removeArrayValue($this->assignmentOperators, $op);
}
}
}
/**
* Parses the source
*
* @return Node\Program
*/
public function parse() {
if ($this->sourceType === \Peast\Peast::SOURCE_TYPE_MODULE) {
$this->scanner
->setStrictMode(true);
$body = $this->parseModuleItemList();
}
else {
$body = $this->parseStatementList(true);
}
$node = $this->createNode("Program", $body ?: $this->scanner
->getPosition());
$node->setSourceType($this->sourceType);
if ($body) {
$node->setBody($body);
}
$program = $this->completeNode($node);
if ($this->scanner
->getToken()) {
$this->error();
}
//Execute scanner end operations
$this->scanner
->consumeEnd();
//Emit the EndParsing event and pass the resulting program node as
//event data
$this->eventsEmitter && $this->eventsEmitter
->fire("EndParsing", array(
$program,
));
return $program;
}
/**
* Converts an expression node to a pattern node
*
* @param Node\Node $node The node to convert
*
* @return Node\Node
*/
protected function expressionToPattern($node) {
if ($node instanceof Node\ArrayExpression) {
$loc = $node->location;
$elems = array();
foreach ($node->getElements() as $elem) {
$elems[] = $this->expressionToPattern($elem);
}
$retNode = $this->createNode("ArrayPattern", $loc->start);
$retNode->setElements($elems);
$this->completeNode($retNode, $loc->end);
}
elseif ($node instanceof Node\ObjectExpression) {
$loc = $node->location;
$props = array();
foreach ($node->getProperties() as $prop) {
$props[] = $this->expressionToPattern($prop);
}
$retNode = $this->createNode("ObjectPattern", $loc->start);
$retNode->setProperties($props);
$this->completeNode($retNode, $loc->end);
}
elseif ($node instanceof Node\Property) {
$loc = $node->location;
$retNode = $this->createNode("AssignmentProperty", $loc->start);
// If it's a shorthand property convert the value to an assignment
// pattern if necessary
$value = $node->getValue();
$key = $node->getKey();
if ($value && $node->getShorthand() && !$value instanceof Node\AssignmentExpression && (!$value instanceof Node\Identifier || $key instanceof Node\Identifier && $key->getName() !== $value->getName())) {
$loc = $node->location;
$valNode = $this->createNode("AssignmentPattern", $loc->start);
$valNode->setLeft($key);
$valNode->setRight($value);
$this->completeNode($valNode, $loc->end);
$value = $valNode;
}
else {
$value = $this->expressionToPattern($value);
}
$retNode->setValue($value);
$retNode->setKey($key);
$retNode->setMethod($node->getMethod());
$retNode->setShorthand($node->getShorthand());
$retNode->setComputed($node->getComputed());
$this->completeNode($retNode, $loc->end);
}
elseif ($node instanceof Node\SpreadElement) {
$loc = $node->location;
$retNode = $this->createNode("RestElement", $loc->start);
$retNode->setArgument($this->expressionToPattern($node->getArgument()));
$this->completeNode($retNode, $loc->end);
}
elseif ($node instanceof Node\AssignmentExpression) {
$loc = $node->location;
$retNode = $this->createNode("AssignmentPattern", $loc->start);
$retNode->setLeft($this->expressionToPattern($node->getLeft()));
$retNode->setRight($node->getRight());
$this->completeNode($retNode, $loc->end);
}
else {
$retNode = $node;
}
return $retNode;
}
/**
* Parses a statement list
*
* @param bool $parseDirectivePrologues True to parse directive prologues
*
* @return Node\Node[]|null
*/
protected function parseStatementList($parseDirectivePrologues = false) {
$items = array();
//Get directive prologues and check if strict mode is present
if ($parseDirectivePrologues) {
$oldStrictMode = $this->scanner
->getStrictMode();
if ($directives = $this->parseDirectivePrologues()) {
$items = array_merge($items, $directives[0]);
//If "use strict" is present enable scanner strict mode
if (in_array("use strict", $directives[1])) {
$this->scanner
->setStrictMode(true);
}
}
}
while ($item = $this->parseStatementListItem()) {
$items[] = $item;
}
//Apply previous strict mode
if ($parseDirectivePrologues) {
$this->scanner
->setStrictMode($oldStrictMode);
}
return count($items) ? $items : null;
}
/**
* Parses a statement list item
*
* @return Node\Statement|Node\Declaration|null
*/
protected function parseStatementListItem() {
if ($declaration = $this->parseDeclaration()) {
return $declaration;
}
elseif ($statement = $this->parseStatement()) {
return $statement;
}
return null;
}
/**
* Parses a statement
*
* @return Node\Statement|null
*/
protected function parseStatement() {
//Here the token value is checked for performance so that functions won't be
//called if not necessary
$token = $this->scanner
->getToken();
if (!$token) {
return null;
}
$val = $token->value;
if ($val === "{" && ($statement = $this->parseBlock())) {
return $statement;
}
elseif ($val === "var" && ($statement = $this->parseVariableStatement())) {
return $statement;
}
elseif ($val === ";" && ($statement = $this->parseEmptyStatement())) {
return $statement;
}
elseif ($val === "if" && ($statement = $this->parseIfStatement())) {
return $statement;
}
elseif (($val === "for" || $val === "while" || $val === "do" || $val === "switch") && ($statement = $this->parseBreakableStatement())) {
return $statement;
}
elseif ($val == "continue" && ($statement = $this->parseContinueStatement())) {
return $statement;
}
elseif ($val === "break" && ($statement = $this->parseBreakStatement())) {
return $statement;
}
elseif ($this->context->allowReturn && $val === "return" && ($statement = $this->parseReturnStatement())) {
return $statement;
}
elseif ($val === "with" && ($statement = $this->parseWithStatement())) {
return $statement;
}
elseif ($val === "throw" && ($statement = $this->parseThrowStatement())) {
return $statement;
}
elseif ($val === "try" && ($statement = $this->parseTryStatement())) {
return $statement;
}
elseif ($val === "debugger" && ($statement = $this->parseDebuggerStatement())) {
return $statement;
}
elseif ($statement = $this->parseLabelledStatement()) {
return $statement;
}
elseif ($statement = $this->parseExpressionStatement()) {
return $statement;
}
return null;
}
/**
* Parses a declaration
*
* @return Node\Declaration|null
*/
protected function parseDeclaration() {
//Here the token value is checked for performance so that functions won't be
//called if not necessary
$token = $this->scanner
->getToken();
if (!$token) {
return null;
}
$val = $token->value;
if ($declaration = $this->parseFunctionOrGeneratorDeclaration()) {
return $declaration;
}
elseif ($val === "class" && ($declaration = $this->parseClassDeclaration())) {
return $declaration;
}
elseif (($val === "let" || $val === "const") && ($declaration = $this->isolateContext(array(
"allowIn" => true,
), "parseLexicalDeclaration"))) {
return $declaration;
}
return null;
}
/**
* Parses a breakable statement
*
* @return Node\Node|null
*/
protected function parseBreakableStatement() {
if ($statement = $this->parseIterationStatement()) {
return $statement;
}
elseif ($statement = $this->parseSwitchStatement()) {
return $statement;
}
return null;
}
/**
* Parses a block statement
*
* @return Node\BlockStatement|null
*/
protected function parseBlock() {
if ($token = $this->scanner
->consume("{")) {
$statements = $this->parseStatementList();
if ($this->scanner
->consume("}")) {
$node = $this->createNode("BlockStatement", $token);
if ($statements) {
$node->setBody($statements);
}
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a module item list
*
* @return Node\Node[]|null
*/
protected function parseModuleItemList() {
$items = array();
while ($item = $this->parseModuleItem()) {
$items[] = $item;
}
return count($items) ? $items : null;
}
/**
* Parses an empty statement
*
* @return Node\EmptyStatement|null
*/
protected function parseEmptyStatement() {
if ($token = $this->scanner
->consume(";")) {
$node = $this->createNode("EmptyStatement", $token);
return $this->completeNode($node);
}
return null;
}
/**
* Parses a debugger statement
*
* @return Node\DebuggerStatement|null
*/
protected function parseDebuggerStatement() {
if ($token = $this->scanner
->consume("debugger")) {
$node = $this->createNode("DebuggerStatement", $token);
$this->assertEndOfStatement();
return $this->completeNode($node);
}
return null;
}
/**
* Parses an if statement
*
* @return Node\IfStatement|null
*/
protected function parseIfStatement() {
if ($token = $this->scanner
->consume("if")) {
if ($this->scanner
->consume("(") && ($test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && (($consequent = $this->parseStatement()) || !$this->scanner
->getStrictMode() && ($consequent = $this->parseFunctionOrGeneratorDeclaration(false, false)))) {
$node = $this->createNode("IfStatement", $token);
$node->setTest($test);
$node->setConsequent($consequent);
if ($this->scanner
->consume("else")) {
if (($alternate = $this->parseStatement()) || !$this->scanner
->getStrictMode() && ($alternate = $this->parseFunctionOrGeneratorDeclaration(false, false))) {
$node->setAlternate($alternate);
return $this->completeNode($node);
}
}
else {
return $this->completeNode($node);
}
}
$this->error();
}
return null;
}
/**
* Parses a try-catch statement
*
* @return Node\TryStatement|null
*/
protected function parseTryStatement() {
if ($token = $this->scanner
->consume("try")) {
if ($block = $this->parseBlock()) {
$node = $this->createNode("TryStatement", $token);
$node->setBlock($block);
if ($handler = $this->parseCatch()) {
$node->setHandler($handler);
}
if ($finalizer = $this->parseFinally()) {
$node->setFinalizer($finalizer);
}
if ($handler || $finalizer) {
return $this->completeNode($node);
}
}
$this->error();
}
return null;
}
/**
* Parses the catch block of a try-catch statement
*
* @return Node\CatchClause|null
*/
protected function parseCatch() {
if ($token = $this->scanner
->consume("catch")) {
$node = $this->createNode("CatchClause", $token);
if ($this->scanner
->consume("(")) {
if (!($param = $this->parseCatchParameter()) || !$this->scanner
->consume(")")) {
$this->error();
}
$node->setParam($param);
}
elseif (!$this->features->optionalCatchBinding) {
$this->error();
}
if (!($body = $this->parseBlock())) {
$this->error();
}
$node->setBody($body);
return $this->completeNode($node);
}
return null;
}
/**
* Parses the catch parameter of a catch block in a try-catch statement
*
* @return Node\Node|null
*/
protected function parseCatchParameter() {
if ($param = $this->parseIdentifier(static::$bindingIdentifier)) {
return $param;
}
elseif ($param = $this->parseBindingPattern()) {
return $param;
}
return null;
}
/**
* Parses a finally block in a try-catch statement
*
* @return Node\BlockStatement|null
*/
protected function parseFinally() {
if ($this->scanner
->consume("finally")) {
if ($block = $this->parseBlock()) {
return $block;
}
$this->error();
}
return null;
}
/**
* Parses a continue statement
*
* @return Node\ContinueStatement|null
*/
protected function parseContinueStatement() {
if ($token = $this->scanner
->consume("continue")) {
$node = $this->createNode("ContinueStatement", $token);
if ($this->scanner
->noLineTerminators() && ($label = $this->parseIdentifier(static::$labelledIdentifier))) {
$node->setLabel($label);
$this->assertEndOfStatement();
}
else {
$this->scanner
->consume(";");
}
return $this->completeNode($node);
}
return null;
}
/**
* Parses a break statement
*
* @return Node\BreakStatement|null
*/
protected function parseBreakStatement() {
if ($token = $this->scanner
->consume("break")) {
$node = $this->createNode("BreakStatement", $token);
if ($this->scanner
->noLineTerminators() && ($label = $this->parseIdentifier(static::$labelledIdentifier))) {
$node->setLabel($label);
$this->assertEndOfStatement();
}
else {
$this->scanner
->consume(";");
}
return $this->completeNode($node);
}
return null;
}
/**
* Parses a return statement
*
* @return Node\ReturnStatement|null
*/
protected function parseReturnStatement() {
if ($token = $this->scanner
->consume("return")) {
$node = $this->createNode("ReturnStatement", $token);
if ($this->scanner
->noLineTerminators()) {
$argument = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($argument) {
$node->setArgument($argument);
}
}
$this->assertEndOfStatement();
return $this->completeNode($node);
}
return null;
}
/**
* Parses a labelled statement
*
* @return Node\LabeledStatement|null
*/
protected function parseLabelledStatement() {
if ($label = $this->parseIdentifier(static::$labelledIdentifier, ":")) {
$this->scanner
->consume(":");
if (($body = $this->parseStatement()) || ($body = $this->parseFunctionOrGeneratorDeclaration(false, false))) {
//Labelled functions are not allowed in strict mode
if ($body instanceof Node\FunctionDeclaration && $this->scanner
->getStrictMode()) {
$this->error("Labelled functions are not allowed in strict mode");
}
$node = $this->createNode("LabeledStatement", $label);
$node->setLabel($label);
$node->setBody($body);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a throw statement
*
* @return Node\ThrowStatement|null
*/
protected function parseThrowStatement() {
if ($token = $this->scanner
->consume("throw")) {
if ($this->scanner
->noLineTerminators() && ($argument = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression"))) {
$this->assertEndOfStatement();
$node = $this->createNode("ThrowStatement", $token);
$node->setArgument($argument);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a with statement
*
* @return Node\WithStatement|null
*/
protected function parseWithStatement() {
if ($token = $this->scanner
->consume("with")) {
if ($this->scanner
->consume("(") && ($object = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("WithStatement", $token);
$node->setObject($object);
$node->setBody($body);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a switch statement
*
* @return Node\SwitchStatement|null
*/
protected function parseSwitchStatement() {
if ($token = $this->scanner
->consume("switch")) {
if ($this->scanner
->consume("(") && ($discriminant = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && ($cases = $this->parseCaseBlock()) !== null) {
$node = $this->createNode("SwitchStatement", $token);
$node->setDiscriminant($discriminant);
$node->setCases($cases);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses the content of a switch statement
*
* @return Node\SwitchCase[]|null
*/
protected function parseCaseBlock() {
if ($this->scanner
->consume("{")) {
$parsedCasesAll = array(
$this->parseCaseClauses(),
$this->parseDefaultClause(),
$this->parseCaseClauses(),
);
if ($this->scanner
->consume("}")) {
$cases = array();
foreach ($parsedCasesAll as $parsedCases) {
if ($parsedCases) {
if (is_array($parsedCases)) {
$cases = array_merge($cases, $parsedCases);
}
else {
$cases[] = $parsedCases;
}
}
}
return $cases;
}
elseif ($this->parseDefaultClause()) {
$this->error("Multiple default clause in switch statement");
}
else {
$this->error();
}
}
return null;
}
/**
* Parses cases in a switch statement
*
* @return Node\SwitchCase[]|null
*/
protected function parseCaseClauses() {
$cases = array();
while ($case = $this->parseCaseClause()) {
$cases[] = $case;
}
return count($cases) ? $cases : null;
}
/**
* Parses a case in a switch statement
*
* @return Node\SwitchCase|null
*/
protected function parseCaseClause() {
if ($token = $this->scanner
->consume("case")) {
if (($test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(":")) {
$node = $this->createNode("SwitchCase", $token);
$node->setTest($test);
if ($consequent = $this->parseStatementList()) {
$node->setConsequent($consequent);
}
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses default case in a switch statement
*
* @return Node\SwitchCase|null
*/
protected function parseDefaultClause() {
if ($token = $this->scanner
->consume("default")) {
if ($this->scanner
->consume(":")) {
$node = $this->createNode("SwitchCase", $token);
if ($consequent = $this->parseStatementList()) {
$node->setConsequent($consequent);
}
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses an expression statement
*
* @return Node\ExpressionStatement|null
*/
protected function parseExpressionStatement() {
$lookaheadTokens = array(
"{",
"function",
"class",
array(
"let",
"[",
),
);
if ($this->features->asyncAwait) {
array_splice($lookaheadTokens, 3, 0, array(
array(
"async",
true,
),
));
}
if (!$this->scanner
->isBefore($lookaheadTokens, true) && ($expression = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression"))) {
$this->assertEndOfStatement();
$node = $this->createNode("ExpressionStatement", $expression);
$node->setExpression($expression);
return $this->completeNode($node);
}
return null;
}
/**
* Parses a do-while statement
*
* @return Node\DoWhileStatement|null
*/
protected function parseDoWhileStatement() {
if ($token = $this->scanner
->consume("do")) {
if (($body = $this->parseStatement()) && $this->scanner
->consume("while") && $this->scanner
->consume("(") && ($test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")")) {
$node = $this->createNode("DoWhileStatement", $token);
$node->setBody($body);
$node->setTest($test);
$node = $this->completeNode($node);
$this->scanner
->consume(";");
return $node;
}
$this->error();
}
return null;
}
/**
* Parses a while statement
*
* @return Node\WhileStatement|null
*/
protected function parseWhileStatement() {
if ($token = $this->scanner
->consume("while")) {
if ($this->scanner
->consume("(") && ($test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("WhileStatement", $token);
$node->setTest($test);
$node->setBody($body);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a for(var ...) statement
*
* @param Token $forToken Token that corresponds to the "for" keyword
*
* @return Node\Node|null
*/
protected function parseForVarStatement($forToken) {
if (!($varToken = $this->scanner
->consume("var"))) {
return null;
}
$state = $this->scanner
->getState();
if (($decl = $this->isolateContext(array(
"allowIn" => false,
), "parseVariableDeclarationList")) && ($varEndPosition = $this->scanner
->getPosition()) && $this->scanner
->consume(";")) {
$init = $this->createNode("VariableDeclaration", $varToken);
$init->setKind($init::KIND_VAR);
$init->setDeclarations($decl);
$init = $this->completeNode($init, $varEndPosition);
$test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($this->scanner
->consume(";")) {
$update = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForStatement", $forToken);
$node->setInit($init);
$node->setTest($test);
$node->setUpdate($update);
$node->setBody($body);
return $this->completeNode($node);
}
}
}
else {
$this->scanner
->setState($state);
if ($decl = $this->parseForBinding()) {
$init = null;
if ($this->features->forInInitializer && $decl->getId()
->getType() === "Identifier") {
$init = $this->parseInitializer();
}
if ($init) {
$decl->setInit($init);
$decl->location->end = $init->location->end;
}
$left = $this->createNode("VariableDeclaration", $varToken);
$left->setKind($left::KIND_VAR);
$left->setDeclarations(array(
$decl,
));
$left = $this->completeNode($left);
if ($this->scanner
->consume("in")) {
if ($init && $this->scanner
->getStrictMode()) {
$this->error("For-in variable initializer not allowed in " . "strict mode");
}
if (($right = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForInStatement", $forToken);
$node->setLeft($left);
$node->setRight($right);
$node->setBody($body);
return $this->completeNode($node);
}
}
elseif (!$init && $this->scanner
->consume("of")) {
if (($right = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForOfStatement", $forToken);
$node->setLeft($left);
$node->setRight($right);
$node->setBody($body);
return $this->completeNode($node);
}
}
}
}
$this->error();
}
/**
* Parses a for(let ...) or for(const ...) statement
*
* @param Token $forToken Token that corresponds to the "for" keyword
*
* @return Node\Node|null
*/
protected function parseForLetConstStatement($forToken) {
$afterBracketState = $this->scanner
->getState();
if (!($init = $this->parseForDeclaration())) {
return null;
}
if ($this->scanner
->consume("in")) {
if (($right = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForInStatement", $forToken);
$node->setLeft($init);
$node->setRight($right);
$node->setBody($body);
return $this->completeNode($node);
}
}
elseif ($this->scanner
->consume("of")) {
if (($right = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForOfStatement", $forToken);
$node->setLeft($init);
$node->setRight($right);
$node->setBody($body);
return $this->completeNode($node);
}
}
else {
$this->scanner
->setState($afterBracketState);
if ($init = $this->isolateContext(array(
"allowIn" => false,
), "parseLexicalDeclaration")) {
$test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($this->scanner
->consume(";")) {
$update = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForStatement", $forToken);
$node->setInit($init);
$node->setTest($test);
$node->setUpdate($update);
$node->setBody($body);
return $this->completeNode($node);
}
}
}
}
$this->error();
}
/**
* Parses a for statement that does not start with var, let or const
*
* @param Token $forToken Token that corresponds to the "for" keyword
* @param bool $hasAwait True if "for" is followed by "await"
*
* @return Node\Node|null
*/
protected function parseForNotVarLetConstStatement($forToken, $hasAwait) {
$state = $this->scanner
->getState();
$notBeforeSB = !$this->scanner
->isBefore(array(
array(
"let",
"[",
),
), true);
if ($notBeforeSB && (($init = $this->isolateContext(array(
"allowIn" => false,
), "parseExpression")) || true) && $this->scanner
->consume(";")) {
$test = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($this->scanner
->consume(";")) {
$update = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForStatement", $forToken);
$node->setInit($init);
$node->setTest($test);
$node->setUpdate($update);
$node->setBody($body);
return $this->completeNode($node);
}
}
}
else {
$this->scanner
->setState($state);
$beforeLetAsyncOf = $this->scanner
->isBefore(array(
"let",
array(
"async",
"of",
),
), true);
$left = $this->parseLeftHandSideExpression();
if ($left && $left->getType() === "ChainExpression") {
$this->error("Optional chain can't appear in left-hand side");
}
$left = $this->expressionToPattern($left);
if ($notBeforeSB && $left && $this->scanner
->consume("in")) {
if (($right = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForInStatement", $forToken);
$node->setLeft($left);
$node->setRight($right);
$node->setBody($body);
return $this->completeNode($node);
}
}
elseif (($hasAwait || !$beforeLetAsyncOf) && $left && $this->scanner
->consume("of")) {
if (($right = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression")) && $this->scanner
->consume(")") && ($body = $this->parseStatement())) {
$node = $this->createNode("ForOfStatement", $forToken);
$node->setLeft($left);
$node->setRight($right);
$node->setBody($body);
return $this->completeNode($node);
}
}
}
$this->error();
}
/**
* Parses do-while, while, for, for-in and for-of statements
*
* @return Node\Node|null
*/
protected function parseIterationStatement() {
if ($node = $this->parseWhileStatement()) {
return $node;
}
elseif ($node = $this->parseDoWhileStatement()) {
return $node;
}
elseif ($startForToken = $this->scanner
->consume("for")) {
$forAwait = false;
if ($this->features->asyncIterationGenerators && $this->context->allowAwait && $this->scanner
->consume("await")) {
$forAwait = true;
}
if ($this->scanner
->consume("(") && (($node = $this->parseForVarStatement($startForToken)) || ($node = $this->parseForLetConstStatement($startForToken)) || ($node = $this->parseForNotVarLetConstStatement($startForToken, $forAwait)))) {
if ($forAwait) {
if (!$node instanceof Node\ForOfStatement) {
$this->error("Async iteration is allowed only with for-of statements", $startForToken->location->start);
}
$node->setAwait(true);
}
return $node;
}
$this->error();
}
return null;
}
/**
* Checks if an async function can start from the current position. Returns
* the async token or null if not found
*
* @param bool $checkFn If false it won't check if the async keyword is
* followed by "function"
*
* @return Token
*/
protected function checkAsyncFunctionStart($checkFn = true) {
return ($asyncToken = $this->scanner
->getToken()) && $asyncToken->value === "async" && (!$checkFn || ($nextToken = $this->scanner
->getNextToken()) && $nextToken->value === "function") && $this->scanner
->noLineTerminators(true) ? $asyncToken : null;
}
/**
* Parses function or generator declaration
*
* @param bool $default Default mode
* @param bool $allowGenerator True to allow parsing of generators
*
* @return Node\FunctionDeclaration|null
*/
protected function parseFunctionOrGeneratorDeclaration($default = false, $allowGenerator = true) {
$async = null;
if ($this->features->asyncAwait && ($async = $this->checkAsyncFunctionStart())) {
$this->scanner
->consumeToken();
if (!$this->features->asyncIterationGenerators) {
$allowGenerator = false;
}
}
if ($token = $this->scanner
->consume("function")) {
$generator = $allowGenerator && $this->scanner
->consume("*");
$id = $this->parseIdentifier(static::$bindingIdentifier);
if ($generator || $async) {
$flags = array(
null,
);
if ($generator) {
$flags["allowYield"] = true;
}
if ($async) {
$flags["allowAwait"] = true;
}
}
else {
$flags = null;
}
if (($default || $id) && $this->scanner
->consume("(") && ($params = $this->isolateContext($flags, "parseFormalParameterList")) !== null && $this->scanner
->consume(")") && ($tokenBodyStart = $this->scanner
->consume("{")) && (($body = $this->isolateContext($flags, "parseFunctionBody")) || true) && $this->scanner
->consume("}")) {
$body->location->start = $tokenBodyStart->location->start;
$body->location->end = $this->scanner
->getPosition();
$node = $this->createNode("FunctionDeclaration", $async ?: $token);
if ($id) {
$node->setId($id);
}
$node->setParams($params);
$node->setBody($body);
$node->setGenerator($generator);
$node->setAsync((bool) $async);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses function or generator expression
*
* @return Node\FunctionExpression|null
*/
protected function parseFunctionOrGeneratorExpression() {
$allowGenerator = true;
$async = false;
if ($this->features->asyncAwait && ($async = $this->checkAsyncFunctionStart())) {
$this->scanner
->consumeToken();
if (!$this->features->asyncIterationGenerators) {
$allowGenerator = false;
}
}
if ($token = $this->scanner
->consume("function")) {
$generator = $allowGenerator && $this->scanner
->consume("*");
if ($generator || $async) {
$flags = array(
null,
);
if ($generator) {
$flags["allowYield"] = true;
}
if ($async) {
$flags["allowAwait"] = true;
}
}
else {
$flags = null;
}
$id = $this->isolateContext($flags, "parseIdentifier", array(
static::$bindingIdentifier,
));
if ($this->scanner
->consume("(") && ($params = $this->isolateContext($flags, "parseFormalParameterList")) !== null && $this->scanner
->consume(")") && ($tokenBodyStart = $this->scanner
->consume("{")) && (($body = $this->isolateContext($flags, "parseFunctionBody")) || true) && $this->scanner
->consume("}")) {
$body->location->start = $tokenBodyStart->location->start;
$body->location->end = $this->scanner
->getPosition();
$node = $this->createNode("FunctionExpression", $async ?: $token);
$node->setId($id);
$node->setParams($params);
$node->setBody($body);
$node->setGenerator($generator);
$node->setAsync((bool) $async);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses yield statement
*
* @return Node\YieldExpression|null
*/
protected function parseYieldExpression() {
if ($token = $this->scanner
->consume("yield")) {
$node = $this->createNode("YieldExpression", $token);
if ($this->scanner
->noLineTerminators()) {
$delegate = $this->scanner
->consume("*");
$argument = $this->isolateContext(array(
"allowYield" => true,
), "parseAssignmentExpression");
if ($argument) {
$node->setArgument($argument);
$node->setDelegate($delegate);
}
}
return $this->completeNode($node);
}
return null;
}
/**
* Parses a parameter list
*
* @return Node\Node[]|null
*/
protected function parseFormalParameterList() {
$hasComma = false;
$list = array();
while (($param = $this->parseBindingRestElement()) || ($param = $this->parseBindingElement())) {
$hasComma = false;
$list[] = $param;
if ($param->getType() === "RestElement") {
break;
}
elseif ($this->scanner
->consume(",")) {
$hasComma = true;
}
else {
break;
}
}
//Check if it ends with a comma, then check if the comma is a trailing comma,
//in that case throw an error if the trailing comma feature is not enabled
if ($hasComma && !$this->features->trailingCommaFunctionCallDeclaration) {
$token = $this->scanner
->getToken();
if ($token && $token->value === ")") {
$this->error();
}
}
return $list;
}
/**
* Parses a function body
*
* @return Node\BlockStatement[]|null
*/
protected function parseFunctionBody() {
$body = $this->isolateContext(array(
"allowReturn" => true,
), "parseStatementList", array(
true,
));
$node = $this->createNode("BlockStatement", $body ?: $this->scanner
->getPosition());
if ($body) {
$node->setBody($body);
}
return $this->completeNode($node);
}
/**
* Parses a class declaration
*
* @param bool $default Default mode
*
* @return Node\ClassDeclaration|null
*/
protected function parseClassDeclaration($default = false) {
if ($token = $this->scanner
->consume("class")) {
//Class declarations are strict mode by default
$prevStrict = $this->scanner
->getStrictMode();
$this->scanner
->setStrictMode(true);
$id = $this->parseIdentifier(static::$bindingIdentifier);
if (($default || $id) && ($tail = $this->parseClassTail())) {
$node = $this->createNode("ClassDeclaration", $token);
if ($id) {
$node->setId($id);
}
if ($tail[0]) {
$node->setSuperClass($tail[0]);
}
$node->setBody($tail[1]);
$this->scanner
->setStrictMode($prevStrict);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a class expression
*
* @return Node\ClassExpression|null
*/
protected function parseClassExpression() {
if ($token = $this->scanner
->consume("class")) {
//Class expressions are strict mode by default
$prevStrict = $this->scanner
->getStrictMode();
$this->scanner
->setStrictMode(true);
$id = $this->parseIdentifier(static::$bindingIdentifier);
$tail = $this->parseClassTail();
$node = $this->createNode("ClassExpression", $token);
if ($id) {
$node->setId($id);
}
if ($tail[0]) {
$node->setSuperClass($tail[0]);
}
$node->setBody($tail[1]);
$this->scanner
->setStrictMode($prevStrict);
return $this->completeNode($node);
}
return null;
}
/**
* Parses the code that comes after the class keyword and class name. The
* return value is an array where the first item is the extended class, if
* any, and the second value is the class body
*
* @return array|null
*/
protected function parseClassTail() {
$heritage = $this->parseClassHeritage();
if ($token = $this->scanner
->consume("{")) {
$body = $this->parseClassBody();
if ($this->scanner
->consume("}")) {
$body->location->start = $token->location->start;
$body->location->end = $this->scanner
->getPosition();
return array(
$heritage,
$body,
);
}
}
$this->error();
}
/**
* Parses the class extends part
*
* @return Node\Node|null
*/
protected function parseClassHeritage() {
if ($this->scanner
->consume("extends")) {
if ($superClass = $this->parseLeftHandSideExpression()) {
return $superClass;
}
$this->error();
}
return null;
}
/**
* Parses the class body
*
* @return Node\ClassBody|null
*/
protected function parseClassBody() {
$body = $this->parseClassElementList();
$node = $this->createNode("ClassBody", $body ?: $this->scanner
->getPosition());
if ($body) {
$node->setBody($body);
}
return $this->completeNode($node);
}
/**
* Parses class elements list
*
* @return Node\MethodDefinition[]|null
*/
protected function parseClassElementList() {
$items = array();
while ($item = $this->parseClassElement()) {
if ($item !== true) {
$items[] = $item;
}
}
return count($items) ? $items : null;
}
/**
* Parses a class elements
*
* @return Node\MethodDefinition|Node\PropertyDefinition|Node\StaticBlock|bool|null
*/
protected function parseClassElement() {
if ($this->scanner
->consume(";")) {
return true;
}
if ($this->features->classStaticBlock && $this->scanner
->isBefore(array(
array(
"static",
"{",
),
), true)) {
return $this->parseClassStaticBlock();
}
$staticToken = null;
$state = $this->scanner
->getState();
//This code handles the case where "static" is the method name
if (!$this->scanner
->isBefore(array(
array(
"static",
"(",
),
), true)) {
$staticToken = $this->scanner
->consume("static");
}
if ($def = $this->parseMethodDefinition()) {
if ($staticToken) {
$def->setStatic(true);
$def->location->start = $staticToken->location->start;
}
return $def;
}
else {
if ($this->features->classFields) {
if ($field = $this->parseFieldDefinition()) {
if ($staticToken) {
$field->setStatic(true);
$field->location->start = $staticToken->location->start;
}
}
elseif ($staticToken) {
//Handle the case when "static" is the field name
$this->scanner
->setState($state);
$field = $this->parseFieldDefinition();
}
return $field;
}
elseif ($staticToken) {
$this->error();
}
}
return null;
}
/**
* Parses a let or const declaration
*
* @return Node\VariableDeclaration|null
*/
protected function parseLexicalDeclaration() {
$state = $this->scanner
->getState();
if ($token = $this->scanner
->consumeOneOf(array(
"let",
"const",
))) {
$declarations = $this->charSeparatedListOf("parseVariableDeclaration");
if ($declarations) {
$this->assertEndOfStatement();
$node = $this->createNode("VariableDeclaration", $token);
$node->setKind($token->value);
$node->setDeclarations($declarations);
return $this->completeNode($node);
}
// "let" can be used as variable name in non-strict mode
if ($this->scanner
->getStrictMode() || $token->value !== "let") {
$this->error();
}
else {
$this->scanner
->setState($state);
}
}
return null;
}
/**
* Parses a var declaration
*
* @return Node\VariableDeclaration|null
*/
protected function parseVariableStatement() {
if ($token = $this->scanner
->consume("var")) {
$declarations = $this->isolateContext(array(
"allowIn" => true,
), "parseVariableDeclarationList");
if ($declarations) {
$this->assertEndOfStatement();
$node = $this->createNode("VariableDeclaration", $token);
$node->setKind($node::KIND_VAR);
$node->setDeclarations($declarations);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses an variable declarations
*
* @return Node\VariableDeclarator[]|null
*/
protected function parseVariableDeclarationList() {
return $this->charSeparatedListOf("parseVariableDeclaration");
}
/**
* Parses a variable declarations
*
* @return Node\VariableDeclarator|null
*/
protected function parseVariableDeclaration() {
if ($id = $this->parseIdentifier(static::$bindingIdentifier)) {
$node = $this->createNode("VariableDeclarator", $id);
$node->setId($id);
if ($init = $this->parseInitializer()) {
$node->setInit($init);
}
return $this->completeNode($node);
}
elseif ($id = $this->parseBindingPattern()) {
if ($init = $this->parseInitializer()) {
$node = $this->createNode("VariableDeclarator", $id);
$node->setId($id);
$node->setInit($init);
return $this->completeNode($node);
}
}
return null;
}
/**
* Parses a let or const declaration in a for statement definition
*
* @return Node\VariableDeclaration|null
*/
protected function parseForDeclaration() {
$state = $this->scanner
->getState();
if ($token = $this->scanner
->consumeOneOf(array(
"let",
"const",
))) {
if ($declaration = $this->parseForBinding()) {
$node = $this->createNode("VariableDeclaration", $token);
$node->setKind($token->value);
$node->setDeclarations(array(
$declaration,
));
return $this->completeNode($node);
}
// "let" can be used as variable name in non-strict mode
if ($this->scanner
->getStrictMode() || $token->value !== "let") {
$this->error();
}
else {
$this->scanner
->setState($state);
}
}
return null;
}
/**
* Parses a binding pattern or an identifier that come after a const or let
* declaration in a for statement definition
*
* @return Node\VariableDeclarator|null
*/
protected function parseForBinding() {
if (($id = $this->parseIdentifier(static::$bindingIdentifier)) || ($id = $this->parseBindingPattern())) {
$node = $this->createNode("VariableDeclarator", $id);
$node->setId($id);
return $this->completeNode($node);
}
return null;
}
/**
* Parses a module item
*
* @return Node\Node|null
*/
protected function parseModuleItem() {
if ($item = $this->parseImportDeclaration()) {
return $item;
}
elseif ($item = $this->parseExportDeclaration()) {
return $item;
}
elseif ($item = $this->isolateContext(array(
"allowYield" => false,
"allowReturn" => false,
"allowAwait" => $this->features->topLevelAwait,
), "parseStatementListItem")) {
return $item;
}
return null;
}
/**
* Parses the from keyword and the following string in import and export
* declarations
*
* @return Node\StringLiteral|null
*/
protected function parseFromClause() {
if ($this->scanner
->consume("from")) {
if ($spec = $this->parseStringLiteral()) {
return $spec;
}
$this->error();
}
return null;
}
/**
* Parses an export declaration
*
* @return Node\ModuleDeclaration|null
*/
protected function parseExportDeclaration() {
if ($token = $this->scanner
->consume("export")) {
if ($this->scanner
->consume("*")) {
$exported = null;
if ($this->features->exportedNameInExportAll && $this->scanner
->consume("as")) {
$exported = $this->parseModuleExportName();
if (!$exported) {
$this->error();
}
}
if ($source = $this->parseFromClause()) {
$this->assertEndOfStatement();
$node = $this->createNode("ExportAllDeclaration", $token);
$node->setSource($source);
$node->setExported($exported);
return $this->completeNode($node);
}
}
elseif ($this->scanner
->consume("default")) {
$lookaheadTokens = array(
"function",
"class",
);
if ($this->features->asyncAwait) {
$lookaheadTokens[] = array(
"async",
true,
);
}
if (($declaration = $this->isolateContext(array(
"allowAwait" => $this->features->topLevelAwait,
), "parseFunctionOrGeneratorDeclaration", array(
true,
))) || ($declaration = $this->isolateContext(array(
"allowAwait" => $this->features->topLevelAwait,
), "parseClassDeclaration", array(
true,
)))) {
$node = $this->createNode("ExportDefaultDeclaration", $token);
$node->setDeclaration($declaration);
return $this->completeNode($node);
}
elseif (!$this->scanner
->isBefore($lookaheadTokens, $this->features->asyncAwait) && ($declaration = $this->isolateContext(array(
"allowIn" => true,
"allowAwait" => $this->features->topLevelAwait,
), "parseAssignmentExpression"))) {
$this->assertEndOfStatement();
$node = $this->createNode("ExportDefaultDeclaration", $token);
$node->setDeclaration($declaration);
return $this->completeNode($node);
}
}
elseif (($specifiers = $this->parseExportClause()) !== null) {
$node = $this->createNode("ExportNamedDeclaration", $token);
$node->setSpecifiers($specifiers);
if ($source = $this->parseFromClause()) {
$node->setSource($source);
}
$this->assertEndOfStatement();
return $this->completeNode($node);
}
elseif (($dec = $this->isolateContext(array(
"allowAwait" => $this->features->topLevelAwait,
), "parseVariableStatement")) || ($dec = $this->isolateContext(array(
"allowAwait" => $this->features->topLevelAwait,
), "parseDeclaration"))) {
$node = $this->createNode("ExportNamedDeclaration", $token);
$node->setDeclaration($dec);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses an export clause
*
* @return Node\ExportSpecifier[]|null
*/
protected function parseExportClause() {
if ($this->scanner
->consume("{")) {
$list = array();
while ($spec = $this->parseExportSpecifier()) {
$list[] = $spec;
if (!$this->scanner
->consume(",")) {
break;
}
}
if ($this->scanner
->consume("}")) {
return $list;
}
$this->error();
}
return null;
}
/**
* Parses an export specifier
*
* @return Node\ExportSpecifier|null
*/
protected function parseExportSpecifier() {
if ($local = $this->parseModuleExportName()) {
$node = $this->createNode("ExportSpecifier", $local);
$node->setLocal($local);
if ($this->scanner
->consume("as")) {
if ($exported = $this->parseModuleExportName()) {
$node->setExported($exported);
return $this->completeNode($node);
}
$this->error();
}
else {
$node->setExported($local);
return $this->completeNode($node);
}
}
return null;
}
/**
* Parses an export name
*
* @return Node\Identifier|Node\StringLiteral|null
*/
protected function parseModuleExportName() {
if ($name = $this->parseIdentifier(static::$identifierName)) {
return $name;
}
elseif ($this->features->arbitraryModuleNSNames && ($name = $this->parseStringLiteral())) {
return $name;
}
return null;
}
/**
* Parses an import declaration
*
* @return Node\ModuleDeclaration|null
*/
protected function parseImportDeclaration() {
//Delay parsing of dynamic import so that it is handled
//by the relative method
if ($this->features->dynamicImport && $this->scanner
->isBefore(array(
array(
"import",
"(",
),
), true)) {
return null;
}
//Delay parsing of import.meta so that it is handled
//by the relative method
if ($this->features->importMeta && $this->scanner
->isBefore(array(
array(
"import",
".",
),
), true)) {
return null;
}
if ($token = $this->scanner
->consume("import")) {
if ($source = $this->parseStringLiteral()) {
$this->assertEndOfStatement();
$node = $this->createNode("ImportDeclaration", $token);
$node->setSource($source);
return $this->completeNode($node);
}
elseif (($specifiers = $this->parseImportClause()) !== null && ($source = $this->parseFromClause())) {
$this->assertEndOfStatement();
$node = $this->createNode("ImportDeclaration", $token);
$node->setSpecifiers($specifiers);
$node->setSource($source);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses an import clause
*
* @return array|null
*/
protected function parseImportClause() {
if ($spec = $this->parseNameSpaceImport()) {
return array(
$spec,
);
}
elseif (($specs = $this->parseNamedImports()) !== null) {
return $specs;
}
elseif ($spec = $this->parseIdentifier(static::$importedBinding)) {
$node = $this->createNode("ImportDefaultSpecifier", $spec);
$node->setLocal($spec);
$ret = array(
$this->completeNode($node),
);
if ($this->scanner
->consume(",")) {
if ($spec = $this->parseNameSpaceImport()) {
$ret[] = $spec;
return $ret;
}
elseif (($specs = $this->parseNamedImports()) !== null) {
return array_merge($ret, $specs);
}
$this->error();
}
else {
return $ret;
}
}
return null;
}
/**
* Parses a namespace import
*
* @return Node\ImportNamespaceSpecifier|null
*/
protected function parseNameSpaceImport() {
if ($token = $this->scanner
->consume("*")) {
if ($this->scanner
->consume("as") && ($local = $this->parseIdentifier(static::$identifierReference))) {
$node = $this->createNode("ImportNamespaceSpecifier", $token);
$node->setLocal($local);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a named imports
*
* @return Node\ImportSpecifier[]|null
*/
protected function parseNamedImports() {
if ($this->scanner
->consume("{")) {
$list = array();
while ($spec = $this->parseImportSpecifier()) {
$list[] = $spec;
if (!$this->scanner
->consume(",")) {
break;
}
}
if ($this->scanner
->consume("}")) {
return $list;
}
$this->error();
}
return null;
}
/**
* Parses an import specifier
*
* @return Node\ImportSpecifier|null
*/
protected function parseImportSpecifier() {
$requiredAs = false;
$imported = $this->parseIdentifier(static::$importedBinding);
if (!$imported) {
$imported = $this->parseModuleExportName();
if (!$imported) {
return null;
}
$requiredAs = true;
}
$node = $this->createNode("ImportSpecifier", $imported);
$node->setImported($imported);
if ($this->scanner
->consume("as")) {
if (!($local = $this->parseIdentifier(static::$importedBinding))) {
$this->error();
}
$node->setLocal($local);
}
elseif ($requiredAs) {
$this->error();
}
else {
$node->setLocal($imported);
}
return $this->completeNode($node);
}
/**
* Parses a binding pattern
*
* @return Node\ArrayPattern|Node\ObjectPattern|null
*/
protected function parseBindingPattern() {
if ($pattern = $this->parseObjectBindingPattern()) {
return $pattern;
}
elseif ($pattern = $this->parseArrayBindingPattern()) {
return $pattern;
}
return null;
}
/**
* Parses an elisions sequence. It returns the number of elisions or null
* if no elision has been found
*
* @return int
*/
protected function parseElision() {
$count = 0;
while ($this->scanner
->consume(",")) {
$count++;
}
return $count ?: null;
}
/**
* Parses an array binding pattern
*
* @return Node\ArrayPattern|null
*/
protected function parseArrayBindingPattern() {
if ($token = $this->scanner
->consume("[")) {
$elements = array();
while (true) {
if ($elision = $this->parseElision()) {
$elements = array_merge($elements, array_fill(0, $elision, null));
}
if ($element = $this->parseBindingElement()) {
$elements[] = $element;
if (!$this->scanner
->consume(",")) {
break;
}
}
elseif ($rest = $this->parseBindingRestElement()) {
$elements[] = $rest;
break;
}
else {
break;
}
}
if ($this->scanner
->consume("]")) {
$node = $this->createNode("ArrayPattern", $token);
$node->setElements($elements);
return $this->completeNode($node);
}
}
return null;
}
/**
* Parses a rest element
*
* @return Node\RestElement|null
*/
protected function parseBindingRestElement() {
if ($token = $this->scanner
->consume("...")) {
if (($argument = $this->parseIdentifier(static::$bindingIdentifier)) || ($argument = $this->parseBindingPattern())) {
$node = $this->createNode("RestElement", $token);
$node->setArgument($argument);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a binding element
*
* @return Node\AssignmentPattern|Node\Identifier|null
*/
protected function parseBindingElement() {
if ($el = $this->parseSingleNameBinding()) {
return $el;
}
elseif ($left = $this->parseBindingPattern()) {
$right = $this->isolateContext(array(
"allowIn" => true,
), "parseInitializer");
if ($right) {
$node = $this->createNode("AssignmentPattern", $left);
$node->setLeft($left);
$node->setRight($right);
return $this->completeNode($node);
}
else {
return $left;
}
}
return null;
}
/**
* Parses single name binding
*
* @return Node\AssignmentPattern|Node\Identifier|null
*/
protected function parseSingleNameBinding() {
if ($left = $this->parseIdentifier(static::$bindingIdentifier)) {
$right = $this->isolateContext(array(
"allowIn" => true,
), "parseInitializer");
if ($right) {
$node = $this->createNode("AssignmentPattern", $left);
$node->setLeft($left);
$node->setRight($right);
return $this->completeNode($node);
}
else {
return $left;
}
}
return null;
}
/**
* Parses a property name. The returned value is an array where there first
* element is the property name and the second element is a boolean
* indicating if it's a computed property
*
* @return array|null
*/
protected function parsePropertyName() {
if ($token = $this->scanner
->consume("[")) {
if (($name = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression")) && $this->scanner
->consume("]")) {
return array(
$name,
true,
$token,
);
}
$this->error();
}
elseif ($name = $this->parseIdentifier(static::$identifierName)) {
return array(
$name,
false,
);
}
elseif ($name = $this->parseStringLiteral()) {
return array(
$name,
false,
);
}
elseif ($name = $this->parseNumericLiteral()) {
return array(
$name,
false,
);
}
return null;
}
/**
* Parses a property name. The returned value is an array where there first
* element is the property name and the second element is a boolean
* indicating if it's a computed property
*
* @return array|null
*/
protected function parseClassElementName() {
if ($this->features->privateMethodsAndFields && ($name = $this->parsePrivateIdentifier())) {
return array(
$name,
false,
);
}
return $this->parsePropertyName();
}
/**
* Parses a field definition
*
* @return Node\StaticBlock
*/
protected function parseClassStaticBlock() {
$staticToken = $this->scanner
->consume("static");
$this->scanner
->consume("{");
$statements = $this->isolateContext(array(
"allowAwait" => true,
), "parseStatementList");
if ($this->scanner
->consume("}")) {
$node = $this->createNode("StaticBlock", $staticToken);
if ($statements) {
$node->setBody($statements);
}
return $this->completeNode($node);
}
$this->error();
}
/**
* Parses a field definition
*
* @return Node\PropertyDefinition|null
*/
protected function parseFieldDefinition() {
$state = $this->scanner
->getState();
if ($prop = $this->parseClassElementName()) {
$value = $this->isolateContext(array(
"allowIn" => true,
), "parseInitializer");
$this->assertEndOfStatement();
$node = $this->createNode("PropertyDefinition", $prop);
$node->setKey($prop[0]);
if ($value) {
$node->setValue($value);
}
$node->setComputed($prop[1]);
return $this->completeNode($node);
}
$this->scanner
->setState($state);
return null;
}
/**
* Parses a method definition
*
* @return Node\MethodDefinition|null
*/
protected function parseMethodDefinition() {
$state = $this->scanner
->getState();
$generator = $error = $async = false;
$position = null;
$kind = Node\MethodDefinition::KIND_METHOD;
if ($token = $this->scanner
->consume("get")) {
$position = $token;
$kind = Node\MethodDefinition::KIND_GET;
}
elseif ($token = $this->scanner
->consume("set")) {
$position = $token;
$kind = Node\MethodDefinition::KIND_SET;
}
elseif ($token = $this->scanner
->consume("*")) {
$position = $token;
$error = true;
$generator = true;
}
elseif ($this->features->asyncAwait && ($token = $this->checkAsyncFunctionStart(false))) {
$this->scanner
->consumeToken();
$position = $token;
$error = true;
$async = true;
if ($this->features->asyncIterationGenerators && $this->scanner
->consume("*")) {
$generator = true;
}
}
//Handle the case where get, set and async are methods name and not the
//definition of a getter/setter or the start of an async function
if (($kind !== Node\MethodDefinition::KIND_METHOD || $async && !$generator) && $this->scanner
->consume("(")) {
$this->scanner
->setState($state);
$kind = Node\MethodDefinition::KIND_METHOD;
$error = $async = false;
}
if ($prop = $this->parseClassElementName()) {
if (!$position) {
$position = isset($prop[2]) ? $prop[2] : $prop[0];
}
if ($tokenFn = $this->scanner
->consume("(")) {
if ($generator || $async) {
$flags = array(
null,
);
if ($generator) {
$flags["allowYield"] = true;
}
if ($async) {
$flags["allowAwait"] = true;
}
}
else {
$flags = null;
}
$error = true;
$params = array();
if ($kind === Node\MethodDefinition::KIND_SET) {
$params = $this->isolateContext(null, "parseBindingElement");
if ($params) {
$params = array(
$params,
);
}
}
elseif ($kind === Node\MethodDefinition::KIND_METHOD) {
$params = $this->isolateContext($flags, "parseFormalParameterList");
}
if ($params !== null && $this->scanner
->consume(")") && ($tokenBodyStart = $this->scanner
->consume("{")) && (($body = $this->isolateContext($flags, "parseFunctionBody")) || true) && $this->scanner
->consume("}")) {
if ($prop[0] instanceof Node\Identifier && $prop[0]->getName() === "constructor") {
$kind = Node\MethodDefinition::KIND_CONSTRUCTOR;
}
$body->location->start = $tokenBodyStart->location->start;
$body->location->end = $this->scanner
->getPosition();
$nodeFn = $this->createNode("FunctionExpression", $tokenFn);
$nodeFn->setParams($params);
$nodeFn->setBody($body);
$nodeFn->setGenerator($generator);
$nodeFn->setAsync($async);
$node = $this->createNode("MethodDefinition", $position);
$node->setKey($prop[0]);
$node->setValue($this->completeNode($nodeFn));
$node->setKind($kind);
$node->setComputed($prop[1]);
return $this->completeNode($node);
}
}
}
elseif ($this->features->classFields && $async && !$generator) {
$this->scanner
->setState($state);
$error = $async = false;
}
if ($error) {
$this->error();
}
else {
$this->scanner
->setState($state);
}
return null;
}
/**
* Parses parameters in an arrow function. If the parameters are wrapped in
* round brackets, the returned value is an array where the first element
* is the parameters list and the second element is the open round brackets,
* this is needed to know the start position
*
* @return Node\Identifier|array|null
*/
protected function parseArrowParameters() {
if ($param = $this->parseIdentifier(static::$bindingIdentifier, "=>")) {
return $param;
}
elseif ($token = $this->scanner
->consume("(")) {
$params = $this->parseFormalParameterList();
if ($params !== null && $this->scanner
->consume(")")) {
return array(
$params,
$token,
);
}
}
return null;
}
/**
* Parses the body of an arrow function. The returned value is an array
* where the first element is the function body and the second element is
* a boolean indicating if the body is wrapped in curly braces
*
* @param bool $async Async body mode
*
* @return array|null
*/
protected function parseConciseBody($async = false) {
if ($token = $this->scanner
->consume("{")) {
if (($body = $this->isolateContext($async ? array(
null,
"allowAwait" => true,
) : null, "parseFunctionBody")) && $this->scanner
->consume("}")) {
$body->location->start = $token->location->start;
$body->location->end = $this->scanner
->getPosition();
return array(
$body,
false,
);
}
$this->error();
}
elseif (!$this->scanner
->isBefore(array(
"{",
)) && ($body = $this->isolateContext($this->features->asyncAwait ? array(
"allowYield" => false,
"allowAwait" => $async,
) : array(
"allowYield" => false,
), "parseAssignmentExpression"))) {
return array(
$body,
true,
);
}
return null;
}
/**
* Parses an arrow function
*
* @return Node\ArrowFunctionExpression|null
*/
protected function parseArrowFunction() {
$state = $this->scanner
->getState();
$async = false;
if ($this->features->asyncAwait && ($async = $this->checkAsyncFunctionStart(false))) {
$this->scanner
->consumeToken();
}
if (($params = $this->parseArrowParameters()) !== null) {
if ($this->scanner
->noLineTerminators() && $this->scanner
->consume("=>")) {
if ($body = $this->parseConciseBody((bool) $async)) {
if (is_array($params)) {
$pos = $params[1];
$params = $params[0];
}
else {
$pos = $params;
$params = array(
$params,
);
}
if ($async) {
$pos = $async;
}
$node = $this->createNode("ArrowFunctionExpression", $pos);
$node->setParams($params);
$node->setBody($body[0]);
$node->setExpression($body[1]);
$node->setAsync((bool) $async);
return $this->completeNode($node);
}
$this->error();
}
}
$this->scanner
->setState($state);
return null;
}
/**
* Parses an object literal
*
* @return Node\ObjectExpression|null
*/
protected function parseObjectLiteral() {
if ($token = $this->scanner
->consume("{")) {
$properties = array();
while ($prop = $this->parsePropertyDefinition()) {
$properties[] = $prop;
if (!$this->scanner
->consume(",")) {
break;
}
}
if ($this->scanner
->consume("}")) {
$node = $this->createNode("ObjectExpression", $token);
if ($properties) {
$node->setProperties($properties);
}
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a property in an object literal
*
* @return Node\Property|null
*/
protected function parsePropertyDefinition() {
if ($this->features->restSpreadProperties && ($prop = $this->parseSpreadElement())) {
return $prop;
}
$state = $this->scanner
->getState();
if (($property = $this->parsePropertyName()) && $this->scanner
->consume(":")) {
$value = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression");
if ($value) {
$startPos = isset($property[2]) ? $property[2] : $property[0];
$node = $this->createNode("Property", $startPos);
$node->setKey($property[0]);
$node->setValue($value);
$node->setComputed($property[1]);
return $this->completeNode($node);
}
$this->error();
}
$this->scanner
->setState($state);
if ($property = $this->parseMethodDefinition()) {
$node = $this->createNode("Property", $property);
$node->setKey($property->getKey());
$node->setValue($property->getValue());
$node->setComputed($property->getComputed());
$kind = $property->getKind();
if ($kind !== Node\MethodDefinition::KIND_GET && $kind !== Node\MethodDefinition::KIND_SET) {
$node->setMethod(true);
$node->setKind(Node\Property::KIND_INIT);
}
else {
$node->setKind($kind);
}
return $this->completeNode($node);
}
elseif ($key = $this->parseIdentifier(static::$identifierReference)) {
$node = $this->createNode("Property", $key);
$node->setShorthand(true);
$node->setKey($key);
$value = $this->isolateContext(array(
"allowIn" => true,
), "parseInitializer");
$node->setValue($value ?: $key);
return $this->completeNode($node);
}
return null;
}
/**
* Parses an initializer
*
* @return Node\Node|null
*/
protected function parseInitializer() {
if ($this->scanner
->consume("=")) {
if ($value = $this->parseAssignmentExpression()) {
return $value;
}
$this->error();
}
return null;
}
/**
* Parses an object binding pattern
*
* @return Node\ObjectPattern|null
*/
protected function parseObjectBindingPattern() {
$state = $this->scanner
->getState();
if ($token = $this->scanner
->consume("{")) {
$properties = array();
while ($prop = $this->parseBindingProperty()) {
$properties[] = $prop;
if (!$this->scanner
->consume(",")) {
break;
}
}
if ($this->features->restSpreadProperties && ($rest = $this->parseRestProperty())) {
$properties[] = $rest;
}
if ($this->scanner
->consume("}")) {
$node = $this->createNode("ObjectPattern", $token);
if ($properties) {
$node->setProperties($properties);
}
return $this->completeNode($node);
}
$this->scanner
->setState($state);
}
return null;
}
/**
* Parses a rest property
*
* @return Node\RestElement|null
*/
protected function parseRestProperty() {
$state = $this->scanner
->getState();
if ($token = $this->scanner
->consume("...")) {
if ($argument = $this->parseIdentifier(static::$bindingIdentifier)) {
$node = $this->createNode("RestElement", $token);
$node->setArgument($argument);
return $this->completeNode($node);
}
$this->scanner
->setState($state);
}
return null;
}
/**
* Parses a property in an object binding pattern
*
* @return Node\AssignmentProperty|null
*/
protected function parseBindingProperty() {
$state = $this->scanner
->getState();
if (($key = $this->parsePropertyName()) && $this->scanner
->consume(":")) {
if ($value = $this->parseBindingElement()) {
$startPos = isset($key[2]) ? $key[2] : $key[0];
$node = $this->createNode("AssignmentProperty", $startPos);
$node->setKey($key[0]);
$node->setComputed($key[1]);
$node->setValue($value);
return $this->completeNode($node);
}
$this->scanner
->setState($state);
return null;
}
$this->scanner
->setState($state);
if ($property = $this->parseSingleNameBinding()) {
$node = $this->createNode("AssignmentProperty", $property);
$node->setShorthand(true);
if ($property instanceof Node\AssignmentPattern) {
$node->setKey($property->getLeft());
}
else {
$node->setKey($property);
}
$node->setValue($property);
return $this->completeNode($node);
}
return null;
}
/**
* Parses an expression
*
* @return Node\Node|null
*/
protected function parseExpression() {
$list = $this->charSeparatedListOf("parseAssignmentExpression");
if (!$list) {
return null;
}
elseif (count($list) === 1) {
return $list[0];
}
else {
$node = $this->createNode("SequenceExpression", $list);
$node->setExpressions($list);
return $this->completeNode($node);
}
}
/**
* Parses an assignment expression
*
* @return Node\Node|null
*/
protected function parseAssignmentExpression() {
if ($expr = $this->parseArrowFunction()) {
return $expr;
}
elseif ($this->context->allowYield && ($expr = $this->parseYieldExpression())) {
return $expr;
}
elseif ($expr = $this->parseConditionalExpression()) {
$exprTypes = array(
"ConditionalExpression",
"LogicalExpression",
"BinaryExpression",
"UpdateExpression",
"UnaryExpression",
);
if (!in_array($expr->getType(), $exprTypes)) {
$operators = $this->assignmentOperators;
if ($operator = $this->scanner
->consumeOneOf($operators)) {
if ($expr->getType() === "ChainExpression") {
$this->error("Optional chain can't appear in left-hand side");
}
$right = $this->parseAssignmentExpression();
if ($right) {
$node = $this->createNode("AssignmentExpression", $expr);
$node->setLeft($this->expressionToPattern($expr));
$node->setOperator($operator->value);
$node->setRight($right);
return $this->completeNode($node);
}
$this->error();
}
}
return $expr;
}
return null;
}
/**
* Parses a conditional expression
*
* @return Node\Node|null
*/
protected function parseConditionalExpression() {
if ($test = $this->parseLogicalBinaryExpression()) {
if ($this->scanner
->consume("?")) {
$consequent = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression");
if ($consequent && $this->scanner
->consume(":") && ($alternate = $this->parseAssignmentExpression())) {
$node = $this->createNode("ConditionalExpression", $test);
$node->setTest($test);
$node->setConsequent($consequent);
$node->setAlternate($alternate);
return $this->completeNode($node);
}
$this->error();
}
else {
return $test;
}
}
return null;
}
/**
* Parses a logical or a binary expression
*
* @return Node\Node|null
*/
protected function parseLogicalBinaryExpression() {
$operators = $this->logicalBinaryOperators;
if (!$this->context->allowIn) {
unset($operators["in"]);
}
if (!($exp = $this->parseUnaryExpression())) {
if (!$this->features->classFieldsPrivateIn || !$this->context->allowIn) {
return null;
}
//Support "#private in x" syntax
$state = $this->scanner
->getState();
if (!($exp = $this->parsePrivateIdentifier()) || !$this->scanner
->isBefore(array(
"in",
))) {
if ($exp) {
$this->scanner
->setState($state);
}
return null;
}
}
$list = array(
$exp,
);
$coalescingFound = $andOrFound = false;
while ($token = $this->scanner
->consumeOneOf(array_keys($operators))) {
$op = $token->value;
// Coalescing and logical expressions can't be used together
if ($op === "??") {
$coalescingFound = true;
}
elseif ($op === "&&" || $op === "||") {
$andOrFound = true;
}
if ($coalescingFound && $andOrFound) {
$this->error("Logical expressions must be wrapped in parentheses when " . "inside coalesce expressions");
}
if (!($exp = $this->parseUnaryExpression())) {
$this->error();
}
$list[] = $op;
$list[] = $exp;
}
$len = count($list);
if ($len > 1) {
$maxGrade = max($operators);
for ($grade = $maxGrade; $grade >= 0; $grade--) {
$class = $grade < 2 ? "LogicalExpression" : "BinaryExpression";
$r2l = $grade === 10;
//Exponentiation operator must be parsed right to left
if ($r2l) {
$i = $len - 2;
$step = -2;
}
else {
$i = 1;
$step = 2;
}
for (; $r2l && $i > 0 || !$r2l && $i < $len; $i += $step) {
if ($operators[$list[$i]] === $grade) {
$node = $this->createNode($class, $list[$i - 1]);
$node->setLeft($list[$i - 1]);
$node->setOperator($list[$i]);
$node->setRight($list[$i + 1]);
$node = $this->completeNode($node, $list[$i + 1]->location->end);
array_splice($list, $i - 1, 3, array(
$node,
));
if (!$r2l) {
$i -= $step;
}
$len = count($list);
}
}
}
}
return $list[0];
}
/**
* Parses a unary expression
*
* @return Node\Node|null
*/
protected function parseUnaryExpression() {
$operators = $this->unaryOperators;
if ($this->features->asyncAwait && $this->context->allowAwait) {
$operators[] = "await";
}
if ($expr = $this->parsePostfixExpression()) {
return $expr;
}
elseif ($token = $this->scanner
->consumeOneOf($operators)) {
if ($argument = $this->parseUnaryExpression()) {
$op = $token->value;
//Deleting a variable without accessing its properties is a
//syntax error in strict mode
if ($op === "delete" && $this->scanner
->getStrictMode() && $argument instanceof Node\Identifier) {
$this->error("Deleting an unqualified identifier is not allowed in strict mode");
}
if ($this->features->asyncAwait && $op === "await") {
$node = $this->createNode("AwaitExpression", $token);
}
else {
if ($op === "++" || $op === "--") {
if ($argument->getType() === "ChainExpression") {
$this->error("Optional chain can't appear in left-hand side");
}
$node = $this->createNode("UpdateExpression", $token);
$node->setPrefix(true);
}
else {
$node = $this->createNode("UnaryExpression", $token);
}
$node->setOperator($op);
}
$node->setArgument($argument);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a postfix expression
*
* @return Node\Node|null
*/
protected function parsePostfixExpression() {
if ($argument = $this->parseLeftHandSideExpression()) {
if ($this->scanner
->noLineTerminators() && ($token = $this->scanner
->consumeOneOf($this->postfixOperators))) {
if ($argument->getType() === "ChainExpression") {
$this->error("Optional chain can't appear in left-hand side");
}
$node = $this->createNode("UpdateExpression", $argument);
$node->setOperator($token->value);
$node->setArgument($argument);
return $this->completeNode($node);
}
return $argument;
}
return null;
}
/**
* Parses a left hand side expression
*
* @return Node\Node|null
*/
protected function parseLeftHandSideExpression() {
$object = null;
$newTokens = array();
//Parse all occurrences of "new"
if ($this->scanner
->isBefore(array(
"new",
))) {
while ($newToken = $this->scanner
->consume("new")) {
if ($this->scanner
->consume(".")) {
//new.target
if (!$this->scanner
->consume("target")) {
$this->error();
}
$node = $this->createNode("MetaProperty", $newToken);
$node->setMeta("new");
$node->setProperty("target");
$object = $this->completeNode($node);
break;
}
$newTokens[] = $newToken;
}
}
elseif ($this->features->importMeta && $this->sourceType === \Peast\Peast::SOURCE_TYPE_MODULE && $this->scanner
->isBefore(array(
array(
"import",
".",
),
), true)) {
//import.meta
$importToken = $this->scanner
->consume("import");
$this->scanner
->consume(".");
if (!$this->scanner
->consume("meta")) {
$this->error();
}
$node = $this->createNode("MetaProperty", $importToken);
$node->setMeta("import");
$node->setProperty("meta");
$object = $this->completeNode($node);
}
$newTokensCount = count($newTokens);
if (!$object && !($object = $this->parseSuperPropertyOrCall()) && !($this->features->dynamicImport && ($object = $this->parseImportCall())) && !($object = $this->parsePrimaryExpression())) {
if ($newTokensCount) {
$this->error();
}
return null;
}
$valid = true;
$optionalChain = false;
$properties = array();
while (true) {
$optional = false;
if ($opToken = $this->scanner
->consumeOneOf(array(
"?.",
".",
))) {
$isOptChain = $opToken->value == "?.";
if ($isOptChain) {
$optionalChain = $optional = true;
}
if ($this->features->privateMethodsAndFields && ($property = $this->parsePrivateIdentifier()) || ($property = $this->parseIdentifier(static::$identifierName))) {
$valid = true;
$properties[] = array(
"type" => "id",
"info" => $property,
"optional" => $optional,
);
continue;
}
else {
$valid = false;
if (!$isOptChain) {
break;
}
}
}
if ($this->scanner
->consume("[")) {
if (($property = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume("]")) {
$valid = true;
$properties[] = array(
"type" => "computed",
"info" => array(
$property,
$this->scanner
->getPosition(),
),
"optional" => $optional,
);
}
else {
$valid = false;
break;
}
}
elseif ($property = $this->parseTemplateLiteral(true)) {
if ($optionalChain) {
$this->error("Optional chain can't appear in tagged template expressions");
}
$valid = true;
$properties[] = array(
"type" => "template",
"info" => $property,
"optional" => $optional,
);
}
elseif (($args = $this->parseArguments()) !== null) {
$valid = true;
$properties[] = array(
"type" => "args",
"info" => array(
$args,
$this->scanner
->getPosition(),
),
"optional" => $optional,
);
}
else {
break;
}
}
$propCount = count($properties);
if (!$valid) {
$this->error();
}
elseif (!$propCount && !$newTokensCount) {
return $object;
}
$node = null;
$endPos = $object->location->end;
$optionalChainStarted = false;
foreach ($properties as $i => $property) {
$lastNode = $node ?: $object;
if ($property["optional"]) {
$optionalChainStarted = true;
}
if ($property["type"] === "args") {
if ($newTokensCount) {
if ($optionalChainStarted) {
$this->error("Optional chain can't appear in new expressions");
}
$node = $this->createNode("NewExpression", array_pop($newTokens));
$newTokensCount--;
}
else {
$node = $this->createNode("CallExpression", $lastNode);
$node->setOptional($property["optional"]);
}
$node->setCallee($lastNode);
$node->setArguments($property["info"][0]);
$endPos = $property["info"][1];
}
elseif ($property["type"] === "id") {
$node = $this->createNode("MemberExpression", $lastNode);
$node->setObject($lastNode);
$node->setOptional($property["optional"]);
$node->setProperty($property["info"]);
$endPos = $property["info"]->location->end;
}
elseif ($property["type"] === "computed") {
$node = $this->createNode("MemberExpression", $lastNode);
$node->setObject($lastNode);
$node->setProperty($property["info"][0]);
$node->setOptional($property["optional"]);
$node->setComputed(true);
$endPos = $property["info"][1];
}
elseif ($property["type"] === "template") {
$node = $this->createNode("TaggedTemplateExpression", $object);
$node->setTag($lastNode);
$node->setQuasi($property["info"]);
$endPos = $property["info"]->location->end;
}
$node = $this->completeNode($node, $endPos);
}
//Wrap the result in multiple NewExpression if there are "new" tokens
if ($newTokensCount) {
for ($i = $newTokensCount - 1; $i >= 0; $i--) {
$lastNode = $node ?: $object;
$node = $this->createNode("NewExpression", $newTokens[$i]);
$node->setCallee($lastNode);
$node = $this->completeNode($node);
}
}
//Wrap the result in a chain expression if required
if ($optionalChain) {
$prevNode = $node;
$node = $this->createNode("ChainExpression", $prevNode);
$node->setExpression($prevNode);
$node = $this->completeNode($node);
}
return $node;
}
/**
* Parses a spread element
*
* @return Node\SpreadElement|null
*/
protected function parseSpreadElement() {
if ($token = $this->scanner
->consume("...")) {
$argument = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression");
if ($argument) {
$node = $this->createNode("SpreadElement", $token);
$node->setArgument($argument);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses an array literal
*
* @return Node\ArrayExpression|null
*/
protected function parseArrayLiteral() {
if ($token = $this->scanner
->consume("[")) {
$elements = array();
while (true) {
if ($elision = $this->parseElision()) {
$elements = array_merge($elements, array_fill(0, $elision, null));
}
if (($element = $this->parseSpreadElement()) || ($element = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression"))) {
$elements[] = $element;
if (!$this->scanner
->consume(",")) {
break;
}
}
else {
break;
}
}
if ($this->scanner
->consume("]")) {
$node = $this->createNode("ArrayExpression", $token);
$node->setElements($elements);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses an arguments list wrapped in round brackets
*
* @return array|null
*/
protected function parseArguments() {
if ($this->scanner
->consume("(")) {
if (($args = $this->parseArgumentList()) !== null && $this->scanner
->consume(")")) {
return $args;
}
$this->error();
}
return null;
}
/**
* Parses an arguments list
*
* @return array|null
*/
protected function parseArgumentList() {
$list = array();
$hasComma = false;
while (true) {
$spread = $this->scanner
->consume("...");
$exp = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression");
if (!$exp) {
//If there's no expression and the spread dots have been found
//or there is a trailing comma that is not allowed, throw an
//error
if ($spread || $hasComma && !$this->features->trailingCommaFunctionCallDeclaration) {
$this->error();
}
break;
}
if ($spread) {
$node = $this->createNode("SpreadElement", $spread);
$node->setArgument($exp);
$list[] = $this->completeNode($node);
}
else {
$list[] = $exp;
}
if (!$this->scanner
->consume(",")) {
break;
}
$hasComma = true;
}
return $list;
}
/**
* Parses a super call or a super property
*
* @return Node\Node|null
*/
protected function parseSuperPropertyOrCall() {
if ($token = $this->scanner
->consume("super")) {
$super = $this->completeNode($this->createNode("Super", $token));
if (($args = $this->parseArguments()) !== null) {
$node = $this->createNode("CallExpression", $token);
$node->setArguments($args);
$node->setCallee($super);
return $this->completeNode($node);
}
$node = $this->createNode("MemberExpression", $token);
$node->setObject($super);
if ($this->scanner
->consume(".")) {
if ($property = $this->parseIdentifier(static::$identifierName)) {
$node->setProperty($property);
return $this->completeNode($node);
}
}
elseif ($this->scanner
->consume("[") && ($property = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume("]")) {
$node->setProperty($property);
$node->setComputed(true);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a primary expression
*
* @return Node\Node|null
*/
protected function parsePrimaryExpression() {
if ($token = $this->scanner
->consume("this")) {
$node = $this->createNode("ThisExpression", $token);
return $this->completeNode($node);
}
elseif ($exp = $this->parseFunctionOrGeneratorExpression()) {
return $exp;
}
elseif ($exp = $this->parseClassExpression()) {
return $exp;
}
elseif ($exp = $this->parseIdentifier(static::$identifierReference)) {
return $exp;
}
elseif ($exp = $this->parseLiteral()) {
return $exp;
}
elseif ($exp = $this->parseArrayLiteral()) {
return $exp;
}
elseif ($exp = $this->parseObjectLiteral()) {
return $exp;
}
elseif ($exp = $this->parseRegularExpressionLiteral()) {
return $exp;
}
elseif ($exp = $this->parseTemplateLiteral()) {
return $exp;
}
elseif ($this->jsx && ($exp = $this->parseJSXFragment())) {
return $exp;
}
elseif ($this->jsx && ($exp = $this->parseJSXElement())) {
return $exp;
}
elseif ($token = $this->scanner
->consume("(")) {
if (($exp = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression")) && $this->scanner
->consume(")")) {
$node = $this->createNode("ParenthesizedExpression", $token);
$node->setExpression($exp);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Parses a private identifier
*
* @return Node\PrivateIdentifier|null
*/
protected function parsePrivateIdentifier() {
$token = $this->scanner
->getToken();
if (!$token || $token->type !== Token::TYPE_PRIVATE_IDENTIFIER) {
return null;
}
$this->scanner
->consumeToken();
$node = $this->createNode("PrivateIdentifier", $token);
$node->setName(substr($token->value, 1));
return $this->completeNode($node);
}
/**
* Parses an identifier
*
* @param int $mode Parsing mode, one of the id parsing mode
* constants
* @param string $after If a string is passed in this parameter, the
* identifier is parsed only if precedes this string
*
* @return Node\Identifier|null
*/
protected function parseIdentifier($mode, $after = null) {
$token = $this->scanner
->getToken();
if (!$token) {
return null;
}
if ($after !== null) {
$next = $this->scanner
->getNextToken();
if (!$next || $next->value !== $after) {
return null;
}
}
$type = $token->type;
switch ($type) {
case Token::TYPE_BOOLEAN_LITERAL:
case Token::TYPE_NULL_LITERAL:
if ($mode !== self::ID_ALLOW_ALL) {
return null;
}
break;
case Token::TYPE_KEYWORD:
if ($mode === self::ID_ALLOW_NOTHING) {
return null;
}
elseif ($mode === self::ID_MIXED && $this->scanner
->isStrictModeKeyword($token)) {
return null;
}
break;
default:
if ($type !== Token::TYPE_IDENTIFIER) {
return null;
}
break;
}
//Exclude keywords that depend on parser context
$value = $token->value;
if ($mode === self::ID_MIXED && isset($this->contextKeywords[$value]) && $this->context->{$this->contextKeywords[$value]}) {
return null;
}
$this->scanner
->consumeToken();
$node = $this->createNode("Identifier", $token);
$node->setRawName($value);
return $this->completeNode($node);
}
/**
* Parses a literal
*
* @return Node\Literal|null
*/
protected function parseLiteral() {
if ($token = $this->scanner
->getToken()) {
if ($token->type === Token::TYPE_NULL_LITERAL) {
$this->scanner
->consumeToken();
$node = $this->createNode("NullLiteral", $token);
return $this->completeNode($node);
}
elseif ($token->type === Token::TYPE_BOOLEAN_LITERAL) {
$this->scanner
->consumeToken();
$node = $this->createNode("BooleanLiteral", $token);
$node->setRaw($token->value);
return $this->completeNode($node);
}
elseif ($literal = $this->parseStringLiteral()) {
return $literal;
}
elseif ($literal = $this->parseNumericLiteral()) {
return $literal;
}
}
return null;
}
/**
* Parses a string literal
*
* @return Node\StringLiteral|null
*/
protected function parseStringLiteral() {
$token = $this->scanner
->getToken();
if ($token && $token->type === Token::TYPE_STRING_LITERAL) {
$val = $token->value;
$this->checkInvalidEscapeSequences($val);
$this->scanner
->consumeToken();
$node = $this->createNode("StringLiteral", $token);
$node->setRaw($val);
return $this->completeNode($node);
}
return null;
}
/**
* Parses a numeric literal
*
* @return Node\NumericLiteral|Node\BigIntLiteral|null
*/
protected function parseNumericLiteral() {
$token = $this->scanner
->getToken();
if ($token && $token->type === Token::TYPE_NUMERIC_LITERAL) {
$val = $token->value;
$this->checkInvalidEscapeSequences($val, true);
$this->scanner
->consumeToken();
$node = $this->createNode("NumericLiteral", $token);
$node->setRaw($val);
return $this->completeNode($node);
}
elseif ($token && $token->type === Token::TYPE_BIGINT_LITERAL) {
$val = $token->value;
$this->checkInvalidEscapeSequences($val, true);
$this->scanner
->consumeToken();
$node = $this->createNode("BigIntLiteral", $token);
$node->setRaw($val);
return $this->completeNode($node);
}
return null;
}
/**
* Parses a template literal
*
* @param bool $tagged True if the template is tagged
*
* @return Node\Literal|null
*/
protected function parseTemplateLiteral($tagged = false) {
$token = $this->scanner
->getToken();
if (!$token || $token->type !== Token::TYPE_TEMPLATE) {
return null;
}
//Do not parse templates parts
$val = $token->value;
if ($val[0] !== "`") {
return null;
}
$quasis = $expressions = array();
$valid = false;
do {
$this->scanner
->consumeToken();
$val = $token->value;
$this->checkInvalidEscapeSequences($val, false, true, $tagged);
$lastChar = substr($val, -1);
$quasi = $this->createNode("TemplateElement", $token);
$quasi->setRawValue($val);
if ($lastChar === "`") {
$quasi->setTail(true);
$quasis[] = $this->completeNode($quasi);
$valid = true;
break;
}
else {
$quasis[] = $this->completeNode($quasi);
$exp = $this->isolateContext(array(
"allowIn" => true,
), "parseExpression");
if ($exp) {
$expressions[] = $exp;
}
else {
$valid = false;
break;
}
}
$token = $this->scanner
->getToken();
} while ($token && $token->type === Token::TYPE_TEMPLATE);
if ($valid) {
$node = $this->createNode("TemplateLiteral", $quasis[0]);
$node->setQuasis($quasis);
$node->setExpressions($expressions);
return $this->completeNode($node);
}
$this->error();
}
/**
* Parses a regular expression literal
*
* @return Node\Literal|null
*/
protected function parseRegularExpressionLiteral() {
if ($token = $this->scanner
->reconsumeCurrentTokenAsRegexp()) {
$this->scanner
->consumeToken();
$node = $this->createNode("RegExpLiteral", $token);
$node->setRaw($token->value);
return $this->completeNode($node);
}
return null;
}
/**
* Parse directive prologues. The result is an array where the first element
* is the array of parsed nodes and the second element is the array of
* directive prologues values
*
* @return array|null
*/
protected function parseDirectivePrologues() {
$directives = $nodes = array();
while (($token = $this->scanner
->getToken()) && $token->type === Token::TYPE_STRING_LITERAL) {
$directive = substr($token->value, 1, -1);
if ($directive === "use strict") {
$directives[] = $directive;
$directiveNode = $this->parseStringLiteral();
$this->assertEndOfStatement();
$node = $this->createNode("ExpressionStatement", $directiveNode);
$node->setExpression($directiveNode);
$nodes[] = $this->completeNode($node);
}
else {
break;
}
}
return count($nodes) ? array(
$nodes,
$directives,
) : null;
}
/**
* Parses an import call
*
* @return Node\Node|null
*/
protected function parseImportCall() {
if (($token = $this->scanner
->consume("import")) && $this->scanner
->consume("(")) {
if (($source = $this->isolateContext(array(
"allowIn" => true,
), "parseAssignmentExpression")) && $this->scanner
->consume(")")) {
$node = $this->createNode("ImportExpression", $token);
$node->setSource($source);
return $this->completeNode($node);
}
$this->error();
}
return null;
}
/**
* Checks if the given string or number contains invalid escape sequences
*
* @param string $val Value to check
* @param bool $number True if the value is a number
* @param bool $forceLegacyOctalCheck True to force legacy octal
* form check
* @param bool $taggedTemplate True if the value is a tagged
* template
*
* @return void
*/
protected function checkInvalidEscapeSequences($val, $number = false, $forceLegacyOctalCheck = false, $taggedTemplate = false) {
if ($this->features->skipEscapeSeqCheckInTaggedTemplates && $taggedTemplate) {
return;
}
$checkLegacyOctal = $forceLegacyOctalCheck || $this->scanner
->getStrictMode();
if ($number) {
if ($val && $val[0] === "0" && preg_match("#^0[0-9_]+\$#", $val)) {
if ($checkLegacyOctal) {
$this->error("Octal literals are not allowed in strict mode");
}
if ($this->features->numericLiteralSeparator && strpos($val, '_') !== false) {
$this->error("Numeric separators are not allowed in legacy octal numbers");
}
}
}
elseif (strpos($val, "\\") !== false) {
$hex = "0-9a-fA-F";
$invalidSyntax = array(
"x[{$hex}]?[^{$hex}]",
"x[{$hex}]?\$",
"u\\{\\}",
"u\\{(?:[{$hex}]*[^{$hex}\\}]+)+[{$hex}]*\\}",
"u\\{[^\\}]*\$",
"u(?!{)[{$hex}]{0,3}[^{$hex}\\{]",
"u[{$hex}]{0,3}\$",
);
if ($checkLegacyOctal) {
$invalidSyntax[] = "\\d{2}";
$invalidSyntax[] = "[1-7]";
$invalidSyntax[] = "0[89]";
}
$reg = "#(\\\\+)(" . implode("|", $invalidSyntax) . ")#";
if (preg_match_all($reg, $val, $matches, PREG_SET_ORDER)) {
foreach ($matches as $match) {
if (strlen($match[1]) % 2) {
$first = $match[2][0];
if ($first === "u") {
$err = "Malformed unicode escape sequence";
}
elseif ($first === "x") {
$err = "Malformed hexadecimal escape sequence";
}
else {
$err = "Octal literals are not allowed in strict mode";
}
$this->error($err);
}
}
}
}
}
}
Members
Title Sort descending | Modifiers | Object type | Summary | Overriden Title |
---|---|---|---|---|
Parser::$assignmentOperators | protected | property | Assignment operators | |
Parser::$bindingIdentifier | protected static | property | Binding identifier parsing rule | |
Parser::$contextKeywords | protected | property | Array of keywords that depends on a context property | |
Parser::$identifierName | protected static | property | Identifier name parsing rule | |
Parser::$identifierReference | protected static | property | Identifier reference parsing rule | |
Parser::$importedBinding | protected static | property | Imported binding parsing rule | |
Parser::$labelledIdentifier | protected static | property | Labelled identifier parsing rule | |
Parser::$logicalBinaryOperators | protected | property | Logical and binary operators | |
Parser::$postfixOperators | protected | property | Postfix operators | |
Parser::$unaryOperators | protected | property | Unary operators | |
Parser::checkAsyncFunctionStart | protected | function | Checks if an async function can start from the current position. Returns the async token or null if not found |
|
Parser::checkInvalidEscapeSequences | protected | function | Checks if the given string or number contains invalid escape sequences | |
Parser::expressionToPattern | protected | function | Converts an expression node to a pattern node | |
Parser::ID_ALLOW_ALL | constant | Everything is allowed as identifier, including keywords, null and booleans | ||
Parser::ID_ALLOW_NOTHING | constant | Keywords, null and booleans are not allowed in any situation | ||
Parser::ID_MIXED | constant | Keywords, null and booleans are not allowed in any situation, future reserved words are allowed if not in strict mode. Keywords that depend on parser context are evaluated only if the parser context allows them. |
||
Parser::initContext | protected | function | Initializes parser context | Overrides ParserAbstract::initContext |
Parser::parse | public | function | Parses the source | Overrides ParserAbstract::parse |
Parser::parseArgumentList | protected | function | Parses an arguments list | |
Parser::parseArguments | protected | function | Parses an arguments list wrapped in round brackets | |
Parser::parseArrayBindingPattern | protected | function | Parses an array binding pattern | |
Parser::parseArrayLiteral | protected | function | Parses an array literal | |
Parser::parseArrowFunction | protected | function | Parses an arrow function | |
Parser::parseArrowParameters | protected | function | Parses parameters in an arrow function. If the parameters are wrapped in round brackets, the returned value is an array where the first element is the parameters list and the second element is the open round brackets, this is needed to know the start⦠|
|
Parser::parseAssignmentExpression | protected | function | Parses an assignment expression | |
Parser::parseBindingElement | protected | function | Parses a binding element | |
Parser::parseBindingPattern | protected | function | Parses a binding pattern | |
Parser::parseBindingProperty | protected | function | Parses a property in an object binding pattern | |
Parser::parseBindingRestElement | protected | function | Parses a rest element | |
Parser::parseBlock | protected | function | Parses a block statement | |
Parser::parseBreakableStatement | protected | function | Parses a breakable statement | |
Parser::parseBreakStatement | protected | function | Parses a break statement | |
Parser::parseCaseBlock | protected | function | Parses the content of a switch statement | |
Parser::parseCaseClause | protected | function | Parses a case in a switch statement | |
Parser::parseCaseClauses | protected | function | Parses cases in a switch statement | |
Parser::parseCatch | protected | function | Parses the catch block of a try-catch statement | |
Parser::parseCatchParameter | protected | function | Parses the catch parameter of a catch block in a try-catch statement | |
Parser::parseClassBody | protected | function | Parses the class body | |
Parser::parseClassDeclaration | protected | function | Parses a class declaration | |
Parser::parseClassElement | protected | function | Parses a class elements | |
Parser::parseClassElementList | protected | function | Parses class elements list | |
Parser::parseClassElementName | protected | function | Parses a property name. The returned value is an array where there first element is the property name and the second element is a boolean indicating if it's a computed property |
|
Parser::parseClassExpression | protected | function | Parses a class expression | |
Parser::parseClassHeritage | protected | function | Parses the class extends part | |
Parser::parseClassStaticBlock | protected | function | Parses a field definition | |
Parser::parseClassTail | protected | function | Parses the code that comes after the class keyword and class name. The return value is an array where the first item is the extended class, if any, and the second value is the class body |
|
Parser::parseConciseBody | protected | function | Parses the body of an arrow function. The returned value is an array where the first element is the function body and the second element is a boolean indicating if the body is wrapped in curly braces |
|
Parser::parseConditionalExpression | protected | function | Parses a conditional expression | |
Parser::parseContinueStatement | protected | function | Parses a continue statement | |
Parser::parseDebuggerStatement | protected | function | Parses a debugger statement | |
Parser::parseDeclaration | protected | function | Parses a declaration | |
Parser::parseDefaultClause | protected | function | Parses default case in a switch statement | |
Parser::parseDirectivePrologues | protected | function | Parse directive prologues. The result is an array where the first element is the array of parsed nodes and the second element is the array of directive prologues values |
|
Parser::parseDoWhileStatement | protected | function | Parses a do-while statement | |
Parser::parseElision | protected | function | Parses an elisions sequence. It returns the number of elisions or null if no elision has been found |
|
Parser::parseEmptyStatement | protected | function | Parses an empty statement | |
Parser::parseExportClause | protected | function | Parses an export clause | |
Parser::parseExportDeclaration | protected | function | Parses an export declaration | |
Parser::parseExportSpecifier | protected | function | Parses an export specifier | |
Parser::parseExpression | protected | function | Parses an expression | |
Parser::parseExpressionStatement | protected | function | Parses an expression statement | |
Parser::parseFieldDefinition | protected | function | Parses a field definition | |
Parser::parseFinally | protected | function | Parses a finally block in a try-catch statement | |
Parser::parseForBinding | protected | function | Parses a binding pattern or an identifier that come after a const or let declaration in a for statement definition |
|
Parser::parseForDeclaration | protected | function | Parses a let or const declaration in a for statement definition | |
Parser::parseForLetConstStatement | protected | function | Parses a for(let ...) or for(const ...) statement | |
Parser::parseFormalParameterList | protected | function | Parses a parameter list | |
Parser::parseForNotVarLetConstStatement | protected | function | Parses a for statement that does not start with var, let or const | |
Parser::parseForVarStatement | protected | function | Parses a for(var ...) statement | |
Parser::parseFromClause | protected | function | Parses the from keyword and the following string in import and export declarations |
|
Parser::parseFunctionBody | protected | function | Parses a function body | |
Parser::parseFunctionOrGeneratorDeclaration | protected | function | Parses function or generator declaration | |
Parser::parseFunctionOrGeneratorExpression | protected | function | Parses function or generator expression | |
Parser::parseIdentifier | protected | function | Parses an identifier | |
Parser::parseIfStatement | protected | function | Parses an if statement | |
Parser::parseImportCall | protected | function | Parses an import call | |
Parser::parseImportClause | protected | function | Parses an import clause | |
Parser::parseImportDeclaration | protected | function | Parses an import declaration | |
Parser::parseImportSpecifier | protected | function | Parses an import specifier | |
Parser::parseInitializer | protected | function | Parses an initializer | |
Parser::parseIterationStatement | protected | function | Parses do-while, while, for, for-in and for-of statements | |
Parser::parseLabelledStatement | protected | function | Parses a labelled statement | |
Parser::parseLeftHandSideExpression | protected | function | Parses a left hand side expression | |
Parser::parseLexicalDeclaration | protected | function | Parses a let or const declaration | |
Parser::parseLiteral | protected | function | Parses a literal | |
Parser::parseLogicalBinaryExpression | protected | function | Parses a logical or a binary expression | |
Parser::parseMethodDefinition | protected | function | Parses a method definition | |
Parser::parseModuleExportName | protected | function | Parses an export name | |
Parser::parseModuleItem | protected | function | Parses a module item | |
Parser::parseModuleItemList | protected | function | Parses a module item list | |
Parser::parseNamedImports | protected | function | Parses a named imports | |
Parser::parseNameSpaceImport | protected | function | Parses a namespace import | |
Parser::parseNumericLiteral | protected | function | Parses a numeric literal | |
Parser::parseObjectBindingPattern | protected | function | Parses an object binding pattern | |
Parser::parseObjectLiteral | protected | function | Parses an object literal | |
Parser::parsePostfixExpression | protected | function | Parses a postfix expression | |
Parser::parsePrimaryExpression | protected | function | Parses a primary expression | |
Parser::parsePrivateIdentifier | protected | function | Parses a private identifier | |
Parser::parsePropertyDefinition | protected | function | Parses a property in an object literal | |
Parser::parsePropertyName | protected | function | Parses a property name. The returned value is an array where there first element is the property name and the second element is a boolean indicating if it's a computed property |
|
Parser::parseRegularExpressionLiteral | protected | function | Parses a regular expression literal | |
Parser::parseRestProperty | protected | function | Parses a rest property | |
Parser::parseReturnStatement | protected | function | Parses a return statement | |
Parser::parseSingleNameBinding | protected | function | Parses single name binding | |
Parser::parseSpreadElement | protected | function | Parses a spread element | |
Parser::parseStatement | protected | function | Parses a statement | |
Parser::parseStatementList | protected | function | Parses a statement list | |
Parser::parseStatementListItem | protected | function | Parses a statement list item | |
Parser::parseStringLiteral | protected | function | Parses a string literal | |
Parser::parseSuperPropertyOrCall | protected | function | Parses a super call or a super property | |
Parser::parseSwitchStatement | protected | function | Parses a switch statement | |
Parser::parseTemplateLiteral | protected | function | Parses a template literal | |
Parser::parseThrowStatement | protected | function | Parses a throw statement | |
Parser::parseTryStatement | protected | function | Parses a try-catch statement | |
Parser::parseUnaryExpression | protected | function | Parses a unary expression | |
Parser::parseVariableDeclaration | protected | function | Parses a variable declarations | |
Parser::parseVariableDeclarationList | protected | function | Parses an variable declarations | |
Parser::parseVariableStatement | protected | function | Parses a var declaration | |
Parser::parseWhileStatement | protected | function | Parses a while statement | |
Parser::parseWithStatement | protected | function | Parses a with statement | |
Parser::parseYieldExpression | protected | function | Parses yield statement | |
Parser::postInit | protected | function | Post initialize operations | Overrides ParserAbstract::postInit |
ParserAbstract::$comments | protected | property | Comments handling | |
ParserAbstract::$context | protected | property | Parser context | |
ParserAbstract::$eventsEmitter | protected | property | Events emitter | |
ParserAbstract::$features | protected | property | Parser features | |
ParserAbstract::$jsx | protected | property | JSX syntax handling | |
ParserAbstract::$scanner | protected | property | Associated scanner | |
ParserAbstract::$sourceType | protected | property | Source type | |
ParserAbstract::assertEndOfStatement | protected | function | Asserts that a valid end of statement follows the current position | |
ParserAbstract::charSeparatedListOf | protected | function | Parses a character separated list of instructions or null if the sequence is not valid |
|
ParserAbstract::completeNode | protected | function | Completes a node by adding the end position | |
ParserAbstract::createNode | protected | function | Creates a node | |
ParserAbstract::error | protected | function | Throws a syntax error | |
ParserAbstract::getEventsEmitter | public | function | Returns the parser's events emitter | |
ParserAbstract::getFeatures | public | function | Returns the parser features class | |
ParserAbstract::getScanner | public | function | Returns the scanner associated with the parser | |
ParserAbstract::isolateContext | protected | function | Calls a method with an isolated parser context, applying the given flags, but restoring their values after the execution. |
|
ParserAbstract::tokenize | public | function | Returns parsed tokens from the source code | |
ParserAbstract::__construct | public | function | Class constructor |