php-parser
Version:
Parse PHP code from JS and returns its AST
357 lines (346 loc) • 9.59 kB
JavaScript
/**
* Copyright (C) 2018 Glayzzle (BSD3 License)
* @authors https://github.com/glayzzle/php-parser/graphs/contributors
* @url http://glayzzle.com
*/
"use strict";
module.exports = {
T_STRING: function () {
const token = this.yytext.toLowerCase();
let id = this.keywords[token];
if (typeof id !== "number") {
if (token === "yield") {
if (this.version >= 700 && this.tryMatch(" from")) {
this.consume(5);
id = this.tok.T_YIELD_FROM;
} else {
id = this.tok.T_YIELD;
}
} else {
id = this.tok.T_STRING;
if (token === "b" || token === "B") {
const ch = this.input();
if (ch === '"') {
return this.ST_DOUBLE_QUOTES();
} else if (ch === "'") {
return this.T_CONSTANT_ENCAPSED_STRING();
} else if (ch) {
this.unput(1);
}
}
}
}
// https://github.com/php/php-src/blob/master/Zend/zend_language_scanner.l#L1546
if (id === this.tok.T_ENUM) {
if (this.version < 801) {
return this.tok.T_STRING;
}
const initial = this.offset;
let ch = this.input();
while (ch == " ") {
ch = this.input();
}
let isEnum = false;
if (this.is_LABEL_START()) {
while (this.is_LABEL()) {
ch += this.input();
}
const label = ch.slice(0, -1).toLowerCase();
isEnum = label !== "extends" && label !== "implements";
}
this.unput(this.offset - initial);
return isEnum ? this.tok.T_ENUM : this.tok.T_STRING;
}
if (this.offset < this.size && id !== this.tok.T_YIELD_FROM) {
// If immediately followed by a backslash, this is a T_NAME_RELATIVE or T_NAME_QUALIFIED.
let ch = this.input();
if (ch === "\\") {
id =
token === "namespace"
? this.tok.T_NAME_RELATIVE
: this.tok.T_NAME_QUALIFIED;
do {
if (this._input[this.offset] === "{") {
// e.g. when using group use statements, the last '\\' is followed by a '{'
this.input();
break;
}
this.consume_LABEL();
ch = this.input();
} while (ch === "\\");
}
if (ch) {
this.unput(1);
}
}
return id;
},
// reads a custom token
consume_TOKEN: function () {
const ch = this._input[this.offset - 1];
const fn = this.tokenTerminals[ch];
if (fn) {
return fn.apply(this, []);
} else {
return this.yytext;
}
},
// list of special char tokens
tokenTerminals: {
$: function () {
this.offset++;
if (this.is_LABEL_START()) {
this.offset--;
this.consume_LABEL();
return this.tok.T_VARIABLE;
} else {
this.offset--;
return "$";
}
},
"-": function () {
const nchar = this._input[this.offset];
if (nchar === ">") {
this.begin("ST_LOOKING_FOR_PROPERTY").input();
return this.tok.T_OBJECT_OPERATOR;
} else if (nchar === "-") {
this.input();
return this.tok.T_DEC;
} else if (nchar === "=") {
this.input();
return this.tok.T_MINUS_EQUAL;
}
return "-";
},
"\\": function () {
if (this.offset < this.size) {
this.input();
if (this.is_LABEL_START()) {
let ch;
do {
if (this._input[this.offset] === "{") {
// e.g. when using group use statements, the last '\\' is followed by a '{'
this.input();
break;
}
this.consume_LABEL();
ch = this.input();
} while (ch === "\\");
this.unput(1);
return this.tok.T_NAME_FULLY_QUALIFIED;
} else {
this.unput(1);
}
}
return this.tok.T_NS_SEPARATOR;
},
"/": function () {
if (this._input[this.offset] === "=") {
this.input();
return this.tok.T_DIV_EQUAL;
}
return "/";
},
":": function () {
if (this._input[this.offset] === ":") {
this.input();
return this.tok.T_DOUBLE_COLON;
} else {
return ":";
}
},
"(": function () {
const initial = this.offset;
this.input();
if (this.is_TABSPACE()) {
this.consume_TABSPACE().input();
}
if (this.is_LABEL_START()) {
const yylen = this.yytext.length;
this.consume_LABEL();
const castToken = this.yytext.substring(yylen - 1).toLowerCase();
const castId = this.castKeywords[castToken];
if (typeof castId === "number") {
this.input();
if (this.is_TABSPACE()) {
this.consume_TABSPACE().input();
}
if (this._input[this.offset - 1] === ")") {
return castId;
}
}
}
// revert the check
this.unput(this.offset - initial);
return "(";
},
"=": function () {
const nchar = this._input[this.offset];
if (nchar === ">") {
this.input();
return this.tok.T_DOUBLE_ARROW;
} else if (nchar === "=") {
if (this._input[this.offset + 1] === "=") {
this.consume(2);
return this.tok.T_IS_IDENTICAL;
} else {
this.input();
return this.tok.T_IS_EQUAL;
}
}
return "=";
},
"+": function () {
const nchar = this._input[this.offset];
if (nchar === "+") {
this.input();
return this.tok.T_INC;
} else if (nchar === "=") {
this.input();
return this.tok.T_PLUS_EQUAL;
}
return "+";
},
"!": function () {
if (this._input[this.offset] === "=") {
if (this._input[this.offset + 1] === "=") {
this.consume(2);
return this.tok.T_IS_NOT_IDENTICAL;
} else {
this.input();
return this.tok.T_IS_NOT_EQUAL;
}
}
return "!";
},
"?": function () {
if (this.version >= 700 && this._input[this.offset] === "?") {
if (this.version >= 704 && this._input[this.offset + 1] === "=") {
this.consume(2);
return this.tok.T_COALESCE_EQUAL;
} else {
this.input();
return this.tok.T_COALESCE;
}
}
if (
this.version >= 800 &&
this._input[this.offset] === "-" &&
this._input[this.offset + 1] === ">"
) {
this.consume(1);
this.begin("ST_LOOKING_FOR_PROPERTY").input();
return this.tok.T_NULLSAFE_OBJECT_OPERATOR;
}
return "?";
},
"<": function () {
let nchar = this._input[this.offset];
if (nchar === "<") {
nchar = this._input[this.offset + 1];
if (nchar === "=") {
this.consume(2);
return this.tok.T_SL_EQUAL;
} else if (nchar === "<") {
if (this.is_HEREDOC()) {
return this.tok.T_START_HEREDOC;
}
}
this.input();
return this.tok.T_SL;
} else if (nchar === "=") {
this.input();
if (this.version >= 700 && this._input[this.offset] === ">") {
this.input();
return this.tok.T_SPACESHIP;
} else {
return this.tok.T_IS_SMALLER_OR_EQUAL;
}
} else if (nchar === ">") {
this.input();
return this.tok.T_IS_NOT_EQUAL;
}
return "<";
},
">": function () {
let nchar = this._input[this.offset];
if (nchar === "=") {
this.input();
return this.tok.T_IS_GREATER_OR_EQUAL;
} else if (nchar === ">") {
nchar = this._input[this.offset + 1];
if (nchar === "=") {
this.consume(2);
return this.tok.T_SR_EQUAL;
} else {
this.input();
return this.tok.T_SR;
}
}
return ">";
},
"*": function () {
const nchar = this._input[this.offset];
if (nchar === "=") {
this.input();
return this.tok.T_MUL_EQUAL;
} else if (nchar === "*") {
this.input();
if (this._input[this.offset] === "=") {
this.input();
return this.tok.T_POW_EQUAL;
} else {
return this.tok.T_POW;
}
}
return "*";
},
".": function () {
const nchar = this._input[this.offset];
if (nchar === "=") {
this.input();
return this.tok.T_CONCAT_EQUAL;
} else if (nchar === "." && this._input[this.offset + 1] === ".") {
this.consume(2);
return this.tok.T_ELLIPSIS;
}
return ".";
},
"%": function () {
if (this._input[this.offset] === "=") {
this.input();
return this.tok.T_MOD_EQUAL;
}
return "%";
},
"&": function () {
const nchar = this._input[this.offset];
if (nchar === "=") {
this.input();
return this.tok.T_AND_EQUAL;
} else if (nchar === "&") {
this.input();
return this.tok.T_BOOLEAN_AND;
}
return "&";
},
"|": function () {
const nchar = this._input[this.offset];
if (nchar === "=") {
this.input();
return this.tok.T_OR_EQUAL;
} else if (nchar === "|") {
this.input();
return this.tok.T_BOOLEAN_OR;
}
return "|";
},
"^": function () {
if (this._input[this.offset] === "=") {
this.input();
return this.tok.T_XOR_EQUAL;
}
return "^";
},
},
};