@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/lexer.js
ADDED
|
@@ -0,0 +1,561 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* This is the php lexer. It will tokenize the string for helping the
|
|
10
|
+
* parser to build the AST from its grammar.
|
|
11
|
+
*
|
|
12
|
+
* @constructor Lexer
|
|
13
|
+
* @memberOf module:php-parser
|
|
14
|
+
* @property {number} EOF
|
|
15
|
+
* @property {boolean} all_tokens defines if all tokens must be retrieved (used by token_get_all only)
|
|
16
|
+
* @property {boolean} comment_tokens extracts comments tokens
|
|
17
|
+
* @property {boolean} mode_eval enables the evald mode (ignore opening tags)
|
|
18
|
+
* @property {boolean} asp_tags disables by default asp tags mode
|
|
19
|
+
* @property {boolean} short_tags enables by default short tags mode
|
|
20
|
+
* @property {object} keywords List of php keyword
|
|
21
|
+
* @property {object} castKeywords List of php keywords for type casting
|
|
22
|
+
*/
|
|
23
|
+
const Lexer = function (engine) {
|
|
24
|
+
this.engine = engine;
|
|
25
|
+
this.tok = this.engine.tokens.names;
|
|
26
|
+
this.EOF = 1;
|
|
27
|
+
this.debug = false;
|
|
28
|
+
this.all_tokens = true;
|
|
29
|
+
this.comment_tokens = false;
|
|
30
|
+
this.mode_eval = false;
|
|
31
|
+
this.asp_tags = false;
|
|
32
|
+
this.short_tags = false;
|
|
33
|
+
this.version = 803;
|
|
34
|
+
this.yyprevcol = 0;
|
|
35
|
+
this.keywords = {
|
|
36
|
+
__class__: this.tok.T_CLASS_C,
|
|
37
|
+
__trait__: this.tok.T_TRAIT_C,
|
|
38
|
+
__function__: this.tok.T_FUNC_C,
|
|
39
|
+
__method__: this.tok.T_METHOD_C,
|
|
40
|
+
__line__: this.tok.T_LINE,
|
|
41
|
+
__file__: this.tok.T_FILE,
|
|
42
|
+
__dir__: this.tok.T_DIR,
|
|
43
|
+
__namespace__: this.tok.T_NS_C,
|
|
44
|
+
exit: this.tok.T_EXIT,
|
|
45
|
+
die: this.tok.T_EXIT,
|
|
46
|
+
function: this.tok.T_FUNCTION,
|
|
47
|
+
const: this.tok.T_CONST,
|
|
48
|
+
return: this.tok.T_RETURN,
|
|
49
|
+
try: this.tok.T_TRY,
|
|
50
|
+
catch: this.tok.T_CATCH,
|
|
51
|
+
finally: this.tok.T_FINALLY,
|
|
52
|
+
throw: this.tok.T_THROW,
|
|
53
|
+
if: this.tok.T_IF,
|
|
54
|
+
elseif: this.tok.T_ELSEIF,
|
|
55
|
+
endif: this.tok.T_ENDIF,
|
|
56
|
+
else: this.tok.T_ELSE,
|
|
57
|
+
while: this.tok.T_WHILE,
|
|
58
|
+
endwhile: this.tok.T_ENDWHILE,
|
|
59
|
+
do: this.tok.T_DO,
|
|
60
|
+
for: this.tok.T_FOR,
|
|
61
|
+
endfor: this.tok.T_ENDFOR,
|
|
62
|
+
foreach: this.tok.T_FOREACH,
|
|
63
|
+
endforeach: this.tok.T_ENDFOREACH,
|
|
64
|
+
declare: this.tok.T_DECLARE,
|
|
65
|
+
enddeclare: this.tok.T_ENDDECLARE,
|
|
66
|
+
instanceof: this.tok.T_INSTANCEOF,
|
|
67
|
+
as: this.tok.T_AS,
|
|
68
|
+
switch: this.tok.T_SWITCH,
|
|
69
|
+
endswitch: this.tok.T_ENDSWITCH,
|
|
70
|
+
case: this.tok.T_CASE,
|
|
71
|
+
default: this.tok.T_DEFAULT,
|
|
72
|
+
break: this.tok.T_BREAK,
|
|
73
|
+
continue: this.tok.T_CONTINUE,
|
|
74
|
+
goto: this.tok.T_GOTO,
|
|
75
|
+
echo: this.tok.T_ECHO,
|
|
76
|
+
print: this.tok.T_PRINT,
|
|
77
|
+
class: this.tok.T_CLASS,
|
|
78
|
+
interface: this.tok.T_INTERFACE,
|
|
79
|
+
trait: this.tok.T_TRAIT,
|
|
80
|
+
enum: this.tok.T_ENUM,
|
|
81
|
+
extends: this.tok.T_EXTENDS,
|
|
82
|
+
implements: this.tok.T_IMPLEMENTS,
|
|
83
|
+
new: this.tok.T_NEW,
|
|
84
|
+
clone: this.tok.T_CLONE,
|
|
85
|
+
var: this.tok.T_VAR,
|
|
86
|
+
eval: this.tok.T_EVAL,
|
|
87
|
+
include: this.tok.T_INCLUDE,
|
|
88
|
+
include_once: this.tok.T_INCLUDE_ONCE,
|
|
89
|
+
require: this.tok.T_REQUIRE,
|
|
90
|
+
require_once: this.tok.T_REQUIRE_ONCE,
|
|
91
|
+
namespace: this.tok.T_NAMESPACE,
|
|
92
|
+
use: this.tok.T_USE,
|
|
93
|
+
insteadof: this.tok.T_INSTEADOF,
|
|
94
|
+
global: this.tok.T_GLOBAL,
|
|
95
|
+
isset: this.tok.T_ISSET,
|
|
96
|
+
empty: this.tok.T_EMPTY,
|
|
97
|
+
__halt_compiler: this.tok.T_HALT_COMPILER,
|
|
98
|
+
static: this.tok.T_STATIC,
|
|
99
|
+
abstract: this.tok.T_ABSTRACT,
|
|
100
|
+
final: this.tok.T_FINAL,
|
|
101
|
+
private: this.tok.T_PRIVATE,
|
|
102
|
+
protected: this.tok.T_PROTECTED,
|
|
103
|
+
public: this.tok.T_PUBLIC,
|
|
104
|
+
unset: this.tok.T_UNSET,
|
|
105
|
+
list: this.tok.T_LIST,
|
|
106
|
+
array: this.tok.T_ARRAY,
|
|
107
|
+
callable: this.tok.T_CALLABLE,
|
|
108
|
+
or: this.tok.T_LOGICAL_OR,
|
|
109
|
+
and: this.tok.T_LOGICAL_AND,
|
|
110
|
+
xor: this.tok.T_LOGICAL_XOR,
|
|
111
|
+
match: this.tok.T_MATCH,
|
|
112
|
+
readonly: this.tok.T_READ_ONLY,
|
|
113
|
+
};
|
|
114
|
+
this.castKeywords = {
|
|
115
|
+
int: this.tok.T_INT_CAST,
|
|
116
|
+
integer: this.tok.T_INT_CAST,
|
|
117
|
+
real: this.tok.T_DOUBLE_CAST,
|
|
118
|
+
double: this.tok.T_DOUBLE_CAST,
|
|
119
|
+
float: this.tok.T_DOUBLE_CAST,
|
|
120
|
+
string: this.tok.T_STRING_CAST,
|
|
121
|
+
binary: this.tok.T_STRING_CAST,
|
|
122
|
+
array: this.tok.T_ARRAY_CAST,
|
|
123
|
+
object: this.tok.T_OBJECT_CAST,
|
|
124
|
+
bool: this.tok.T_BOOL_CAST,
|
|
125
|
+
boolean: this.tok.T_BOOL_CAST,
|
|
126
|
+
unset: this.tok.T_UNSET_CAST,
|
|
127
|
+
};
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Initialize the lexer with the specified input
|
|
132
|
+
* @function Lexer#setInput
|
|
133
|
+
* @memberOf module:php-parser
|
|
134
|
+
*/
|
|
135
|
+
Lexer.prototype.setInput = function (input) {
|
|
136
|
+
this._input = input;
|
|
137
|
+
this.size = input.length;
|
|
138
|
+
this.yylineno = 1;
|
|
139
|
+
this.offset = 0;
|
|
140
|
+
this.yyprevcol = 0;
|
|
141
|
+
this.yytext = "";
|
|
142
|
+
this.yylloc = {
|
|
143
|
+
first_offset: 0,
|
|
144
|
+
first_line: 1,
|
|
145
|
+
first_column: 0,
|
|
146
|
+
prev_offset: 0,
|
|
147
|
+
prev_line: 1,
|
|
148
|
+
prev_column: 0,
|
|
149
|
+
last_line: 1,
|
|
150
|
+
last_column: 0,
|
|
151
|
+
};
|
|
152
|
+
this.tokens = [];
|
|
153
|
+
if (this.version > 703) {
|
|
154
|
+
this.keywords.fn = this.tok.T_FN;
|
|
155
|
+
} else {
|
|
156
|
+
delete this.keywords.fn;
|
|
157
|
+
}
|
|
158
|
+
this.done = this.offset >= this.size;
|
|
159
|
+
if (!this.all_tokens && this.mode_eval) {
|
|
160
|
+
this.conditionStack = ["INITIAL"];
|
|
161
|
+
this.begin("ST_IN_SCRIPTING");
|
|
162
|
+
} else {
|
|
163
|
+
this.conditionStack = [];
|
|
164
|
+
this.begin("INITIAL");
|
|
165
|
+
}
|
|
166
|
+
// https://github.com/php/php-src/blob/999e32b65a8a4bb59e27e538fa68ffae4b99d863/Zend/zend_language_scanner.h#L59
|
|
167
|
+
// Used for heredoc and nowdoc
|
|
168
|
+
this.heredoc_label = {
|
|
169
|
+
label: "",
|
|
170
|
+
length: 0,
|
|
171
|
+
indentation: 0,
|
|
172
|
+
indentation_uses_spaces: false,
|
|
173
|
+
finished: false,
|
|
174
|
+
/*
|
|
175
|
+
* this used for parser to detemine the if current node segment is first encaps node.
|
|
176
|
+
* if ture, the indentation will remove from the begining. and if false, the prev node
|
|
177
|
+
* might be a variable '}' ,and the leading spaces should not be removed util meet the
|
|
178
|
+
* first \n
|
|
179
|
+
*/
|
|
180
|
+
first_encaps_node: false,
|
|
181
|
+
// for backward compatible
|
|
182
|
+
/* istanbul ignore next */
|
|
183
|
+
toString() {
|
|
184
|
+
this.label;
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
return this;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* consumes and returns one char from the input
|
|
192
|
+
* @function Lexer#input
|
|
193
|
+
* @memberOf module:php-parser
|
|
194
|
+
*/
|
|
195
|
+
Lexer.prototype.input = function () {
|
|
196
|
+
const ch = this._input[this.offset];
|
|
197
|
+
if (!ch) return "";
|
|
198
|
+
this.yytext += ch;
|
|
199
|
+
this.offset++;
|
|
200
|
+
if (ch === "\r" && this._input[this.offset] === "\n") {
|
|
201
|
+
this.yytext += "\n";
|
|
202
|
+
this.offset++;
|
|
203
|
+
}
|
|
204
|
+
if (ch === "\n" || ch === "\r") {
|
|
205
|
+
this.yylloc.last_line = ++this.yylineno;
|
|
206
|
+
this.yyprevcol = this.yylloc.last_column;
|
|
207
|
+
this.yylloc.last_column = 0;
|
|
208
|
+
} else {
|
|
209
|
+
this.yylloc.last_column++;
|
|
210
|
+
}
|
|
211
|
+
return ch;
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* revert eating specified size
|
|
216
|
+
* @function Lexer#unput
|
|
217
|
+
* @memberOf module:php-parser
|
|
218
|
+
*/
|
|
219
|
+
Lexer.prototype.unput = function (size) {
|
|
220
|
+
if (size === 1) {
|
|
221
|
+
// 1 char unput (most cases)
|
|
222
|
+
this.offset--;
|
|
223
|
+
if (
|
|
224
|
+
this._input[this.offset] === "\n" &&
|
|
225
|
+
this._input[this.offset - 1] === "\r"
|
|
226
|
+
) {
|
|
227
|
+
this.offset--;
|
|
228
|
+
size++;
|
|
229
|
+
}
|
|
230
|
+
if (
|
|
231
|
+
this._input[this.offset] === "\r" ||
|
|
232
|
+
this._input[this.offset] === "\n"
|
|
233
|
+
) {
|
|
234
|
+
this.yylloc.last_line--;
|
|
235
|
+
this.yylineno--;
|
|
236
|
+
this.yylloc.last_column = this.yyprevcol;
|
|
237
|
+
} else {
|
|
238
|
+
this.yylloc.last_column--;
|
|
239
|
+
}
|
|
240
|
+
this.yytext = this.yytext.substring(0, this.yytext.length - size);
|
|
241
|
+
} else if (size > 0) {
|
|
242
|
+
this.offset -= size;
|
|
243
|
+
if (size < this.yytext.length) {
|
|
244
|
+
this.yytext = this.yytext.substring(0, this.yytext.length - size);
|
|
245
|
+
// re-calculate position
|
|
246
|
+
this.yylloc.last_line = this.yylloc.first_line;
|
|
247
|
+
this.yylloc.last_column = this.yyprevcol = this.yylloc.first_column;
|
|
248
|
+
for (let i = 0; i < this.yytext.length; i++) {
|
|
249
|
+
let c = this.yytext[i];
|
|
250
|
+
if (c === "\r") {
|
|
251
|
+
c = this.yytext[++i];
|
|
252
|
+
this.yyprevcol = this.yylloc.last_column;
|
|
253
|
+
this.yylloc.last_line++;
|
|
254
|
+
this.yylloc.last_column = 0;
|
|
255
|
+
if (c !== "\n") {
|
|
256
|
+
if (c === "\r") {
|
|
257
|
+
this.yylloc.last_line++;
|
|
258
|
+
} else {
|
|
259
|
+
this.yylloc.last_column++;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
} else if (c === "\n") {
|
|
263
|
+
this.yyprevcol = this.yylloc.last_column;
|
|
264
|
+
this.yylloc.last_line++;
|
|
265
|
+
this.yylloc.last_column = 0;
|
|
266
|
+
} else {
|
|
267
|
+
this.yylloc.last_column++;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
this.yylineno = this.yylloc.last_line;
|
|
271
|
+
} else {
|
|
272
|
+
// reset full text
|
|
273
|
+
this.yytext = "";
|
|
274
|
+
this.yylloc.last_line = this.yylineno = this.yylloc.first_line;
|
|
275
|
+
this.yylloc.last_column = this.yylloc.first_column;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return this;
|
|
280
|
+
};
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* check if the text matches
|
|
284
|
+
* @function Lexer#tryMatch
|
|
285
|
+
* @memberOf module:php-parser
|
|
286
|
+
* @param {string} text
|
|
287
|
+
* @returns {boolean}
|
|
288
|
+
*/
|
|
289
|
+
Lexer.prototype.tryMatch = function (text) {
|
|
290
|
+
return text === this.ahead(text.length);
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* check if the text matches
|
|
295
|
+
* @function Lexer#tryMatchCaseless
|
|
296
|
+
* @memberOf module:php-parser
|
|
297
|
+
* @param {string} text
|
|
298
|
+
* @returns {boolean}
|
|
299
|
+
*/
|
|
300
|
+
Lexer.prototype.tryMatchCaseless = function (text) {
|
|
301
|
+
return text === this.ahead(text.length).toLowerCase();
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* look ahead
|
|
306
|
+
* @function Lexer#ahead
|
|
307
|
+
* @memberOf module:php-parser
|
|
308
|
+
* @param {number} size
|
|
309
|
+
* @returns {string}
|
|
310
|
+
*/
|
|
311
|
+
Lexer.prototype.ahead = function (size) {
|
|
312
|
+
let text = this._input.substring(this.offset, this.offset + size);
|
|
313
|
+
if (
|
|
314
|
+
text[text.length - 1] === "\r" &&
|
|
315
|
+
this._input[this.offset + size + 1] === "\n"
|
|
316
|
+
) {
|
|
317
|
+
text += "\n";
|
|
318
|
+
}
|
|
319
|
+
return text;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* consume the specified size
|
|
324
|
+
* @function Lexer#consume
|
|
325
|
+
* @memberOf module:php-parser
|
|
326
|
+
* @param {number} size
|
|
327
|
+
* @returns {Lexer}
|
|
328
|
+
*/
|
|
329
|
+
Lexer.prototype.consume = function (size) {
|
|
330
|
+
for (let i = 0; i < size; i++) {
|
|
331
|
+
const ch = this._input[this.offset];
|
|
332
|
+
if (!ch) break;
|
|
333
|
+
this.yytext += ch;
|
|
334
|
+
this.offset++;
|
|
335
|
+
if (ch === "\r" && this._input[this.offset] === "\n") {
|
|
336
|
+
this.yytext += "\n";
|
|
337
|
+
this.offset++;
|
|
338
|
+
i++;
|
|
339
|
+
}
|
|
340
|
+
if (ch === "\n" || ch === "\r") {
|
|
341
|
+
this.yylloc.last_line = ++this.yylineno;
|
|
342
|
+
this.yyprevcol = this.yylloc.last_column;
|
|
343
|
+
this.yylloc.last_column = 0;
|
|
344
|
+
} else {
|
|
345
|
+
this.yylloc.last_column++;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return this;
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Gets the current state
|
|
353
|
+
* @function Lexer#getState
|
|
354
|
+
* @memberOf module:php-parser
|
|
355
|
+
*/
|
|
356
|
+
Lexer.prototype.getState = function () {
|
|
357
|
+
return {
|
|
358
|
+
yytext: this.yytext,
|
|
359
|
+
offset: this.offset,
|
|
360
|
+
yylineno: this.yylineno,
|
|
361
|
+
yyprevcol: this.yyprevcol,
|
|
362
|
+
yylloc: {
|
|
363
|
+
first_offset: this.yylloc.first_offset,
|
|
364
|
+
first_line: this.yylloc.first_line,
|
|
365
|
+
first_column: this.yylloc.first_column,
|
|
366
|
+
last_line: this.yylloc.last_line,
|
|
367
|
+
last_column: this.yylloc.last_column,
|
|
368
|
+
},
|
|
369
|
+
heredoc_label: this.heredoc_label,
|
|
370
|
+
};
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Sets the current lexer state
|
|
375
|
+
* @function Lexer#setState
|
|
376
|
+
* @memberOf module:php-parser
|
|
377
|
+
*/
|
|
378
|
+
Lexer.prototype.setState = function (state) {
|
|
379
|
+
this.yytext = state.yytext;
|
|
380
|
+
this.offset = state.offset;
|
|
381
|
+
this.yylineno = state.yylineno;
|
|
382
|
+
this.yyprevcol = state.yyprevcol;
|
|
383
|
+
this.yylloc = state.yylloc;
|
|
384
|
+
if (state.heredoc_label) {
|
|
385
|
+
this.heredoc_label = state.heredoc_label;
|
|
386
|
+
}
|
|
387
|
+
return this;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* prepend next token
|
|
392
|
+
* @function Lexer#appendToken
|
|
393
|
+
* @memberOf module:php-parser
|
|
394
|
+
* @param {*} value
|
|
395
|
+
* @param {*} ahead
|
|
396
|
+
* @returns {Lexer}
|
|
397
|
+
*/
|
|
398
|
+
Lexer.prototype.appendToken = function (value, ahead) {
|
|
399
|
+
this.tokens.push([value, ahead]);
|
|
400
|
+
return this;
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* return next match that has a token
|
|
405
|
+
* @function Lexer#lex
|
|
406
|
+
* @memberOf module:php-parser
|
|
407
|
+
* @returns {number|string}
|
|
408
|
+
*/
|
|
409
|
+
Lexer.prototype.lex = function () {
|
|
410
|
+
this.yylloc.prev_offset = this.offset;
|
|
411
|
+
this.yylloc.prev_line = this.yylloc.last_line;
|
|
412
|
+
this.yylloc.prev_column = this.yylloc.last_column;
|
|
413
|
+
let token = this.next() || this.lex();
|
|
414
|
+
if (!this.all_tokens) {
|
|
415
|
+
while (
|
|
416
|
+
token === this.tok.T_WHITESPACE || // ignore white space
|
|
417
|
+
(!this.comment_tokens &&
|
|
418
|
+
(token === this.tok.T_COMMENT || // ignore single lines comments
|
|
419
|
+
token === this.tok.T_DOC_COMMENT)) || // ignore doc comments
|
|
420
|
+
// ignore open tags
|
|
421
|
+
token === this.tok.T_OPEN_TAG
|
|
422
|
+
) {
|
|
423
|
+
token = this.next() || this.lex();
|
|
424
|
+
}
|
|
425
|
+
if (token == this.tok.T_OPEN_TAG_WITH_ECHO) {
|
|
426
|
+
// https://github.com/php/php-src/blob/7ff186434e82ee7be7c59d0db9a976641cf7b09c/Zend/zend_compile.c#L1683
|
|
427
|
+
// open tag with echo statement
|
|
428
|
+
return this.tok.T_ECHO;
|
|
429
|
+
} else if (token === this.tok.T_CLOSE_TAG) {
|
|
430
|
+
// https://github.com/php/php-src/blob/7ff186434e82ee7be7c59d0db9a976641cf7b09c/Zend/zend_compile.c#L1680
|
|
431
|
+
return ";"; /* implicit ; */
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
if (!this.yylloc.prev_offset) {
|
|
435
|
+
this.yylloc.prev_offset = this.yylloc.first_offset;
|
|
436
|
+
this.yylloc.prev_line = this.yylloc.first_line;
|
|
437
|
+
this.yylloc.prev_column = this.yylloc.first_column;
|
|
438
|
+
}
|
|
439
|
+
/*else if (this.yylloc.prev_offset === this.offset && this.offset !== this.size) {
|
|
440
|
+
throw new Error('Infinite loop @ ' + this.offset + ' / ' + this.size);
|
|
441
|
+
}*/
|
|
442
|
+
return token;
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* activates a new lexer condition state (pushes the new lexer condition state onto the condition stack)
|
|
447
|
+
* @function Lexer#begin
|
|
448
|
+
* @memberOf module:php-parser
|
|
449
|
+
* @param {*} condition
|
|
450
|
+
* @returns {Lexer}
|
|
451
|
+
*/
|
|
452
|
+
Lexer.prototype.begin = function (condition) {
|
|
453
|
+
this.conditionStack.push(condition);
|
|
454
|
+
this.curCondition = condition;
|
|
455
|
+
this.stateCb = this["match" + condition];
|
|
456
|
+
/* istanbul ignore next */
|
|
457
|
+
if (typeof this.stateCb !== "function") {
|
|
458
|
+
throw new Error('Undefined condition state "' + condition + '"');
|
|
459
|
+
}
|
|
460
|
+
return this;
|
|
461
|
+
};
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* pop the previously active lexer condition state off the condition stack
|
|
465
|
+
* @function Lexer#popState
|
|
466
|
+
* @memberOf module:php-parser
|
|
467
|
+
* @returns {string|*}
|
|
468
|
+
*/
|
|
469
|
+
Lexer.prototype.popState = function () {
|
|
470
|
+
const n = this.conditionStack.length - 1;
|
|
471
|
+
const condition = n > 0 ? this.conditionStack.pop() : this.conditionStack[0];
|
|
472
|
+
this.curCondition = this.conditionStack[this.conditionStack.length - 1];
|
|
473
|
+
this.stateCb = this["match" + this.curCondition];
|
|
474
|
+
/* istanbul ignore next */
|
|
475
|
+
if (typeof this.stateCb !== "function") {
|
|
476
|
+
throw new Error('Undefined condition state "' + this.curCondition + '"');
|
|
477
|
+
}
|
|
478
|
+
return condition;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* return next match in input
|
|
483
|
+
* @function Lexer#next
|
|
484
|
+
* @memberOf module:php-parser
|
|
485
|
+
* @returns {number|*}
|
|
486
|
+
*/
|
|
487
|
+
Lexer.prototype.next = function () {
|
|
488
|
+
let token;
|
|
489
|
+
if (!this._input) {
|
|
490
|
+
this.done = true;
|
|
491
|
+
}
|
|
492
|
+
this.yylloc.first_offset = this.offset;
|
|
493
|
+
this.yylloc.first_line = this.yylloc.last_line;
|
|
494
|
+
this.yylloc.first_column = this.yylloc.last_column;
|
|
495
|
+
this.yytext = "";
|
|
496
|
+
if (this.done) {
|
|
497
|
+
this.yylloc.prev_offset = this.yylloc.first_offset;
|
|
498
|
+
this.yylloc.prev_line = this.yylloc.first_line;
|
|
499
|
+
this.yylloc.prev_column = this.yylloc.first_column;
|
|
500
|
+
return this.EOF;
|
|
501
|
+
}
|
|
502
|
+
if (this.tokens.length > 0) {
|
|
503
|
+
token = this.tokens.shift();
|
|
504
|
+
if (typeof token[1] === "object") {
|
|
505
|
+
this.setState(token[1]);
|
|
506
|
+
} else {
|
|
507
|
+
this.consume(token[1]);
|
|
508
|
+
}
|
|
509
|
+
token = token[0];
|
|
510
|
+
} else {
|
|
511
|
+
token = this.stateCb.apply(this, []);
|
|
512
|
+
}
|
|
513
|
+
if (this.offset >= this.size && this.tokens.length === 0) {
|
|
514
|
+
this.done = true;
|
|
515
|
+
}
|
|
516
|
+
/* istanbul ignore next */
|
|
517
|
+
if (this.debug) {
|
|
518
|
+
let tName = token;
|
|
519
|
+
if (typeof tName === "number") {
|
|
520
|
+
tName = this.engine.tokens.values[tName];
|
|
521
|
+
} else {
|
|
522
|
+
tName = '"' + tName + '"';
|
|
523
|
+
}
|
|
524
|
+
const e = new Error(
|
|
525
|
+
tName +
|
|
526
|
+
"\tfrom " +
|
|
527
|
+
this.yylloc.first_line +
|
|
528
|
+
"," +
|
|
529
|
+
this.yylloc.first_column +
|
|
530
|
+
"\t - to " +
|
|
531
|
+
this.yylloc.last_line +
|
|
532
|
+
"," +
|
|
533
|
+
this.yylloc.last_column +
|
|
534
|
+
'\t"' +
|
|
535
|
+
this.yytext +
|
|
536
|
+
'"',
|
|
537
|
+
);
|
|
538
|
+
// eslint-disable-next-line no-console
|
|
539
|
+
console.error(e.stack);
|
|
540
|
+
}
|
|
541
|
+
return token;
|
|
542
|
+
};
|
|
543
|
+
|
|
544
|
+
// extends the lexer with states
|
|
545
|
+
[
|
|
546
|
+
require("./lexer/attribute.js"),
|
|
547
|
+
require("./lexer/comments.js"),
|
|
548
|
+
require("./lexer/initial.js"),
|
|
549
|
+
require("./lexer/numbers.js"),
|
|
550
|
+
require("./lexer/property.js"),
|
|
551
|
+
require("./lexer/scripting.js"),
|
|
552
|
+
require("./lexer/strings.js"),
|
|
553
|
+
require("./lexer/tokens.js"),
|
|
554
|
+
require("./lexer/utils.js"),
|
|
555
|
+
].forEach(function (ext) {
|
|
556
|
+
for (const k in ext) {
|
|
557
|
+
Lexer.prototype[k] = ext[k];
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
module.exports = Lexer;
|
|
@@ -0,0 +1,113 @@
|
|
|
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
|
+
module.exports = {
|
|
9
|
+
/*
|
|
10
|
+
* Parse an array
|
|
11
|
+
* ```ebnf
|
|
12
|
+
* array ::= T_ARRAY '(' array_pair_list ')' |
|
|
13
|
+
* '[' array_pair_list ']'
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
read_array() {
|
|
17
|
+
let expect = null;
|
|
18
|
+
let shortForm = false;
|
|
19
|
+
const result = this.node("array");
|
|
20
|
+
|
|
21
|
+
if (this.token === this.tok.T_ARRAY) {
|
|
22
|
+
this.next().expect("(");
|
|
23
|
+
expect = ")";
|
|
24
|
+
} else {
|
|
25
|
+
shortForm = true;
|
|
26
|
+
expect = "]";
|
|
27
|
+
}
|
|
28
|
+
let items = [];
|
|
29
|
+
if (this.next().token !== expect) {
|
|
30
|
+
items = this.read_array_pair_list(shortForm);
|
|
31
|
+
}
|
|
32
|
+
this.expect(expect);
|
|
33
|
+
this.next();
|
|
34
|
+
return result(shortForm, items);
|
|
35
|
+
},
|
|
36
|
+
/*
|
|
37
|
+
* Reads an array of items
|
|
38
|
+
* ```ebnf
|
|
39
|
+
* array_pair_list ::= array_pair (',' array_pair?)*
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
read_array_pair_list(shortForm) {
|
|
43
|
+
const self = this;
|
|
44
|
+
return this.read_list(
|
|
45
|
+
function () {
|
|
46
|
+
return self.read_array_pair(shortForm);
|
|
47
|
+
},
|
|
48
|
+
",",
|
|
49
|
+
true,
|
|
50
|
+
);
|
|
51
|
+
},
|
|
52
|
+
/*
|
|
53
|
+
* Reads an entry
|
|
54
|
+
* array_pair:
|
|
55
|
+
* expr T_DOUBLE_ARROW expr
|
|
56
|
+
* | expr
|
|
57
|
+
* | expr T_DOUBLE_ARROW '&' variable
|
|
58
|
+
* | '&' variable
|
|
59
|
+
* | expr T_DOUBLE_ARROW T_LIST '(' array_pair_list ')'
|
|
60
|
+
* | T_LIST '(' array_pair_list ')'
|
|
61
|
+
*/
|
|
62
|
+
read_array_pair(shortForm) {
|
|
63
|
+
if (
|
|
64
|
+
(!shortForm && this.token === ")") ||
|
|
65
|
+
(shortForm && this.token === "]")
|
|
66
|
+
) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (this.token === ",") {
|
|
71
|
+
return this.node("noop")();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const entry = this.node("entry");
|
|
75
|
+
|
|
76
|
+
let key = null;
|
|
77
|
+
let value = null;
|
|
78
|
+
let byRef = false;
|
|
79
|
+
let unpack = false;
|
|
80
|
+
|
|
81
|
+
if (this.token === "&") {
|
|
82
|
+
this.next();
|
|
83
|
+
byRef = true;
|
|
84
|
+
value = this.read_variable(true, false);
|
|
85
|
+
} else if (this.token === this.tok.T_ELLIPSIS && this.version >= 704) {
|
|
86
|
+
this.next();
|
|
87
|
+
if (this.token === "&") {
|
|
88
|
+
this.error();
|
|
89
|
+
}
|
|
90
|
+
unpack = true;
|
|
91
|
+
value = this.read_expr();
|
|
92
|
+
} else {
|
|
93
|
+
const expr = this.read_expr();
|
|
94
|
+
|
|
95
|
+
if (this.token === this.tok.T_DOUBLE_ARROW) {
|
|
96
|
+
this.next();
|
|
97
|
+
key = expr;
|
|
98
|
+
|
|
99
|
+
if (this.token === "&") {
|
|
100
|
+
this.next();
|
|
101
|
+
byRef = true;
|
|
102
|
+
value = this.read_variable(true, false);
|
|
103
|
+
} else {
|
|
104
|
+
value = this.read_expr();
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
value = expr;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return entry(key, value, byRef, unpack);
|
|
112
|
+
},
|
|
113
|
+
};
|