php-parser
Version:
Parse PHP code from JS and returns its AST
222 lines (218 loc) • 6.35 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 if (this.token === "(") {
// @fixme after merging #478
name.resolution = this.ast.reference.RELATIVE_NAME;
name.name = name.name.substring(1);
result.destroy();
return this.node("call")(name, this.read_argument_list());
} 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 relative = false;
if (this.token === this.tok.T_NAMESPACE) {
this.next().expect(this.tok.T_NS_SEPARATOR) && this.next();
relative = true;
}
const names = this.read_list(
this.tok.T_STRING,
this.tok.T_NS_SEPARATOR,
true
);
if (
!relative &&
names.length === 1 &&
(resolveReference || this.token !== "(")
) {
if (names[0].toLowerCase() === "parent") {
return result("parentreference", names[0]);
} else if (names[0].toLowerCase() === "self") {
return result("selfreference", names[0]);
}
}
return result("name", names, relative);
},
/*
* 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_FUNCTION &&
this.token !== this.tok.T_CONST &&
this.token !== this.tok.T_STRING
) {
break;
}
} else if (
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;
},
};