php-parser
Version:
Parse PHP code from JS and returns its AST
232 lines (225 loc) • 6.76 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 namespace declaration block
* ```ebnf
* namespace ::= T_NAMESPACE namespace_name? '{'
* top_statements
* '}'
* | T_NAMESPACE namespace_name ';' top_statements
* ```
* @see http://php.net/manual/en/language.namespaces.php
* @return {Namespace}
*/
read_namespace: function () {
const result = this.node("namespace");
let body;
this.expect(this.tok.T_NAMESPACE) && this.next();
let name;
if (this.token === "{") {
name = {
name: [""],
};
} else {
name = this.read_namespace_name();
}
this.currentNamespace = name;
if (this.token === ";") {
this.currentNamespace = name;
body = this.next().read_top_statements();
this.expect(this.EOF);
return result(name.name, body, false);
} else if (this.token === "{") {
this.currentNamespace = name;
body = this.next().read_top_statements();
this.expect("}") && this.next();
if (
body.length === 0 &&
this.extractDoc &&
this._docs.length > this._docIndex
) {
body.push(this.node("noop")());
}
return result(name.name, body, true);
} else {
this.error(["{", ";"]);
// graceful mode :
this.currentNamespace = name;
body = this.read_top_statements();
this.expect(this.EOF);
return result(name, body, false);
}
},
/*
* Reads a namespace name
* ```ebnf
* namespace_name ::= T_NS_SEPARATOR? (T_STRING T_NS_SEPARATOR)* T_STRING
* ```
* @see http://php.net/manual/en/language.namespaces.rules.php
* @return {Reference}
*/
read_namespace_name: function (resolveReference) {
const result = this.node();
let resolution;
let name = this.text();
switch (this.token) {
case this.tok.T_NAME_RELATIVE:
resolution = this.ast.name.RELATIVE_NAME;
name = name.replace(/^namespace\\/, "");
break;
case this.tok.T_NAME_QUALIFIED:
resolution = this.ast.name.QUALIFIED_NAME;
break;
case this.tok.T_NAME_FULLY_QUALIFIED:
resolution = this.ast.name.FULL_QUALIFIED_NAME;
break;
default:
resolution = this.ast.name.UNQUALIFIED_NAME;
if (!this.expect(this.tok.T_STRING)) {
// graceful mode
return result("name", "", this.ast.name.FULL_QUALIFIED_NAME);
}
}
this.next();
if (resolveReference || this.token !== "(") {
if (name.toLowerCase() === "parent") {
return result("parentreference", name);
} else if (name.toLowerCase() === "self") {
return result("selfreference", name);
}
}
return result("name", name, resolution);
},
/*
* Reads a use statement
* ```ebnf
* use_statement ::= T_USE
* use_type? use_declarations |
* use_type use_statement '{' use_declarations '}' |
* use_statement '{' use_declarations(=>typed) '}'
* ';'
* ```
* @see http://php.net/manual/en/language.namespaces.importing.php
* @return {UseGroup}
*/
read_use_statement: function () {
let result = this.node("usegroup");
let items = [];
let name = null;
this.expect(this.tok.T_USE) && this.next();
const type = this.read_use_type();
items.push(this.read_use_declaration(false));
if (this.token === ",") {
items = items.concat(this.next().read_use_declarations(false));
} else if (this.token === "{") {
name = items[0].name;
items = this.next().read_use_declarations(type === null);
this.expect("}") && this.next();
}
result = result(name, type, items);
this.expect(";") && this.next();
return result;
},
/*
*
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1045
*/
read_class_name_reference: function () {
// resolved as the same
return this.read_variable(true, false);
},
/*
* Reads a use declaration
* ```ebnf
* use_declaration ::= use_type? namespace_name use_alias
* ```
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380
* @return {UseItem}
*/
read_use_declaration: function (typed) {
const result = this.node("useitem");
let type = null;
if (typed) type = this.read_use_type();
const name = this.read_namespace_name();
const alias = this.read_use_alias();
return result(name.name, alias, type);
},
/*
* Reads a list of use declarations
* ```ebnf
* use_declarations ::= use_declaration (',' use_declaration)*
* ```
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L380
* @return {UseItem[]}
*/
read_use_declarations: function (typed) {
const result = [this.read_use_declaration(typed)];
while (this.token === ",") {
this.next();
if (typed) {
if (
this.token !== this.tok.T_NAME_RELATIVE &&
this.token !== this.tok.T_NAME_QUALIFIED &&
this.token !== this.tok.T_NAME_FULLY_QUALIFIED &&
this.token !== this.tok.T_FUNCTION &&
this.token !== this.tok.T_CONST &&
this.token !== this.tok.T_STRING
) {
break;
}
} else if (
this.token !== this.tok.T_NAME_RELATIVE &&
this.token !== this.tok.T_NAME_QUALIFIED &&
this.token !== this.tok.T_NAME_FULLY_QUALIFIED &&
this.token !== this.tok.T_STRING &&
this.token !== this.tok.T_NS_SEPARATOR
) {
break;
}
result.push(this.read_use_declaration(typed));
}
return result;
},
/*
* Reads a use statement
* ```ebnf
* use_alias ::= (T_AS T_STRING)?
* ```
* @return {String|null}
*/
read_use_alias: function () {
let result = null;
if (this.token === this.tok.T_AS) {
if (this.next().expect(this.tok.T_STRING)) {
const aliasName = this.node("identifier");
const name = this.text();
this.next();
result = aliasName(name);
}
}
return result;
},
/*
* Reads the namespace type declaration
* ```ebnf
* use_type ::= (T_FUNCTION | T_CONST)?
* ```
* @see https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L335
* @return {String|null} Possible values : function, const
*/
read_use_type: function () {
if (this.token === this.tok.T_FUNCTION) {
this.next();
return this.ast.useitem.TYPE_FUNCTION;
} else if (this.token === this.tok.T_CONST) {
this.next();
return this.ast.useitem.TYPE_CONST;
}
return null;
},
};