Skip to content

Commit 12c0a62

Browse files
authored
Merge pull request glayzzle#921 from MaartenStaa/parse-namespace-single-token
Parse namespaces as single tokens
2 parents 2c4a293 + 7dd65db commit 12c0a62

18 files changed

+619
-66
lines changed

src/ast.js

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -402,20 +402,20 @@ AST.prototype.prepare = function (kind, docs, parser) {
402402
result.postBuild(astNode);
403403
}
404404
if (parser.debug) {
405-
delete AST.stack[result.stackUid];
405+
delete self.stack[result.stackUid];
406406
}
407407
return self.resolvePrecedence(astNode, parser);
408408
};
409409
if (parser.debug) {
410-
if (!AST.stack) {
411-
AST.stack = {};
412-
AST.stackUid = 1;
410+
if (!this.stack) {
411+
this.stack = {};
412+
this.stackUid = 1;
413413
}
414-
AST.stack[++AST.stackUid] = {
414+
this.stack[++this.stackUid] = {
415415
position: start,
416416
stack: new Error().stack.split("\n").slice(3, 5),
417417
};
418-
result.stackUid = AST.stackUid;
418+
result.stackUid = this.stackUid;
419419
}
420420

421421
/**
@@ -451,20 +451,21 @@ AST.prototype.prepare = function (kind, docs, parser) {
451451
}
452452
}
453453
if (parser.debug) {
454-
delete AST.stack[result.stackUid];
454+
delete self.stack[result.stackUid];
455455
}
456456
};
457457
return result;
458458
};
459459

460460
AST.prototype.checkNodes = function () {
461461
const errors = [];
462-
for (const k in AST.stack) {
463-
if (Object.prototype.hasOwnProperty.call(AST.stack, k)) {
464-
errors.push(AST.stack[k]);
462+
for (const k in this.stack) {
463+
if (Object.prototype.hasOwnProperty.call(this.stack, k)) {
464+
this.stack[k].key = k;
465+
errors.push(this.stack[k]);
465466
}
466467
}
467-
AST.stack = {};
468+
this.stack = {};
468469
return errors;
469470
};
470471

src/ast/name.js

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,10 @@ const KIND = "name";
1818
*/
1919
const Name = Reference.extends(
2020
KIND,
21-
function Name(name, isRelative, docs, location) {
21+
function Name(name, resolution, docs, location) {
2222
Reference.apply(this, [KIND, docs, location]);
23-
if (isRelative) {
24-
this.resolution = Name.RELATIVE_NAME;
25-
} else if (name.length === 1) {
26-
this.resolution = Name.UNQUALIFIED_NAME;
27-
} else if (!name[0]) {
28-
this.resolution = Name.FULL_QUALIFIED_NAME;
29-
} else {
30-
this.resolution = Name.QUALIFIED_NAME;
31-
}
32-
this.name = name.join("\\");
23+
this.name = name.replace(/\\$/, "");
24+
this.resolution = resolution;
3325
}
3426
);
3527

src/lexer/tokens.js

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ module.exports = {
2020
} else {
2121
id = this.tok.T_STRING;
2222
if (token === "b" || token === "B") {
23-
const ch = this.input(1);
23+
const ch = this.input();
2424
if (ch === '"') {
2525
return this.ST_DOUBLE_QUOTES();
2626
} else if (ch === "'") {
@@ -31,6 +31,32 @@ module.exports = {
3131
}
3232
}
3333
}
34+
35+
if (this.offset < this.size && id !== this.tok.T_YIELD_FROM) {
36+
// If immediately followed by a backslash, this is a T_NAME_RELATIVE or T_NAME_QUALIFIED.
37+
let ch = this.input();
38+
if (ch === "\\") {
39+
id =
40+
token === "namespace"
41+
? this.tok.T_NAME_RELATIVE
42+
: this.tok.T_NAME_QUALIFIED;
43+
do {
44+
if (this._input[this.offset] === "{") {
45+
// e.g. when using group use statements, the last '\\' is followed by a '{'
46+
this.input();
47+
break;
48+
}
49+
50+
this.consume_LABEL();
51+
ch = this.input();
52+
} while (ch === "\\");
53+
}
54+
55+
if (ch) {
56+
this.unput(1);
57+
}
58+
}
59+
3460
return id;
3561
},
3662
// reads a custom token
@@ -71,6 +97,28 @@ module.exports = {
7197
return "-";
7298
},
7399
"\\": function () {
100+
if (this.offset < this.size) {
101+
this.input();
102+
if (this.is_LABEL_START()) {
103+
let ch;
104+
do {
105+
if (this._input[this.offset] === "{") {
106+
// e.g. when using group use statements, the last '\\' is followed by a '{'
107+
this.input();
108+
break;
109+
}
110+
111+
this.consume_LABEL();
112+
ch = this.input();
113+
} while (ch === "\\");
114+
115+
this.unput(1);
116+
117+
return this.tok.T_NAME_FULLY_QUALIFIED;
118+
} else {
119+
this.unput(1);
120+
}
121+
}
74122
return this.tok.T_NS_SEPARATOR;
75123
},
76124
"/": function () {

src/parser.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,10 @@ const Parser = function (lexer, ast) {
130130
this.tok.T_VARIABLE,
131131
"$",
132132
"&",
133-
this.tok.T_NS_SEPARATOR,
134133
this.tok.T_STRING,
134+
this.tok.T_NAME_RELATIVE,
135+
this.tok.T_NAME_QUALIFIED,
136+
this.tok.T_NAME_FULLY_QUALIFIED,
135137
this.tok.T_NAMESPACE,
136138
this.tok.T_STATIC,
137139
].map(mapIt)
@@ -222,6 +224,9 @@ const Parser = function (lexer, ast) {
222224
"$",
223225
this.tok.T_NS_SEPARATOR,
224226
this.tok.T_STRING,
227+
this.tok.T_NAME_RELATIVE,
228+
this.tok.T_NAME_QUALIFIED,
229+
this.tok.T_NAME_FULLY_QUALIFIED,
225230
// using SCALAR :
226231
this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
227232
this.tok.T_CONSTANT_ENCAPSED_STRING,

src/parser/expr.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,9 @@ module.exports = {
784784
read_new_class_name: function () {
785785
if (
786786
this.token === this.tok.T_NS_SEPARATOR ||
787+
this.token === this.tok.T_NAME_RELATIVE ||
788+
this.token === this.tok.T_NAME_QUALIFIED ||
789+
this.token === this.tok.T_NAME_FULLY_QUALIFIED ||
787790
this.token === this.tok.T_STRING ||
788791
this.token === this.tok.T_NAMESPACE
789792
) {

src/parser/function.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,9 @@ module.exports = {
432432
this.next();
433433
return result("typereference", type.toLowerCase(), type);
434434
} else if (
435+
this.token === this.tok.T_NAME_RELATIVE ||
436+
this.token === this.tok.T_NAME_QUALIFIED ||
437+
this.token === this.tok.T_NAME_FULLY_QUALIFIED ||
435438
this.token === this.tok.T_STRING ||
436439
this.token === this.tok.T_STATIC
437440
) {
@@ -451,13 +454,6 @@ module.exports = {
451454
result.destroy();
452455
return this.read_namespace_name();
453456
}
454-
} else if (
455-
this.token === this.tok.T_NAMESPACE ||
456-
this.token === this.tok.T_NS_SEPARATOR
457-
) {
458-
// fix : destroy not consumed node (release comments)
459-
result.destroy();
460-
return this.read_namespace_name();
461457
}
462458
// fix : destroy not consumed node (release comments)
463459
result.destroy();

src/parser/namespace.js

Lines changed: 35 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ module.exports = {
2323
this.expect(this.tok.T_NAMESPACE) && this.next();
2424
let name;
2525

26-
if (this.token == "{") {
26+
if (this.token === "{") {
2727
name = {
2828
name: [""],
2929
};
@@ -32,12 +32,12 @@ module.exports = {
3232
}
3333
this.currentNamespace = name;
3434

35-
if (this.token == ";") {
35+
if (this.token === ";") {
3636
this.currentNamespace = name;
3737
body = this.next().read_top_statements();
3838
this.expect(this.EOF);
3939
return result(name.name, body, false);
40-
} else if (this.token == "{") {
40+
} else if (this.token === "{") {
4141
this.currentNamespace = name;
4242
body = this.next().read_top_statements();
4343
this.expect("}") && this.next();
@@ -49,12 +49,6 @@ module.exports = {
4949
body.push(this.node("noop")());
5050
}
5151
return result(name.name, body, true);
52-
} else if (this.token === "(") {
53-
// @fixme after merging #478
54-
name.resolution = this.ast.reference.RELATIVE_NAME;
55-
name.name = name.name.substring(1);
56-
result.destroy();
57-
return this.node("call")(name, this.read_argument_list());
5852
} else {
5953
this.error(["{", ";"]);
6054
// graceful mode :
@@ -74,28 +68,38 @@ module.exports = {
7468
*/
7569
read_namespace_name: function (resolveReference) {
7670
const result = this.node();
77-
let relative = false;
78-
if (this.token === this.tok.T_NAMESPACE) {
79-
this.next().expect(this.tok.T_NS_SEPARATOR) && this.next();
80-
relative = true;
71+
let resolution;
72+
let name = this.text();
73+
switch (this.token) {
74+
case this.tok.T_NAME_RELATIVE:
75+
resolution = this.ast.name.RELATIVE_NAME;
76+
name = name.replace(/^namespace\\/, "");
77+
break;
78+
case this.tok.T_NAME_QUALIFIED:
79+
resolution = this.ast.name.QUALIFIED_NAME;
80+
break;
81+
case this.tok.T_NAME_FULLY_QUALIFIED:
82+
resolution = this.ast.name.FULL_QUALIFIED_NAME;
83+
break;
84+
default:
85+
resolution = this.ast.name.UNQUALIFIED_NAME;
86+
if (!this.expect(this.tok.T_STRING)) {
87+
// graceful mode
88+
return result("name", "", this.ast.name.FULL_QUALIFIED_NAME);
89+
}
8190
}
82-
const names = this.read_list(
83-
this.tok.T_STRING,
84-
this.tok.T_NS_SEPARATOR,
85-
true
86-
);
87-
if (
88-
!relative &&
89-
names.length === 1 &&
90-
(resolveReference || this.token !== "(")
91-
) {
92-
if (names[0].toLowerCase() === "parent") {
93-
return result("parentreference", names[0]);
94-
} else if (names[0].toLowerCase() === "self") {
95-
return result("selfreference", names[0]);
91+
92+
this.next();
93+
94+
if (resolveReference || this.token !== "(") {
95+
if (name.toLowerCase() === "parent") {
96+
return result("parentreference", name);
97+
} else if (name.toLowerCase() === "self") {
98+
return result("selfreference", name);
9699
}
97100
}
98-
return result("name", names, relative);
101+
102+
return result("name", name, resolution);
99103
},
100104
/*
101105
* Reads a use statement
@@ -172,6 +176,9 @@ module.exports = {
172176
break;
173177
}
174178
} else if (
179+
this.token !== this.tok.T_NAME_RELATIVE &&
180+
this.token !== this.tok.T_NAME_QUALIFIED &&
181+
this.token !== this.tok.T_NAME_FULLY_QUALIFIED &&
175182
this.token !== this.tok.T_STRING &&
176183
this.token !== this.tok.T_NS_SEPARATOR
177184
) {

src/parser/scalar.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ module.exports = {
364364
this.next();
365365
// check if lookup an offset
366366
// https://github.com/php/php-src/blob/master/Zend/zend_language_parser.y#L1243
367+
result.destroy();
367368
if (this.token === "[") {
368369
name = name(varName, false);
369370
node = this.node("offsetlookup");

src/parser/variable.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ module.exports = {
3838
this.is([
3939
this.tok.T_NS_SEPARATOR,
4040
this.tok.T_STRING,
41+
this.tok.T_NAME_RELATIVE,
42+
this.tok.T_NAME_QUALIFIED,
43+
this.tok.T_NAME_FULLY_QUALIFIED,
4144
this.tok.T_NAMESPACE,
4245
])
4346
) {

src/tokens.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,9 @@ const tokens = {
156156
238: "T_ATTRIBUTE",
157157
239: "T_ENUM",
158158
240: "T_READ_ONLY",
159+
241: "T_NAME_RELATIVE",
160+
242: "T_NAME_QUALIFIED",
161+
243: "T_NAME_FULLY_QUALIFIED",
159162
},
160163
names: {
161164
T_HALT_COMPILER: 101,
@@ -298,6 +301,9 @@ const tokens = {
298301
T_ATTRIBUTE: 238,
299302
T_ENUM: 239,
300303
T_READ_ONLY: 240,
304+
T_NAME_RELATIVE: 241,
305+
T_NAME_QUALIFIED: 242,
306+
T_NAME_FULLY_QUALIFIED: 243,
301307
},
302308
};
303309

0 commit comments

Comments
 (0)