Skip to content
Merged
23 changes: 12 additions & 11 deletions src/ast.js
Original file line number Diff line number Diff line change
Expand Up @@ -402,20 +402,20 @@ AST.prototype.prepare = function (kind, docs, parser) {
result.postBuild(astNode);
}
if (parser.debug) {
delete AST.stack[result.stackUid];
delete self.stack[result.stackUid];
}
return self.resolvePrecedence(astNode, parser);
};
if (parser.debug) {
if (!AST.stack) {
AST.stack = {};
AST.stackUid = 1;
if (!this.stack) {
this.stack = {};
this.stackUid = 1;
}
AST.stack[++AST.stackUid] = {
this.stack[++this.stackUid] = {
position: start,
stack: new Error().stack.split("\n").slice(3, 5),
};
result.stackUid = AST.stackUid;
result.stackUid = this.stackUid;
}

/**
Expand Down Expand Up @@ -451,20 +451,21 @@ AST.prototype.prepare = function (kind, docs, parser) {
}
}
if (parser.debug) {
delete AST.stack[result.stackUid];
delete self.stack[result.stackUid];
}
};
return result;
};

AST.prototype.checkNodes = function () {
const errors = [];
for (const k in AST.stack) {
if (Object.prototype.hasOwnProperty.call(AST.stack, k)) {
errors.push(AST.stack[k]);
for (const k in this.stack) {
if (Object.prototype.hasOwnProperty.call(this.stack, k)) {
this.stack[k].key = k;
errors.push(this.stack[k]);
}
}
AST.stack = {};
this.stack = {};
return errors;
};

Expand Down
14 changes: 3 additions & 11 deletions src/ast/name.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,10 @@ const KIND = "name";
*/
const Name = Reference.extends(
KIND,
function Name(name, isRelative, docs, location) {
function Name(name, resolution, docs, location) {
Reference.apply(this, [KIND, docs, location]);
if (isRelative) {
this.resolution = Name.RELATIVE_NAME;
} else if (name.length === 1) {
this.resolution = Name.UNQUALIFIED_NAME;
} else if (!name[0]) {
this.resolution = Name.FULL_QUALIFIED_NAME;
} else {
this.resolution = Name.QUALIFIED_NAME;
}
this.name = name.join("\\");
this.name = name.replace(/\\$/, "");
this.resolution = resolution;
}
);

Expand Down
50 changes: 49 additions & 1 deletion src/lexer/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = {
} else {
id = this.tok.T_STRING;
if (token === "b" || token === "B") {
const ch = this.input(1);
const ch = this.input();
if (ch === '"') {
return this.ST_DOUBLE_QUOTES();
} else if (ch === "'") {
Expand All @@ -31,6 +31,32 @@ module.exports = {
}
}
}

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
Expand Down Expand Up @@ -71,6 +97,28 @@ module.exports = {
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 () {
Expand Down
7 changes: 6 additions & 1 deletion src/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,10 @@ const Parser = function (lexer, ast) {
this.tok.T_VARIABLE,
"$",
"&",
this.tok.T_NS_SEPARATOR,
this.tok.T_STRING,
this.tok.T_NAME_RELATIVE,
this.tok.T_NAME_QUALIFIED,
this.tok.T_NAME_FULLY_QUALIFIED,
this.tok.T_NAMESPACE,
this.tok.T_STATIC,
].map(mapIt)
Expand Down Expand Up @@ -222,6 +224,9 @@ const Parser = function (lexer, ast) {
"$",
this.tok.T_NS_SEPARATOR,
this.tok.T_STRING,
this.tok.T_NAME_RELATIVE,
this.tok.T_NAME_QUALIFIED,
this.tok.T_NAME_FULLY_QUALIFIED,
// using SCALAR :
this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
this.tok.T_CONSTANT_ENCAPSED_STRING,
Expand Down
3 changes: 3 additions & 0 deletions src/parser/expr.js
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,9 @@ module.exports = {
read_new_class_name: function () {
if (
this.token === this.tok.T_NS_SEPARATOR ||
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_NAMESPACE
) {
Expand Down
10 changes: 3 additions & 7 deletions src/parser/function.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,9 @@ module.exports = {
this.next();
return result("typereference", type.toLowerCase(), type);
} 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_STATIC
) {
Expand All @@ -425,13 +428,6 @@ module.exports = {
result.destroy();
return this.read_namespace_name();
}
} else if (
this.token === this.tok.T_NAMESPACE ||
this.token === this.tok.T_NS_SEPARATOR
) {
// fix : destroy not consumed node (release comments)
result.destroy();
return this.read_namespace_name();
}
// fix : destroy not consumed node (release comments)
result.destroy();
Expand Down
63 changes: 35 additions & 28 deletions src/parser/namespace.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ module.exports = {
this.expect(this.tok.T_NAMESPACE) && this.next();
let name;

if (this.token == "{") {
if (this.token === "{") {
name = {
name: [""],
};
Expand All @@ -32,12 +32,12 @@ module.exports = {
}
this.currentNamespace = name;

if (this.token == ";") {
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 == "{") {
} else if (this.token === "{") {
this.currentNamespace = name;
body = this.next().read_top_statements();
this.expect("}") && this.next();
Expand All @@ -49,12 +49,6 @@ module.exports = {
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 :
Expand All @@ -74,28 +68,38 @@ module.exports = {
*/
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;
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);
}
}
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]);

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", names, relative);

return result("name", name, resolution);
},
/*
* Reads a use statement
Expand Down Expand Up @@ -172,6 +176,9 @@ module.exports = {
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
) {
Expand Down
1 change: 1 addition & 0 deletions src/parser/scalar.js
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@ module.exports = {
this.next();
// check if lookup an offset
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243
result.destroy();
if (this.token === "[") {
name = name(varName, false);
node = this.node("offsetlookup");
Expand Down
3 changes: 3 additions & 0 deletions src/parser/variable.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ module.exports = {
this.is([
this.tok.T_NS_SEPARATOR,
this.tok.T_STRING,
this.tok.T_NAME_RELATIVE,
this.tok.T_NAME_QUALIFIED,
this.tok.T_NAME_FULLY_QUALIFIED,
this.tok.T_NAMESPACE,
])
) {
Expand Down
6 changes: 6 additions & 0 deletions src/tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ const tokens = {
238: "T_ATTRIBUTE",
239: "T_ENUM",
240: "T_READ_ONLY",
241: "T_NAME_RELATIVE",
242: "T_NAME_QUALIFIED",
243: "T_NAME_FULLY_QUALIFIED",
},
names: {
T_HALT_COMPILER: 101,
Expand Down Expand Up @@ -298,6 +301,9 @@ const tokens = {
T_ATTRIBUTE: 238,
T_ENUM: 239,
T_READ_ONLY: 240,
T_NAME_RELATIVE: 241,
T_NAME_QUALIFIED: 242,
T_NAME_FULLY_QUALIFIED: 243,
},
};

Expand Down
Loading