php-parser
Version:
Parse PHP code from JS and returns its AST
204 lines (189 loc) • 4.99 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 = {
/*
* Reads a short form of tokens
* @param {Number} token - The ending token
* @return {Block}
*/
read_short_form: function (token) {
const body = this.node("block");
const items = [];
/* istanbul ignore next */
if (this.expect(":")) this.next();
while (this.token != this.EOF && this.token !== token) {
items.push(this.read_inner_statement());
}
if (
items.length === 0 &&
this.extractDoc &&
this._docs.length > this._docIndex
) {
items.push(this.node("noop")());
}
/* istanbul ignore next */
if (this.expect(token)) this.next();
this.expectEndOfStatement();
return body(null, items);
},
/*
* https://wiki.php.net/rfc/trailing-comma-function-calls
* @param {*} item
* @param {*} separator
*/
read_function_list: function (item, separator) {
const result = [];
do {
if (this.token == separator && this.version >= 703 && result.length > 0) {
result.push(this.node("noop")());
break;
}
result.push(item.apply(this, []));
if (this.token != separator) {
break;
}
if (this.next().token == ")" && this.version >= 703) {
break;
}
} while (this.token != this.EOF);
return result;
},
/*
* Helper : reads a list of tokens / sample : T_STRING ',' T_STRING ...
* ```ebnf
* list ::= separator? ( item separator )* item
* ```
*/
read_list: function (item, separator, preserveFirstSeparator) {
const result = [];
if (this.token == separator) {
if (preserveFirstSeparator) {
result.push(typeof item === "function" ? this.node("noop")() : null);
}
this.next();
}
if (typeof item === "function") {
do {
const itemResult = item.apply(this, []);
if (itemResult) {
result.push(itemResult);
}
if (this.token != separator) {
break;
}
} while (this.next().token != this.EOF);
} else {
if (this.expect(item)) {
result.push(this.text());
} else {
return [];
}
while (this.next().token != this.EOF) {
if (this.token != separator) break;
// trim current separator & check item
if (this.next().token != item) break;
result.push(this.text());
}
}
return result;
},
/*
* Reads a list of names separated by a comma
*
* ```ebnf
* name_list ::= namespace (',' namespace)*
* ```
*
* Sample code :
* ```php
* <?php class foo extends bar, baz { }
* ```
*
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L726
* @return {Reference[]}
*/
read_name_list: function () {
return this.read_list(this.read_namespace_name, ",", false);
},
/*
* Reads the byref token and assign it to the specified node
* @param {*} cb
*/
read_byref: function (cb) {
let byref = this.node("byref");
this.next();
byref = byref(null);
const result = cb();
if (result) {
this.ast.swapLocations(result, byref, result, this);
result.byref = true;
}
return result;
},
/*
* Reads a list of variables declarations
*
* ```ebnf
* variable_declaration ::= T_VARIABLE ('=' expr)?*
* variable_declarations ::= variable_declaration (',' variable_declaration)*
* ```
*
* Sample code :
* ```php
* <?php static $a = 'hello', $b = 'world';
* ```
* @return {StaticVariable[]} Returns an array composed by a list of variables, or
* assign values
*/
read_variable_declarations: function () {
return this.read_list(function () {
const node = this.node("staticvariable");
let variable = this.node("variable");
// plain variable name
/* istanbul ignore else */
if (this.expect(this.tok.T_VARIABLE)) {
const name = this.text().substring(1);
this.next();
variable = variable(name, false);
} else {
variable = variable("#ERR", false);
}
if (this.token === "=") {
return node(variable, this.next().read_expr());
} else {
return variable;
}
}, ",");
},
/*
* Reads class extends
*/
read_extends_from: function () {
if (this.token === this.tok.T_EXTENDS) {
return this.next().read_namespace_name();
}
return null;
},
/*
* Reads interface extends list
*/
read_interface_extends_list: function () {
if (this.token === this.tok.T_EXTENDS) {
return this.next().read_name_list();
}
return null;
},
/*
* Reads implements list
*/
read_implements_list: function () {
if (this.token === this.tok.T_IMPLEMENTS) {
return this.next().read_name_list();
}
return null;
},
};