@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
@@ -0,0 +1,507 @@
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
+ * checks if current token is a reference keyword
11
+ */
12
+ is_reference() {
13
+ if (this.token === "&") {
14
+ this.next();
15
+ return true;
16
+ }
17
+ return false;
18
+ },
19
+ /*
20
+ * checks if current token is a variadic keyword
21
+ */
22
+ is_variadic() {
23
+ if (this.token === this.tok.T_ELLIPSIS) {
24
+ this.next();
25
+ return true;
26
+ }
27
+ return false;
28
+ },
29
+ /*
30
+ * reading a function
31
+ * ```ebnf
32
+ * function ::= function_declaration code_block
33
+ * ```
34
+ */
35
+ read_function(closure, flag, attrs, locStart) {
36
+ const result = this.read_function_declaration(
37
+ closure ? 1 : flag ? 2 : 0,
38
+ flag && flag[1] === 1,
39
+ attrs || [],
40
+ locStart,
41
+ );
42
+ if (flag && flag[2] == 1) {
43
+ // abstract function :
44
+ result.parseFlags(flag);
45
+ if (this.expect(";")) {
46
+ this.next();
47
+ }
48
+ } else {
49
+ if (this.expect("{")) {
50
+ result.body = this.read_code_block(false);
51
+ if (result.loc && result.body.loc) {
52
+ result.loc.end = result.body.loc.end;
53
+ }
54
+ }
55
+ if (!closure && flag) {
56
+ result.parseFlags(flag);
57
+ }
58
+ }
59
+ return result;
60
+ },
61
+ /*
62
+ * reads a function declaration (without his body)
63
+ * ```ebnf
64
+ * function_declaration ::= T_FUNCTION '&'? T_STRING '(' parameter_list ')'
65
+ * ```
66
+ */
67
+ read_function_declaration(type, isStatic, attrs, locStart) {
68
+ let nodeName = "function";
69
+ if (type === 1) {
70
+ nodeName = "closure";
71
+ } else if (type === 2) {
72
+ nodeName = "method";
73
+ }
74
+ const result = this.node(nodeName);
75
+
76
+ if (this.expect(this.tok.T_FUNCTION)) {
77
+ this.next();
78
+ }
79
+ const isRef = this.is_reference();
80
+ let name = false,
81
+ use = [],
82
+ returnType = null,
83
+ nullable = false;
84
+ if (type !== 1) {
85
+ const nameNode = this.node("identifier");
86
+ if (type === 2) {
87
+ if (this.version >= 700) {
88
+ if (this.token === this.tok.T_STRING || this.is("IDENTIFIER")) {
89
+ name = this.text();
90
+ this.next();
91
+ } else if (this.version < 704) {
92
+ this.error("IDENTIFIER");
93
+ }
94
+ } else if (this.token === this.tok.T_STRING) {
95
+ name = this.text();
96
+ this.next();
97
+ } else {
98
+ this.error("IDENTIFIER");
99
+ }
100
+ } else {
101
+ if (this.version >= 700) {
102
+ if (this.token === this.tok.T_STRING) {
103
+ name = this.text();
104
+ this.next();
105
+ } else if (this.version >= 704) {
106
+ if (!this.expect("(")) {
107
+ this.next();
108
+ }
109
+ } else {
110
+ this.error(this.tok.T_STRING);
111
+ this.next();
112
+ }
113
+ } else {
114
+ if (this.expect(this.tok.T_STRING)) {
115
+ name = this.text();
116
+ }
117
+ this.next();
118
+ }
119
+ }
120
+ name = nameNode(name);
121
+ }
122
+ if (this.expect("(")) this.next();
123
+ const params = this.read_parameter_list(name.name === "__construct");
124
+ if (this.expect(")")) this.next();
125
+ if (type === 1) {
126
+ use = this.read_lexical_vars();
127
+ }
128
+ if (this.token === ":") {
129
+ if (this.next().token === "?") {
130
+ nullable = true;
131
+ this.next();
132
+ }
133
+ returnType = this.read_types();
134
+ }
135
+ const apply_attrgroup_location = (node) => {
136
+ node.attrGroups = attrs || [];
137
+
138
+ if (locStart && node.loc) {
139
+ node.loc.start = locStart;
140
+ if (node.loc.source) {
141
+ node.loc.source = this.lexer._input.substr(
142
+ node.loc.start.offset,
143
+ node.loc.end.offset - node.loc.start.offset,
144
+ );
145
+ }
146
+ }
147
+ return node;
148
+ };
149
+
150
+ if (type === 1) {
151
+ // closure
152
+ return apply_attrgroup_location(
153
+ result(params, isRef, use, returnType, nullable, isStatic),
154
+ );
155
+ }
156
+ return apply_attrgroup_location(
157
+ result(name, params, isRef, returnType, nullable),
158
+ );
159
+ },
160
+
161
+ read_lexical_vars() {
162
+ let result = [];
163
+
164
+ if (this.token === this.tok.T_USE) {
165
+ this.next();
166
+ this.expect("(") && this.next();
167
+ result = this.read_lexical_var_list();
168
+ this.expect(")") && this.next();
169
+ }
170
+
171
+ return result;
172
+ },
173
+
174
+ read_list_with_dangling_comma(item) {
175
+ const result = [];
176
+
177
+ while (this.token != this.EOF) {
178
+ result.push(item());
179
+ if (this.token == ",") {
180
+ this.next();
181
+ if (this.version >= 800 && this.token === ")") {
182
+ return result;
183
+ }
184
+ } else if (this.token == ")") {
185
+ break;
186
+ } else {
187
+ this.error([",", ")"]);
188
+ break;
189
+ }
190
+ }
191
+ return result;
192
+ },
193
+
194
+ read_lexical_var_list() {
195
+ return this.read_list_with_dangling_comma(this.read_lexical_var.bind(this));
196
+ },
197
+
198
+ /*
199
+ * ```ebnf
200
+ * lexical_var ::= '&'? T_VARIABLE
201
+ * ```
202
+ */
203
+ read_lexical_var() {
204
+ if (this.token === "&") {
205
+ return this.read_byref(this.read_lexical_var.bind(this));
206
+ }
207
+ const result = this.node("variable");
208
+ this.expect(this.tok.T_VARIABLE);
209
+ const name = this.text().substring(1);
210
+ this.next();
211
+ return result(name, false);
212
+ },
213
+ /*
214
+ * reads a list of parameters
215
+ * ```ebnf
216
+ * parameter_list ::= (parameter ',')* parameter?
217
+ * ```
218
+ */
219
+ read_parameter_list(is_class_constructor) {
220
+ if (this.token !== ")") {
221
+ let wasVariadic = false;
222
+
223
+ return this.read_list_with_dangling_comma(
224
+ function () {
225
+ const parameter = this.read_parameter(is_class_constructor);
226
+ if (parameter) {
227
+ // variadic parameters can only be defined at the end of the parameter list
228
+ if (wasVariadic) {
229
+ this.raiseError(
230
+ "Unexpected parameter after a variadic parameter",
231
+ );
232
+ }
233
+ if (parameter.variadic) {
234
+ wasVariadic = true;
235
+ }
236
+ }
237
+ return parameter;
238
+ }.bind(this),
239
+ ",",
240
+ );
241
+ }
242
+
243
+ return [];
244
+ },
245
+ /*
246
+ * ```ebnf
247
+ * parameter ::= type? '&'? T_ELLIPSIS? T_VARIABLE ('=' expr)?
248
+ * ```
249
+ * @see https://github.com/php/php-src/blob/493524454d66adde84e00d249d607ecd540de99f/Zend/zend_language_parser.y#L640
250
+ */
251
+ read_parameter(is_class_constructor) {
252
+ const node = this.node("parameter");
253
+ let parameterName = null;
254
+ let value = null;
255
+ let types = null;
256
+ let nullable = false;
257
+ let readonly = false;
258
+ let attrs = [];
259
+ if (this.token === this.tok.T_ATTRIBUTE) attrs = this.read_attr_list();
260
+
261
+ if (this.version >= 801 && this.token === this.tok.T_READ_ONLY) {
262
+ if (is_class_constructor) {
263
+ this.next();
264
+ readonly = true;
265
+ } else {
266
+ this.raiseError(
267
+ "readonly properties can be used only on class constructor",
268
+ );
269
+ }
270
+ }
271
+
272
+ const flags = this.read_promoted();
273
+
274
+ if (
275
+ !readonly &&
276
+ this.version >= 801 &&
277
+ this.token === this.tok.T_READ_ONLY
278
+ ) {
279
+ if (is_class_constructor) {
280
+ this.next();
281
+ readonly = true;
282
+ } else {
283
+ this.raiseError(
284
+ "readonly properties can be used only on class constructor",
285
+ );
286
+ }
287
+ }
288
+
289
+ if (this.token === "?") {
290
+ this.next();
291
+ nullable = true;
292
+ }
293
+ types = this.read_types();
294
+ if (nullable && !types) {
295
+ this.raiseError(
296
+ "Expecting a type definition combined with nullable operator",
297
+ );
298
+ }
299
+ const isRef = this.is_reference();
300
+ const isVariadic = this.is_variadic();
301
+ if (this.expect(this.tok.T_VARIABLE)) {
302
+ parameterName = this.node("identifier");
303
+ const name = this.text().substring(1);
304
+ this.next();
305
+ parameterName = parameterName(name);
306
+ }
307
+ if (this.token == "=") {
308
+ value = this.next().read_expr();
309
+ }
310
+ const result = node(
311
+ parameterName,
312
+ types,
313
+ value,
314
+ isRef,
315
+ isVariadic,
316
+ readonly,
317
+ nullable,
318
+ flags,
319
+ );
320
+ if (attrs) result.attrGroups = attrs;
321
+ return result;
322
+ },
323
+ read_types() {
324
+ const MODE_UNSET = "unset";
325
+ const MODE_UNION = "union";
326
+ const MODE_INTERSECTION = "intersection";
327
+
328
+ const types = [];
329
+ let mode = MODE_UNSET;
330
+ const type = this.read_type();
331
+ if (!type) return null;
332
+
333
+ // we have matched a single type
334
+ types.push(type);
335
+
336
+ // is the current token a:
337
+ // - | for union type
338
+ // - & for intersection type (> php 8.1)
339
+ while (this.token === "|" || (this.version >= 801 && this.token === "&")) {
340
+ const nextToken = this.peek();
341
+
342
+ if (
343
+ nextToken === this.tok.T_ELLIPSIS ||
344
+ nextToken === this.tok.T_VARIABLE
345
+ ) {
346
+ // the next token is part of the variable (or the variable itself),
347
+ // we're not gonna match anymore types
348
+ break;
349
+ }
350
+
351
+ if (mode === MODE_UNSET) {
352
+ // are we in union or intersection "mode"
353
+ mode = this.token === "|" ? MODE_UNION : MODE_INTERSECTION;
354
+ } else {
355
+ // it is not possible to mix "modes"
356
+ if (
357
+ (mode === MODE_UNION && this.token !== "|") ||
358
+ (mode === MODE_INTERSECTION && this.token !== "&")
359
+ ) {
360
+ this.raiseError(
361
+ 'Unexpect token "' + this.token + '", "|" and "&" can not be mixed',
362
+ );
363
+ }
364
+ }
365
+
366
+ this.next();
367
+ types.push(this.read_type());
368
+ }
369
+ if (types.length === 1) {
370
+ return types[0];
371
+ } else {
372
+ return mode === MODE_INTERSECTION
373
+ ? this.node("intersectiontype")(types)
374
+ : this.node("uniontype")(types);
375
+ }
376
+ },
377
+ read_promoted() {
378
+ const MODIFIER_PUBLIC = 1;
379
+ const MODIFIER_PROTECTED = 2;
380
+ const MODIFIER_PRIVATE = 4;
381
+ if (this.token === this.tok.T_PUBLIC) {
382
+ this.next();
383
+ return MODIFIER_PUBLIC;
384
+ } else if (this.token === this.tok.T_PROTECTED) {
385
+ this.next();
386
+ return MODIFIER_PROTECTED;
387
+ } else if (this.token === this.tok.T_PRIVATE) {
388
+ this.next();
389
+ return MODIFIER_PRIVATE;
390
+ }
391
+ return 0;
392
+ },
393
+ /*
394
+ * Reads a list of arguments
395
+ * ```ebnf
396
+ * function_argument_list ::= '(' (argument_list (',' argument_list)*)? ')'
397
+ * ```
398
+ */
399
+ read_argument_list() {
400
+ let result = [];
401
+ this.expect("(") && this.next();
402
+ if (
403
+ this.version >= 801 &&
404
+ this.token === this.tok.T_ELLIPSIS &&
405
+ this.peek() === ")"
406
+ ) {
407
+ result.push(this.node("variadicplaceholder")());
408
+ this.next();
409
+ } else if (this.token !== ")") {
410
+ result = this.read_non_empty_argument_list();
411
+ }
412
+ this.expect(")") && this.next();
413
+ return result;
414
+ },
415
+ /*
416
+ * Reads non empty argument list
417
+ */
418
+ read_non_empty_argument_list() {
419
+ let wasVariadic = false;
420
+
421
+ return this.read_function_list(
422
+ function () {
423
+ const argument = this.read_argument();
424
+ if (argument) {
425
+ const isVariadic = argument.kind === "variadic";
426
+ // variadic arguments can only be followed by other variadic arguments
427
+ if (wasVariadic && !isVariadic) {
428
+ this.raiseError(
429
+ "Unexpected non-variadic argument after a variadic argument",
430
+ );
431
+ }
432
+ if (isVariadic) {
433
+ wasVariadic = true;
434
+ }
435
+ }
436
+ return argument;
437
+ }.bind(this),
438
+ ",",
439
+ );
440
+ },
441
+ /*
442
+ * ```ebnf
443
+ * argument_list ::= T_STRING ':' expr | T_ELLIPSIS? expr
444
+ * ```
445
+ */
446
+ read_argument() {
447
+ if (this.token === this.tok.T_ELLIPSIS) {
448
+ return this.node("variadic")(this.next().read_expr());
449
+ }
450
+ if (
451
+ this.token === this.tok.T_STRING ||
452
+ Object.values(this.lexer.keywords).includes(this.token)
453
+ ) {
454
+ const nextToken = this.peek();
455
+ if (nextToken === ":") {
456
+ if (this.version < 800) {
457
+ this.raiseError("PHP 8+ is required to use named arguments");
458
+ }
459
+ return this.node("namedargument")(
460
+ this.text(),
461
+ this.next().next().read_expr(),
462
+ );
463
+ }
464
+ }
465
+ return this.read_expr();
466
+ },
467
+ /*
468
+ * read type hinting
469
+ * ```ebnf
470
+ * type ::= T_ARRAY | T_CALLABLE | namespace_name
471
+ * ```
472
+ */
473
+ read_type() {
474
+ const result = this.node();
475
+ if (this.token === this.tok.T_ARRAY || this.token === this.tok.T_CALLABLE) {
476
+ const type = this.text();
477
+ this.next();
478
+ return result("typereference", type.toLowerCase(), type);
479
+ } else if (
480
+ this.token === this.tok.T_NAME_RELATIVE ||
481
+ this.token === this.tok.T_NAME_QUALIFIED ||
482
+ this.token === this.tok.T_NAME_FULLY_QUALIFIED ||
483
+ this.token === this.tok.T_STRING ||
484
+ this.token === this.tok.T_STATIC
485
+ ) {
486
+ const type = this.text();
487
+ const backup = [this.token, this.lexer.getState()];
488
+ this.next();
489
+ if (
490
+ this.token !== this.tok.T_NS_SEPARATOR &&
491
+ this.ast.typereference.types.indexOf(type.toLowerCase()) > -1
492
+ ) {
493
+ return result("typereference", type.toLowerCase(), type);
494
+ } else {
495
+ // rollback a classic namespace
496
+ this.lexer.tokens.push(backup);
497
+ this.next();
498
+ // fix : destroy not consumed node (release comments)
499
+ result.destroy();
500
+ return this.read_namespace_name();
501
+ }
502
+ }
503
+ // fix : destroy not consumed node (release comments)
504
+ result.destroy();
505
+ return null;
506
+ },
507
+ };
@@ -0,0 +1,94 @@
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
+ * Reads an IF statement
11
+ *
12
+ * ```ebnf
13
+ * if ::= T_IF '(' expr ')' ':' ...
14
+ * ```
15
+ */
16
+ read_if() {
17
+ const result = this.node("if");
18
+ const test = this.next().read_if_expr();
19
+ let body = null;
20
+ let alternate = null;
21
+ let shortForm = false;
22
+
23
+ if (this.token === ":") {
24
+ shortForm = true;
25
+ this.next();
26
+ body = this.node("block");
27
+ const items = [];
28
+ while (this.token !== this.EOF && this.token !== this.tok.T_ENDIF) {
29
+ if (this.token === this.tok.T_ELSEIF) {
30
+ alternate = this.read_elseif_short();
31
+ break;
32
+ } else if (this.token === this.tok.T_ELSE) {
33
+ alternate = this.read_else_short();
34
+ break;
35
+ }
36
+ items.push(this.read_inner_statement());
37
+ }
38
+ body = body(null, items);
39
+ this.expect(this.tok.T_ENDIF) && this.next();
40
+ this.expectEndOfStatement();
41
+ } else {
42
+ body = this.read_statement();
43
+ if (this.token === this.tok.T_ELSEIF) {
44
+ alternate = this.read_if();
45
+ } else if (this.token === this.tok.T_ELSE) {
46
+ alternate = this.next().read_statement();
47
+ }
48
+ }
49
+ return result(test, body, alternate, shortForm);
50
+ },
51
+ /*
52
+ * reads an if expression : '(' expr ')'
53
+ */
54
+ read_if_expr() {
55
+ this.expect("(") && this.next();
56
+ const result = this.read_expr();
57
+ this.expect(")") && this.next();
58
+ return result;
59
+ },
60
+ /*
61
+ * reads an elseif (expr): statements
62
+ */
63
+ read_elseif_short() {
64
+ let alternate = null;
65
+ const result = this.node("if");
66
+ const test = this.next().read_if_expr();
67
+ if (this.expect(":")) this.next();
68
+ const body = this.node("block");
69
+ const items = [];
70
+ while (this.token != this.EOF && this.token !== this.tok.T_ENDIF) {
71
+ if (this.token === this.tok.T_ELSEIF) {
72
+ alternate = this.read_elseif_short();
73
+ break;
74
+ } else if (this.token === this.tok.T_ELSE) {
75
+ alternate = this.read_else_short();
76
+ break;
77
+ }
78
+ items.push(this.read_inner_statement());
79
+ }
80
+ return result(test, body(null, items), alternate, true);
81
+ },
82
+ /*
83
+ *
84
+ */
85
+ read_else_short() {
86
+ if (this.next().expect(":")) this.next();
87
+ const body = this.node("block");
88
+ const items = [];
89
+ while (this.token != this.EOF && this.token !== this.tok.T_ENDIF) {
90
+ items.push(this.read_inner_statement());
91
+ }
92
+ return body(null, items);
93
+ },
94
+ };