@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.
- package/LICENSE +27 -0
- package/README.md +108 -0
- package/dist/@jorgsowa/php-parser.js +11239 -0
- package/dist/@jorgsowa/php-parser.min.js +2 -0
- package/dist/@jorgsowa/php-parser.min.js.LICENSE.txt +10 -0
- package/package.json +86 -0
- package/src/ast/array.js +44 -0
- package/src/ast/arrowfunc.js +43 -0
- package/src/ast/assign.js +28 -0
- package/src/ast/assignref.js +27 -0
- package/src/ast/attrgroup.js +21 -0
- package/src/ast/attribute.js +26 -0
- package/src/ast/bin.js +27 -0
- package/src/ast/block.js +24 -0
- package/src/ast/boolean.js +23 -0
- package/src/ast/break.js +21 -0
- package/src/ast/byref.js +21 -0
- package/src/ast/call.js +26 -0
- package/src/ast/case.js +26 -0
- package/src/ast/cast.js +28 -0
- package/src/ast/catch.js +29 -0
- package/src/ast/class.js +36 -0
- package/src/ast/classconstant.js +71 -0
- package/src/ast/clone.js +21 -0
- package/src/ast/closure.js +47 -0
- package/src/ast/comment.js +23 -0
- package/src/ast/commentblock.js +22 -0
- package/src/ast/commentline.js +22 -0
- package/src/ast/constant.js +26 -0
- package/src/ast/constantstatement.js +24 -0
- package/src/ast/continue.js +24 -0
- package/src/ast/declaration.js +60 -0
- package/src/ast/declare.js +71 -0
- package/src/ast/declaredirective.js +26 -0
- package/src/ast/do.js +26 -0
- package/src/ast/echo.js +26 -0
- package/src/ast/empty.js +23 -0
- package/src/ast/encapsed.js +75 -0
- package/src/ast/encapsedpart.js +28 -0
- package/src/ast/entry.js +30 -0
- package/src/ast/enum.js +30 -0
- package/src/ast/enumcase.js +26 -0
- package/src/ast/error.js +30 -0
- package/src/ast/eval.js +24 -0
- package/src/ast/exit.js +26 -0
- package/src/ast/expression.js +20 -0
- package/src/ast/expressionstatement.js +24 -0
- package/src/ast/for.js +33 -0
- package/src/ast/foreach.js +33 -0
- package/src/ast/function.js +34 -0
- package/src/ast/global.js +24 -0
- package/src/ast/goto.js +22 -0
- package/src/ast/halt.js +22 -0
- package/src/ast/identifier.js +26 -0
- package/src/ast/if.js +30 -0
- package/src/ast/include.js +28 -0
- package/src/ast/inline.js +23 -0
- package/src/ast/interface.js +28 -0
- package/src/ast/intersectiontype.js +24 -0
- package/src/ast/isset.js +23 -0
- package/src/ast/label.js +21 -0
- package/src/ast/list.js +26 -0
- package/src/ast/literal.js +28 -0
- package/src/ast/location.js +22 -0
- package/src/ast/lookup.js +26 -0
- package/src/ast/magic.js +22 -0
- package/src/ast/match.js +26 -0
- package/src/ast/matcharm.js +26 -0
- package/src/ast/method.js +24 -0
- package/src/ast/name.js +55 -0
- package/src/ast/namedargument.js +27 -0
- package/src/ast/namespace.js +26 -0
- package/src/ast/new.js +26 -0
- package/src/ast/node.js +111 -0
- package/src/ast/noop.js +20 -0
- package/src/ast/nowdoc.js +26 -0
- package/src/ast/nullkeyword.js +20 -0
- package/src/ast/nullsafepropertylookup.js +22 -0
- package/src/ast/number.js +23 -0
- package/src/ast/offsetlookup.js +22 -0
- package/src/ast/operation.js +19 -0
- package/src/ast/parameter.js +61 -0
- package/src/ast/parentreference.js +24 -0
- package/src/ast/position.js +22 -0
- package/src/ast/post.js +26 -0
- package/src/ast/pre.js +26 -0
- package/src/ast/print.js +23 -0
- package/src/ast/program.js +32 -0
- package/src/ast/property.js +46 -0
- package/src/ast/propertyhook.js +33 -0
- package/src/ast/propertylookup.js +22 -0
- package/src/ast/propertystatement.js +59 -0
- package/src/ast/reference.js +21 -0
- package/src/ast/retif.js +28 -0
- package/src/ast/return.js +21 -0
- package/src/ast/selfreference.js +24 -0
- package/src/ast/silent.js +24 -0
- package/src/ast/statement.js +19 -0
- package/src/ast/static.js +24 -0
- package/src/ast/staticlookup.js +22 -0
- package/src/ast/staticreference.js +24 -0
- package/src/ast/staticvariable.js +26 -0
- package/src/ast/string.js +28 -0
- package/src/ast/switch.js +28 -0
- package/src/ast/throw.js +21 -0
- package/src/ast/trait.js +24 -0
- package/src/ast/traitalias.js +44 -0
- package/src/ast/traitprecedence.js +28 -0
- package/src/ast/traituse.js +26 -0
- package/src/ast/try.js +28 -0
- package/src/ast/typereference.js +40 -0
- package/src/ast/unary.js +26 -0
- package/src/ast/uniontype.js +24 -0
- package/src/ast/unset.js +23 -0
- package/src/ast/usegroup.js +30 -0
- package/src/ast/useitem.js +45 -0
- package/src/ast/variable.js +36 -0
- package/src/ast/variadic.js +25 -0
- package/src/ast/variadicplaceholder.js +24 -0
- package/src/ast/while.js +28 -0
- package/src/ast/yield.js +27 -0
- package/src/ast/yieldfrom.js +25 -0
- package/src/ast.js +593 -0
- package/src/index.js +239 -0
- package/src/lexer/attribute.js +85 -0
- package/src/lexer/comments.js +63 -0
- package/src/lexer/initial.js +64 -0
- package/src/lexer/numbers.js +171 -0
- package/src/lexer/property.js +96 -0
- package/src/lexer/scripting.js +114 -0
- package/src/lexer/strings.js +524 -0
- package/src/lexer/tokens.js +356 -0
- package/src/lexer/utils.js +112 -0
- package/src/lexer.js +561 -0
- package/src/parser/array.js +113 -0
- package/src/parser/class.js +718 -0
- package/src/parser/comment.js +52 -0
- package/src/parser/enum.js +56 -0
- package/src/parser/expr.js +848 -0
- package/src/parser/function.js +507 -0
- package/src/parser/if.js +94 -0
- package/src/parser/loops.js +168 -0
- package/src/parser/main.js +21 -0
- package/src/parser/namespace.js +231 -0
- package/src/parser/scalar.js +492 -0
- package/src/parser/statement.js +444 -0
- package/src/parser/switch.js +99 -0
- package/src/parser/try.js +43 -0
- package/src/parser/utils.js +203 -0
- package/src/parser/variable.js +363 -0
- package/src/parser.js +748 -0
- package/src/tokens.js +177 -0
- 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;
|