@jorgsowa/php-parser 3.2.5-1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +108 -0
  3. package/dist/@jorgsowa/php-parser.js +11239 -0
  4. package/dist/@jorgsowa/php-parser.min.js +2 -0
  5. package/dist/@jorgsowa/php-parser.min.js.LICENSE.txt +10 -0
  6. package/package.json +86 -0
  7. package/src/ast/array.js +44 -0
  8. package/src/ast/arrowfunc.js +43 -0
  9. package/src/ast/assign.js +28 -0
  10. package/src/ast/assignref.js +27 -0
  11. package/src/ast/attrgroup.js +21 -0
  12. package/src/ast/attribute.js +26 -0
  13. package/src/ast/bin.js +27 -0
  14. package/src/ast/block.js +24 -0
  15. package/src/ast/boolean.js +23 -0
  16. package/src/ast/break.js +21 -0
  17. package/src/ast/byref.js +21 -0
  18. package/src/ast/call.js +26 -0
  19. package/src/ast/case.js +26 -0
  20. package/src/ast/cast.js +28 -0
  21. package/src/ast/catch.js +29 -0
  22. package/src/ast/class.js +36 -0
  23. package/src/ast/classconstant.js +71 -0
  24. package/src/ast/clone.js +21 -0
  25. package/src/ast/closure.js +47 -0
  26. package/src/ast/comment.js +23 -0
  27. package/src/ast/commentblock.js +22 -0
  28. package/src/ast/commentline.js +22 -0
  29. package/src/ast/constant.js +26 -0
  30. package/src/ast/constantstatement.js +24 -0
  31. package/src/ast/continue.js +24 -0
  32. package/src/ast/declaration.js +60 -0
  33. package/src/ast/declare.js +71 -0
  34. package/src/ast/declaredirective.js +26 -0
  35. package/src/ast/do.js +26 -0
  36. package/src/ast/echo.js +26 -0
  37. package/src/ast/empty.js +23 -0
  38. package/src/ast/encapsed.js +75 -0
  39. package/src/ast/encapsedpart.js +28 -0
  40. package/src/ast/entry.js +30 -0
  41. package/src/ast/enum.js +30 -0
  42. package/src/ast/enumcase.js +26 -0
  43. package/src/ast/error.js +30 -0
  44. package/src/ast/eval.js +24 -0
  45. package/src/ast/exit.js +26 -0
  46. package/src/ast/expression.js +20 -0
  47. package/src/ast/expressionstatement.js +24 -0
  48. package/src/ast/for.js +33 -0
  49. package/src/ast/foreach.js +33 -0
  50. package/src/ast/function.js +34 -0
  51. package/src/ast/global.js +24 -0
  52. package/src/ast/goto.js +22 -0
  53. package/src/ast/halt.js +22 -0
  54. package/src/ast/identifier.js +26 -0
  55. package/src/ast/if.js +30 -0
  56. package/src/ast/include.js +28 -0
  57. package/src/ast/inline.js +23 -0
  58. package/src/ast/interface.js +28 -0
  59. package/src/ast/intersectiontype.js +24 -0
  60. package/src/ast/isset.js +23 -0
  61. package/src/ast/label.js +21 -0
  62. package/src/ast/list.js +26 -0
  63. package/src/ast/literal.js +28 -0
  64. package/src/ast/location.js +22 -0
  65. package/src/ast/lookup.js +26 -0
  66. package/src/ast/magic.js +22 -0
  67. package/src/ast/match.js +26 -0
  68. package/src/ast/matcharm.js +26 -0
  69. package/src/ast/method.js +24 -0
  70. package/src/ast/name.js +55 -0
  71. package/src/ast/namedargument.js +27 -0
  72. package/src/ast/namespace.js +26 -0
  73. package/src/ast/new.js +26 -0
  74. package/src/ast/node.js +111 -0
  75. package/src/ast/noop.js +20 -0
  76. package/src/ast/nowdoc.js +26 -0
  77. package/src/ast/nullkeyword.js +20 -0
  78. package/src/ast/nullsafepropertylookup.js +22 -0
  79. package/src/ast/number.js +23 -0
  80. package/src/ast/offsetlookup.js +22 -0
  81. package/src/ast/operation.js +19 -0
  82. package/src/ast/parameter.js +61 -0
  83. package/src/ast/parentreference.js +24 -0
  84. package/src/ast/position.js +22 -0
  85. package/src/ast/post.js +26 -0
  86. package/src/ast/pre.js +26 -0
  87. package/src/ast/print.js +23 -0
  88. package/src/ast/program.js +32 -0
  89. package/src/ast/property.js +46 -0
  90. package/src/ast/propertyhook.js +33 -0
  91. package/src/ast/propertylookup.js +22 -0
  92. package/src/ast/propertystatement.js +59 -0
  93. package/src/ast/reference.js +21 -0
  94. package/src/ast/retif.js +28 -0
  95. package/src/ast/return.js +21 -0
  96. package/src/ast/selfreference.js +24 -0
  97. package/src/ast/silent.js +24 -0
  98. package/src/ast/statement.js +19 -0
  99. package/src/ast/static.js +24 -0
  100. package/src/ast/staticlookup.js +22 -0
  101. package/src/ast/staticreference.js +24 -0
  102. package/src/ast/staticvariable.js +26 -0
  103. package/src/ast/string.js +28 -0
  104. package/src/ast/switch.js +28 -0
  105. package/src/ast/throw.js +21 -0
  106. package/src/ast/trait.js +24 -0
  107. package/src/ast/traitalias.js +44 -0
  108. package/src/ast/traitprecedence.js +28 -0
  109. package/src/ast/traituse.js +26 -0
  110. package/src/ast/try.js +28 -0
  111. package/src/ast/typereference.js +40 -0
  112. package/src/ast/unary.js +26 -0
  113. package/src/ast/uniontype.js +24 -0
  114. package/src/ast/unset.js +23 -0
  115. package/src/ast/usegroup.js +30 -0
  116. package/src/ast/useitem.js +45 -0
  117. package/src/ast/variable.js +36 -0
  118. package/src/ast/variadic.js +25 -0
  119. package/src/ast/variadicplaceholder.js +24 -0
  120. package/src/ast/while.js +28 -0
  121. package/src/ast/yield.js +27 -0
  122. package/src/ast/yieldfrom.js +25 -0
  123. package/src/ast.js +593 -0
  124. package/src/index.js +239 -0
  125. package/src/lexer/attribute.js +85 -0
  126. package/src/lexer/comments.js +63 -0
  127. package/src/lexer/initial.js +64 -0
  128. package/src/lexer/numbers.js +171 -0
  129. package/src/lexer/property.js +96 -0
  130. package/src/lexer/scripting.js +114 -0
  131. package/src/lexer/strings.js +524 -0
  132. package/src/lexer/tokens.js +356 -0
  133. package/src/lexer/utils.js +112 -0
  134. package/src/lexer.js +561 -0
  135. package/src/parser/array.js +113 -0
  136. package/src/parser/class.js +718 -0
  137. package/src/parser/comment.js +52 -0
  138. package/src/parser/enum.js +56 -0
  139. package/src/parser/expr.js +848 -0
  140. package/src/parser/function.js +507 -0
  141. package/src/parser/if.js +94 -0
  142. package/src/parser/loops.js +168 -0
  143. package/src/parser/main.js +21 -0
  144. package/src/parser/namespace.js +231 -0
  145. package/src/parser/scalar.js +492 -0
  146. package/src/parser/statement.js +444 -0
  147. package/src/parser/switch.js +99 -0
  148. package/src/parser/try.js +43 -0
  149. package/src/parser/utils.js +203 -0
  150. package/src/parser/variable.js +363 -0
  151. package/src/parser.js +748 -0
  152. package/src/tokens.js +177 -0
  153. package/types.d.ts +1457 -0
package/src/parser.js ADDED
@@ -0,0 +1,748 @@
1
+ /**
2
+ * Copyright (C) 2018 Glayzzle (BSD3 License)
3
+ * @authors https://github.com/glayzzle/php-parser/graphs/contributors
4
+ * @url http://glayzzle.com
5
+ */
6
+ "use strict";
7
+
8
+ const Position = require("./ast/position");
9
+
10
+ /**
11
+ * @private
12
+ */
13
+ function isNumber(n) {
14
+ return n != "." && n != "," && !isNaN(parseFloat(n)) && isFinite(n);
15
+ }
16
+
17
+ /**
18
+ * The PHP Parser class that build the AST tree from the lexer
19
+ *
20
+ * @constructor Parser
21
+ * @memberOf module:php-parser
22
+ * @tutorial Parser
23
+ * @property {Lexer} lexer - current lexer instance
24
+ * @property {AST} ast - the AST factory instance
25
+ * @property {number|string} token - current token
26
+ * @property {boolean} extractDoc - should extract documentation as AST node
27
+ * @property {boolean} extractTokens - should extract each token
28
+ * @property {boolean} suppressErrors - should ignore parsing errors and continue
29
+ * @property {boolean} debug - should output debug informations
30
+ */
31
+ const Parser = function (lexer, ast) {
32
+ this.lexer = lexer;
33
+ this.ast = ast;
34
+ this.tok = lexer.tok;
35
+ this.EOF = lexer.EOF;
36
+ this.token = null;
37
+ this.prev = null;
38
+ this.debug = false;
39
+ this.version = 803;
40
+ this.extractDoc = false;
41
+ this.extractTokens = false;
42
+ this.suppressErrors = false;
43
+ const mapIt = function (item) {
44
+ return [item, null];
45
+ };
46
+ this.entries = {
47
+ // reserved_non_modifiers
48
+ IDENTIFIER: new Map(
49
+ [
50
+ this.tok.T_ABSTRACT,
51
+ this.tok.T_ARRAY,
52
+ this.tok.T_AS,
53
+ this.tok.T_BREAK,
54
+ this.tok.T_CALLABLE,
55
+ this.tok.T_CASE,
56
+ this.tok.T_CATCH,
57
+ this.tok.T_CLASS,
58
+ this.tok.T_CLASS_C,
59
+ this.tok.T_CLONE,
60
+ this.tok.T_CONST,
61
+ this.tok.T_CONTINUE,
62
+ this.tok.T_DECLARE,
63
+ this.tok.T_DEFAULT,
64
+ this.tok.T_DIR,
65
+ this.tok.T_DO,
66
+ this.tok.T_ECHO,
67
+ this.tok.T_ELSE,
68
+ this.tok.T_ELSEIF,
69
+ this.tok.T_EMPTY,
70
+ this.tok.T_ENDDECLARE,
71
+ this.tok.T_ENDFOR,
72
+ this.tok.T_ENDFOREACH,
73
+ this.tok.T_ENDIF,
74
+ this.tok.T_ENDSWITCH,
75
+ this.tok.T_ENDWHILE,
76
+ this.tok.T_ENUM,
77
+ this.tok.T_EVAL,
78
+ this.tok.T_EXIT,
79
+ this.tok.T_EXTENDS,
80
+ this.tok.T_FILE,
81
+ this.tok.T_FINAL,
82
+ this.tok.T_FINALLY,
83
+ this.tok.T_FN,
84
+ this.tok.T_FOR,
85
+ this.tok.T_FOREACH,
86
+ this.tok.T_FUNC_C,
87
+ this.tok.T_FUNCTION,
88
+ this.tok.T_GLOBAL,
89
+ this.tok.T_GOTO,
90
+ this.tok.T_IF,
91
+ this.tok.T_IMPLEMENTS,
92
+ this.tok.T_INCLUDE,
93
+ this.tok.T_INCLUDE_ONCE,
94
+ this.tok.T_INSTANCEOF,
95
+ this.tok.T_INSTEADOF,
96
+ this.tok.T_INTERFACE,
97
+ this.tok.T_ISSET,
98
+ this.tok.T_LINE,
99
+ this.tok.T_LIST,
100
+ this.tok.T_LOGICAL_AND,
101
+ this.tok.T_LOGICAL_OR,
102
+ this.tok.T_LOGICAL_XOR,
103
+ this.tok.T_MATCH,
104
+ this.tok.T_METHOD_C,
105
+ this.tok.T_NAMESPACE,
106
+ this.tok.T_NEW,
107
+ this.tok.T_NS_C,
108
+ this.tok.T_PRINT,
109
+ this.tok.T_PRIVATE,
110
+ this.tok.T_PROTECTED,
111
+ this.tok.T_PUBLIC,
112
+ this.tok.T_READ_ONLY,
113
+ this.tok.T_REQUIRE,
114
+ this.tok.T_REQUIRE_ONCE,
115
+ this.tok.T_RETURN,
116
+ this.tok.T_STATIC,
117
+ this.tok.T_SWITCH,
118
+ this.tok.T_THROW,
119
+ this.tok.T_TRAIT,
120
+ this.tok.T_TRY,
121
+ this.tok.T_UNSET,
122
+ this.tok.T_USE,
123
+ this.tok.T_VAR,
124
+ this.tok.T_WHILE,
125
+ this.tok.T_YIELD,
126
+ ].map(mapIt),
127
+ ),
128
+ VARIABLE: new Map(
129
+ [
130
+ this.tok.T_VARIABLE,
131
+ "$",
132
+ "&",
133
+ this.tok.T_STRING,
134
+ this.tok.T_NAME_RELATIVE,
135
+ this.tok.T_NAME_QUALIFIED,
136
+ this.tok.T_NAME_FULLY_QUALIFIED,
137
+ this.tok.T_NAMESPACE,
138
+ this.tok.T_STATIC,
139
+ ].map(mapIt),
140
+ ),
141
+ SCALAR: new Map(
142
+ [
143
+ this.tok.T_CONSTANT_ENCAPSED_STRING,
144
+ this.tok.T_START_HEREDOC,
145
+ this.tok.T_LNUMBER,
146
+ this.tok.T_DNUMBER,
147
+ this.tok.T_ARRAY,
148
+ "[",
149
+ this.tok.T_CLASS_C,
150
+ this.tok.T_TRAIT_C,
151
+ this.tok.T_FUNC_C,
152
+ this.tok.T_METHOD_C,
153
+ this.tok.T_LINE,
154
+ this.tok.T_FILE,
155
+ this.tok.T_DIR,
156
+ this.tok.T_NS_C,
157
+ '"',
158
+ 'b"',
159
+ 'B"',
160
+ "-",
161
+ this.tok.T_NS_SEPARATOR,
162
+ ].map(mapIt),
163
+ ),
164
+ T_MAGIC_CONST: new Map(
165
+ [
166
+ this.tok.T_CLASS_C,
167
+ this.tok.T_TRAIT_C,
168
+ this.tok.T_FUNC_C,
169
+ this.tok.T_METHOD_C,
170
+ this.tok.T_LINE,
171
+ this.tok.T_FILE,
172
+ this.tok.T_DIR,
173
+ this.tok.T_NS_C,
174
+ ].map(mapIt),
175
+ ),
176
+ T_MEMBER_FLAGS: new Map(
177
+ [
178
+ this.tok.T_PUBLIC,
179
+ this.tok.T_PRIVATE,
180
+ this.tok.T_PROTECTED,
181
+ this.tok.T_STATIC,
182
+ this.tok.T_ABSTRACT,
183
+ this.tok.T_FINAL,
184
+ ].map(mapIt),
185
+ ),
186
+ EOS: new Map([";", this.EOF, this.tok.T_INLINE_HTML].map(mapIt)),
187
+ EXPR: new Map(
188
+ [
189
+ "@",
190
+ "-",
191
+ "+",
192
+ "!",
193
+ "~",
194
+ "(",
195
+ "`",
196
+ this.tok.T_LIST,
197
+ this.tok.T_CLONE,
198
+ this.tok.T_INC,
199
+ this.tok.T_DEC,
200
+ this.tok.T_NEW,
201
+ this.tok.T_ISSET,
202
+ this.tok.T_EMPTY,
203
+ this.tok.T_MATCH,
204
+ this.tok.T_INCLUDE,
205
+ this.tok.T_INCLUDE_ONCE,
206
+ this.tok.T_REQUIRE,
207
+ this.tok.T_REQUIRE_ONCE,
208
+ this.tok.T_EVAL,
209
+ this.tok.T_INT_CAST,
210
+ this.tok.T_DOUBLE_CAST,
211
+ this.tok.T_STRING_CAST,
212
+ this.tok.T_ARRAY_CAST,
213
+ this.tok.T_OBJECT_CAST,
214
+ this.tok.T_BOOL_CAST,
215
+ this.tok.T_UNSET_CAST,
216
+ this.tok.T_EXIT,
217
+ this.tok.T_PRINT,
218
+ this.tok.T_YIELD,
219
+ this.tok.T_STATIC,
220
+ this.tok.T_FUNCTION,
221
+ this.tok.T_FN,
222
+ // using VARIABLES :
223
+ this.tok.T_VARIABLE,
224
+ "$",
225
+ this.tok.T_NS_SEPARATOR,
226
+ this.tok.T_STRING,
227
+ this.tok.T_NAME_RELATIVE,
228
+ this.tok.T_NAME_QUALIFIED,
229
+ this.tok.T_NAME_FULLY_QUALIFIED,
230
+ // using SCALAR :
231
+ this.tok.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
232
+ this.tok.T_CONSTANT_ENCAPSED_STRING,
233
+ this.tok.T_START_HEREDOC,
234
+ this.tok.T_LNUMBER,
235
+ this.tok.T_DNUMBER,
236
+ this.tok.T_ARRAY,
237
+ "[",
238
+ this.tok.T_CLASS_C,
239
+ this.tok.T_TRAIT_C,
240
+ this.tok.T_FUNC_C,
241
+ this.tok.T_METHOD_C,
242
+ this.tok.T_LINE,
243
+ this.tok.T_FILE,
244
+ this.tok.T_DIR,
245
+ this.tok.T_NS_C,
246
+ '"',
247
+ 'b"',
248
+ 'B"',
249
+ "-",
250
+ this.tok.T_NS_SEPARATOR,
251
+ ].map(mapIt),
252
+ ),
253
+ };
254
+ };
255
+
256
+ /**
257
+ * helper : gets a token name
258
+ * @function Parser#getTokenName
259
+ * @memberOf module:php-parser
260
+ */
261
+ Parser.prototype.getTokenName = function (token) {
262
+ if (!isNumber(token)) {
263
+ return "'" + token + "'";
264
+ } else {
265
+ if (token == this.EOF) return "the end of file (EOF)";
266
+ return this.lexer.engine.tokens.values[token];
267
+ }
268
+ };
269
+
270
+ /**
271
+ * main entry point : converts a source code to AST
272
+ * @function Parser#parse
273
+ * @memberOf module:php-parser
274
+ */
275
+ Parser.prototype.parse = function (code, filename) {
276
+ this._errors = [];
277
+ this.filename = filename || "eval";
278
+ this.currentNamespace = [""];
279
+ if (this.extractDoc) {
280
+ this._docs = [];
281
+ } else {
282
+ this._docs = null;
283
+ }
284
+ if (this.extractTokens) {
285
+ this._tokens = [];
286
+ } else {
287
+ this._tokens = null;
288
+ }
289
+ this._docIndex = 0;
290
+ this._lastNode = null;
291
+ this.lexer.setInput(code);
292
+ this.lexer.all_tokens = this.extractTokens;
293
+ this.lexer.comment_tokens = this.extractDoc;
294
+ this.length = this.lexer._input.length;
295
+ this.innerList = false;
296
+ this.innerListForm = false;
297
+ const program = this.node("program");
298
+ const childs = [];
299
+ this.next();
300
+ while (this.token != this.EOF) {
301
+ childs.push(this.read_start());
302
+ }
303
+ // append last comment
304
+ if (
305
+ childs.length === 0 &&
306
+ this.extractDoc &&
307
+ this._docs.length > this._docIndex
308
+ ) {
309
+ childs.push(this.node("noop")());
310
+ }
311
+ // #176 : register latest position
312
+ this.prev = [
313
+ this.lexer.yylloc.last_line,
314
+ this.lexer.yylloc.last_column,
315
+ this.lexer.offset,
316
+ ];
317
+ const result = program(childs, this._errors, this._docs, this._tokens);
318
+ if (this.debug) {
319
+ const errors = this.ast.checkNodes();
320
+ /* istanbul ignore next */
321
+ if (errors.length > 0) {
322
+ errors.forEach(function (error) {
323
+ if (error.position) {
324
+ // eslint-disable-next-line no-console
325
+ console.log(
326
+ "Node at line " +
327
+ error.position.line +
328
+ ", column " +
329
+ error.position.column,
330
+ );
331
+ }
332
+ // eslint-disable-next-line no-console
333
+ console.log(error.stack.join("\n"));
334
+ });
335
+ throw new Error("Some nodes are not closed");
336
+ }
337
+ }
338
+ return result;
339
+ };
340
+
341
+ /**
342
+ * Raise an error
343
+ * @function Parser#raiseError
344
+ * @memberOf module:php-parser
345
+ */
346
+ Parser.prototype.raiseError = function (message, msgExpect, expect, token) {
347
+ message += " on line " + this.lexer.yylloc.first_line;
348
+ if (!this.suppressErrors) {
349
+ const err = new SyntaxError(
350
+ message,
351
+ this.filename,
352
+ this.lexer.yylloc.first_line,
353
+ );
354
+ err.lineNumber = this.lexer.yylloc.first_line;
355
+ err.fileName = this.filename;
356
+ err.columnNumber = this.lexer.yylloc.first_column;
357
+ throw err;
358
+ }
359
+ // Error node :
360
+ const node = this.ast.prepare("error", null, this)(
361
+ message,
362
+ token,
363
+ this.lexer.yylloc.first_line,
364
+ expect,
365
+ );
366
+ this._errors.push(node);
367
+ return node;
368
+ };
369
+
370
+ /**
371
+ * handling errors
372
+ * @function Parser#error
373
+ * @memberOf module:php-parser
374
+ */
375
+ Parser.prototype.error = function (expect) {
376
+ let msg = "Parse Error : syntax error";
377
+ let token = this.getTokenName(this.token);
378
+ let msgExpect = "";
379
+
380
+ if (this.token !== this.EOF) {
381
+ if (isNumber(this.token)) {
382
+ let symbol = this.text();
383
+ /* istanbul ignore next */
384
+ if (symbol.length > 10) {
385
+ symbol = symbol.substring(0, 7) + "...";
386
+ }
387
+ token = "'" + symbol + "' (" + token + ")";
388
+ }
389
+ msg += ", unexpected " + token;
390
+ }
391
+ if (expect && !Array.isArray(expect)) {
392
+ if (isNumber(expect) || expect.length === 1) {
393
+ msgExpect = ", expecting " + this.getTokenName(expect);
394
+ }
395
+ msg += msgExpect;
396
+ }
397
+ return this.raiseError(msg, msgExpect, expect, token);
398
+ };
399
+
400
+ /**
401
+ * Create a position node from the lexers position
402
+ *
403
+ * @function Parser#position
404
+ * @memberOf module:php-parser
405
+ * @return {Position}
406
+ */
407
+ Parser.prototype.position = function () {
408
+ return new Position(
409
+ this.lexer.yylloc.first_line,
410
+ this.lexer.yylloc.first_column,
411
+ this.lexer.yylloc.first_offset,
412
+ );
413
+ };
414
+
415
+ /**
416
+ * Creates a new AST node
417
+ * @function Parser#node
418
+ * @memberOf module:php-parser
419
+ */
420
+ Parser.prototype.node = function (name) {
421
+ if (this.extractDoc) {
422
+ let docs = null;
423
+ if (this._docIndex < this._docs.length) {
424
+ docs = this._docs.slice(this._docIndex);
425
+ this._docIndex = this._docs.length;
426
+ /* istanbul ignore next */
427
+ if (this.debug) {
428
+ // eslint-disable-next-line no-console
429
+ console.log(new Error("Append docs on " + name));
430
+ // eslint-disable-next-line no-console
431
+ console.log(docs);
432
+ }
433
+ }
434
+ const node = this.ast.prepare(name, docs, this);
435
+ /*
436
+ * TOKENS :
437
+ * node1 commentA token commmentB node2 commentC token commentD node3 commentE token
438
+ *
439
+ * AST :
440
+ * structure:S1 [
441
+ * left: node1 ( trail: commentA ),
442
+ * right: structure:S2 [
443
+ * node2 (lead: commentB, trail: commentC),
444
+ * node3 (lead: commentD)
445
+ * ],
446
+ * trail: commentE
447
+ * ]
448
+ *
449
+ * Algorithm :
450
+ *
451
+ * Attach the last comments on parent of current node
452
+ * If a new node is started and the parent has a trailing comment
453
+ * the move it on previous node
454
+ *
455
+ * start S2
456
+ * start node1
457
+ * consume node1 & set commentA as trailingComment on S2
458
+ * start S2
459
+ * S1 has a trailingComment, attach it on node1
460
+ * ...
461
+ * NOTE : As the trailingComment Behavior depends on AST, it will be build on
462
+ * the AST layer - last child node will keep it's trailingComment nodes
463
+ */
464
+ node.postBuild = function (self) {
465
+ if (this._docIndex < this._docs.length) {
466
+ if (this._lastNode) {
467
+ const offset = this.prev[2];
468
+ let max = this._docIndex;
469
+ for (; max < this._docs.length; max++) {
470
+ if (this._docs[max].offset > offset) {
471
+ break;
472
+ }
473
+ }
474
+ if (max > this._docIndex) {
475
+ // inject trailing comment on child node
476
+ this._lastNode.setTrailingComments(
477
+ this._docs.slice(this._docIndex, max),
478
+ );
479
+ this._docIndex = max;
480
+ }
481
+ } else if (this.token === this.EOF) {
482
+ // end of content
483
+ self.setTrailingComments(this._docs.slice(this._docIndex));
484
+ this._docIndex = this._docs.length;
485
+ }
486
+ }
487
+ this._lastNode = self;
488
+ }.bind(this);
489
+ return node;
490
+ }
491
+ return this.ast.prepare(name, null, this);
492
+ };
493
+
494
+ /**
495
+ * expects an end of statement or end of file
496
+ * @function Parser#expectEndOfStatement
497
+ * @memberOf module:php-parser
498
+ * @return {boolean}
499
+ */
500
+ Parser.prototype.expectEndOfStatement = function (node) {
501
+ if (this.token === ";") {
502
+ // include only real ';' statements
503
+ // https://github.com/glayzzle/php-parser/issues/164
504
+ if (node && this.lexer.yytext === ";") {
505
+ node.includeToken(this);
506
+ }
507
+ } else if (this.token !== this.tok.T_INLINE_HTML && this.token !== this.EOF) {
508
+ this.error(";");
509
+ return false;
510
+ }
511
+ this.next();
512
+ return true;
513
+ };
514
+
515
+ const ignoreStack = ["parser.next", "parser.node", "parser.showlog"];
516
+ /**
517
+ * outputs some debug information on current token
518
+ * @private
519
+ * @function Parser#showlog
520
+ * @memberOf module:php-parser
521
+ */
522
+ Parser.prototype.showlog = function () {
523
+ const stack = new Error().stack.split("\n");
524
+ let line;
525
+ for (let offset = 2; offset < stack.length; offset++) {
526
+ line = stack[offset].trim();
527
+ let found = false;
528
+ for (let i = 0; i < ignoreStack.length; i++) {
529
+ /* istanbul ignore next */
530
+ if (line.substring(3, 3 + ignoreStack[i].length) === ignoreStack[i]) {
531
+ found = true;
532
+ break;
533
+ }
534
+ }
535
+ /* istanbul ignore next */
536
+ if (!found) {
537
+ break;
538
+ }
539
+ }
540
+ // eslint-disable-next-line no-console
541
+ console.log(
542
+ "Line " +
543
+ this.lexer.yylloc.first_line +
544
+ " : " +
545
+ this.getTokenName(this.token) +
546
+ ">" +
547
+ this.lexer.yytext +
548
+ "<" +
549
+ " @-->" +
550
+ line,
551
+ );
552
+ return this;
553
+ };
554
+
555
+ /**
556
+ * Force the parser to check the current token.
557
+ *
558
+ * If the current token does not match to expected token,
559
+ * the an error will be raised.
560
+ *
561
+ * If the suppressError mode is activated, then the error will
562
+ * be added to the program error stack and this function will return `false`.
563
+ *
564
+ * @function Parser#expect
565
+ * @memberOf module:php-parser
566
+ * @param {String|Number} token
567
+ * @return {boolean}
568
+ * @throws Error
569
+ */
570
+ Parser.prototype.expect = function (token) {
571
+ if (Array.isArray(token)) {
572
+ if (token.indexOf(this.token) === -1) {
573
+ this.error(token);
574
+ return false;
575
+ }
576
+ } else if (this.token != token) {
577
+ this.error(token);
578
+ return false;
579
+ }
580
+ return true;
581
+ };
582
+
583
+ /**
584
+ * Returns the current token contents
585
+ * @function Parser#text
586
+ * @memberOf module:php-parser
587
+ * @return {String}
588
+ */
589
+ Parser.prototype.text = function () {
590
+ return this.lexer.yytext;
591
+ };
592
+
593
+ /**
594
+ * consume the next token
595
+ * @function Parser#next
596
+ * @memberOf module:php-parser
597
+ */
598
+ Parser.prototype.next = function () {
599
+ // prepare the back command
600
+ if (this.token !== ";" || this.lexer.yytext === ";") {
601
+ // ignore '?>' from automated resolution
602
+ // https://github.com/glayzzle/php-parser/issues/168
603
+ this.prev = [
604
+ this.lexer.yylloc.last_line,
605
+ this.lexer.yylloc.last_column,
606
+ this.lexer.offset,
607
+ ];
608
+ }
609
+
610
+ // eating the token
611
+ this.lex();
612
+
613
+ // showing the debug
614
+ if (this.debug) {
615
+ this.showlog();
616
+ }
617
+
618
+ // handling comments
619
+ if (this.extractDoc) {
620
+ while (
621
+ this.token === this.tok.T_COMMENT ||
622
+ this.token === this.tok.T_DOC_COMMENT
623
+ ) {
624
+ // APPEND COMMENTS
625
+ if (this.token === this.tok.T_COMMENT) {
626
+ this._docs.push(this.read_comment());
627
+ } else {
628
+ this._docs.push(this.read_doc_comment());
629
+ }
630
+ }
631
+ }
632
+
633
+ return this;
634
+ };
635
+
636
+ /**
637
+ * Peek at the next token.
638
+ * @function Parser#peek
639
+ * @memberOf module:php-parser
640
+ * @returns {string|number} Next Token
641
+ */
642
+ Parser.prototype.peek = function () {
643
+ const lexerState = this.lexer.getState();
644
+ const nextToken = this.lexer.lex();
645
+ this.lexer.setState(lexerState);
646
+ return nextToken;
647
+ };
648
+
649
+ /**
650
+ * Eating a token
651
+ * @function Parser#lex
652
+ * @memberOf module:php-parser
653
+ */
654
+ Parser.prototype.lex = function () {
655
+ // append on token stack
656
+ if (this.extractTokens) {
657
+ do {
658
+ // the token
659
+ this.token = this.lexer.lex() || /* istanbul ignore next */ this.EOF;
660
+ if (this.token === this.EOF) return this;
661
+ let entry = this.lexer.yytext;
662
+ if (
663
+ Object.prototype.hasOwnProperty.call(
664
+ this.lexer.engine.tokens.values,
665
+ this.token,
666
+ )
667
+ ) {
668
+ entry = [
669
+ this.lexer.engine.tokens.values[this.token],
670
+ entry,
671
+ this.lexer.yylloc.first_line,
672
+ this.lexer.yylloc.first_offset,
673
+ this.lexer.offset,
674
+ ];
675
+ } else {
676
+ entry = [
677
+ null,
678
+ entry,
679
+ this.lexer.yylloc.first_line,
680
+ this.lexer.yylloc.first_offset,
681
+ this.lexer.offset,
682
+ ];
683
+ }
684
+ this._tokens.push(entry);
685
+ if (this.token === this.tok.T_CLOSE_TAG) {
686
+ // https://github.com/php/php-src/blob/7ff186434e82ee7be7c59d0db9a976641cf7b09c/Zend/zend_compile.c#L1680
687
+ this.token = ";";
688
+ return this;
689
+ } else if (this.token === this.tok.T_OPEN_TAG_WITH_ECHO) {
690
+ this.token = this.tok.T_ECHO;
691
+ return this;
692
+ }
693
+ } while (
694
+ this.token === this.tok.T_WHITESPACE || // ignore white space
695
+ (!this.extractDoc &&
696
+ (this.token === this.tok.T_COMMENT || // ignore single lines comments
697
+ this.token === this.tok.T_DOC_COMMENT)) || // ignore doc comments
698
+ // ignore open tags
699
+ this.token === this.tok.T_OPEN_TAG
700
+ );
701
+ } else {
702
+ this.token = this.lexer.lex() || /* istanbul ignore next */ this.EOF;
703
+ }
704
+ return this;
705
+ };
706
+
707
+ /**
708
+ * Check if token is of specified type
709
+ * @function Parser#is
710
+ * @memberOf module:php-parser
711
+ */
712
+ Parser.prototype.is = function (type) {
713
+ if (Array.isArray(type)) {
714
+ return type.indexOf(this.token) !== -1;
715
+ }
716
+ return this.entries[type].has(this.token);
717
+ };
718
+
719
+ // extends the parser with syntax files
720
+ [
721
+ require("./parser/array.js"),
722
+ require("./parser/class.js"),
723
+ require("./parser/comment.js"),
724
+ require("./parser/expr.js"),
725
+ require("./parser/enum.js"),
726
+ require("./parser/function.js"),
727
+ require("./parser/if.js"),
728
+ require("./parser/loops.js"),
729
+ require("./parser/main.js"),
730
+ require("./parser/namespace.js"),
731
+ require("./parser/scalar.js"),
732
+ require("./parser/statement.js"),
733
+ require("./parser/switch.js"),
734
+ require("./parser/try.js"),
735
+ require("./parser/utils.js"),
736
+ require("./parser/variable.js"),
737
+ ].forEach(function (ext) {
738
+ for (const k in ext) {
739
+ /* istanbul ignore next */
740
+ if (Object.prototype.hasOwnProperty.call(Parser.prototype, k)) {
741
+ // @see https://github.com/glayzzle/php-parser/issues/234
742
+ throw new Error("Function " + k + " is already defined - collision");
743
+ }
744
+ Parser.prototype[k] = ext[k];
745
+ }
746
+ });
747
+
748
+ module.exports = Parser;