22
22
use FFI \Preprocessor \Internal \Lexer ;
23
23
use FFI \Preprocessor \Io \Directory \Repository as DirectoriesRepository ;
24
24
use FFI \Preprocessor \Io \Source \Repository as SourcesRepository ;
25
+ use FFI \Preprocessor \Option ;
25
26
use FFI \Preprocessor \Preprocessor ;
27
+ use JetBrains \PhpStorm \ExpectedValues ;
26
28
use Phplrt \Contracts \Exception \RuntimeExceptionInterface ;
27
29
use Phplrt \Contracts \Lexer \TokenInterface ;
28
30
use Phplrt \Contracts \Source \FileInterface ;
35
37
/**
36
38
* @internal SourceExecutor is an internal library class, please do not use it in your code.
37
39
* @psalm-internal FFI\Preprocessor\Internal
40
+ *
41
+ * @psalm-import-type OptionEnum from Option
38
42
*/
39
43
final class SourceExecutor
40
44
{
@@ -68,12 +72,15 @@ final class SourceExecutor
68
72
* @param DirectoriesRepository $directories
69
73
* @param SourcesRepository $sources
70
74
* @param LoggerInterface $logger
75
+ * @param int-mask-of<OptionEnum> $options
71
76
*/
72
77
public function __construct (
73
78
private DirectivesRepository $ directives ,
74
79
private DirectoriesRepository $ directories ,
75
80
private SourcesRepository $ sources ,
76
81
private LoggerInterface $ logger ,
82
+ #[ExpectedValues(flagsFromClass: Option::class)]
83
+ private int $ options ,
77
84
) {
78
85
$ this ->lexer = new Lexer ();
79
86
$ this ->stack = new OutputStack ();
@@ -98,11 +105,11 @@ public function execute(ReadableInterface $source): \Traversable
98
105
try {
99
106
switch ($ token ->getName ()) {
100
107
case Lexer::T_ERROR :
101
- $ this ->doError ($ token , $ source );
108
+ yield from $ this ->doError ($ token , $ source );
102
109
break ;
103
110
104
111
case Lexer::T_WARNING :
105
- $ this ->doWarning ($ token , $ source );
112
+ yield from $ this ->doWarning ($ token , $ source );
106
113
break ;
107
114
108
115
case Lexer::T_QUOTED_INCLUDE :
@@ -111,39 +118,39 @@ public function execute(ReadableInterface $source): \Traversable
111
118
break ;
112
119
113
120
case Lexer::T_IFDEF :
114
- $ this ->doIfDefined ($ token );
121
+ yield from $ this ->doIfDefined ($ token, $ source );
115
122
break ;
116
123
117
124
case Lexer::T_IFNDEF :
118
- $ this ->doIfNotDefined ($ token );
125
+ yield from $ this ->doIfNotDefined ($ token, $ source );
119
126
break ;
120
127
121
128
case Lexer::T_ENDIF :
122
- $ this ->doEndIf ();
129
+ yield from $ this ->doEndIf ($ token , $ source );
123
130
break ;
124
131
125
132
case Lexer::T_IF :
126
- $ this ->doIf ($ token );
133
+ yield from $ this ->doIf ($ token, $ source );
127
134
break ;
128
135
129
136
case Lexer::T_ELSE_IF :
130
- $ this ->doElseIf ($ token , $ source );
137
+ yield from $ this ->doElseIf ($ token , $ source );
131
138
break ;
132
139
133
140
case Lexer::T_ELSE :
134
- $ this ->doElse ();
141
+ yield from $ this ->doElse ($ token , $ source );
135
142
break ;
136
143
137
144
case Lexer::T_OBJECT_MACRO :
138
- $ this ->doObjectLikeDirective ($ token );
145
+ yield from $ this ->doObjectLikeDirective ($ token, $ source );
139
146
break ;
140
147
141
148
case Lexer::T_FUNCTION_MACRO :
142
- $ this ->doFunctionLikeDirective ($ token );
149
+ yield from $ this ->doFunctionLikeDirective ($ token, $ source );
143
150
break ;
144
151
145
152
case Lexer::T_UNDEF :
146
- $ this ->doRemoveDefine ($ token );
153
+ yield from $ this ->doRemoveDefine ($ token, $ source );
147
154
break ;
148
155
149
156
case Lexer::T_SOURCE :
@@ -163,6 +170,39 @@ public function execute(ReadableInterface $source): \Traversable
163
170
}
164
171
}
165
172
173
+ /**
174
+ * @param ReadableInterface $source
175
+ * @param TokenInterface $token
176
+ * @param array<string> $comments
177
+ * @return iterable<string>
178
+ */
179
+ private function debug (ReadableInterface $ source , TokenInterface $ token , array $ comments = []): iterable
180
+ {
181
+ if (Option::contains ($ this ->options , Option::KEEP_DEBUG_COMMENTS )) {
182
+ $ map = static fn (string $ line ): string => "// $ line \n" ;
183
+ $ line = Position::fromOffset ($ source , $ token ->getOffset ())
184
+ ->getLine ();
185
+ return [
186
+ '// ' . $ this ->sourceToString ($ source ) . ': ' . $ line . "\n" ,
187
+ ...\array_map ($ map , $ comments )
188
+ ];
189
+ }
190
+
191
+ return [];
192
+ }
193
+
194
+ /**
195
+ * @param ReadableInterface $source
196
+ * @return string
197
+ */
198
+ private function sourceToString (ReadableInterface $ source ): string
199
+ {
200
+ return $ source instanceof FileInterface
201
+ ? $ source ->getPathname ()
202
+ : '{ ' . $ source ->getHash () . '} '
203
+ ;
204
+ }
205
+
166
206
/**
167
207
* @param ReadableInterface $source
168
208
* @return string
@@ -177,11 +217,12 @@ private function read(ReadableInterface $source): string
177
217
/**
178
218
* @param Composite $tok
179
219
* @param ReadableInterface $src
220
+ * @return iterable<string>
180
221
*/
181
- private function doError (Composite $ tok , ReadableInterface $ src ): void
222
+ private function doError (Composite $ tok , ReadableInterface $ src ): iterable
182
223
{
183
224
if (! $ this ->stack ->isEnabled ()) {
184
- return ;
225
+ return [] ;
185
226
}
186
227
187
228
$ message = $ this ->escape (\trim ($ tok [0 ]->getValue ()));
@@ -190,6 +231,8 @@ private function doError(Composite $tok, ReadableInterface $src): void
190
231
'position ' => Position::fromOffset ($ tok ->getOffset ()),
191
232
'source ' => $ src ,
192
233
]);
234
+
235
+ return $ this ->debug ($ src , $ tok , ['error ' . $ message ]);
193
236
}
194
237
195
238
/**
@@ -228,11 +271,12 @@ private function escape(string $body): string
228
271
/**
229
272
* @param Composite $tok
230
273
* @param ReadableInterface $src
274
+ * @return iterable<string>
231
275
*/
232
- private function doWarning (Composite $ tok , ReadableInterface $ src ): void
276
+ private function doWarning (Composite $ tok , ReadableInterface $ src ): iterable
233
277
{
234
278
if (! $ this ->stack ->isEnabled ()) {
235
- return ;
279
+ return [] ;
236
280
}
237
281
238
282
$ message = $ this ->escape (\trim ($ tok [0 ]->getValue ()));
@@ -241,6 +285,8 @@ private function doWarning(Composite $tok, ReadableInterface $src): void
241
285
'position ' => Position::fromOffset ($ tok ->getOffset ()),
242
286
'source ' => $ src ,
243
287
]);
288
+
289
+ return $ this ->debug ($ src , $ tok , ['warning ' . $ message ]);
244
290
}
245
291
246
292
/**
@@ -267,6 +313,7 @@ private function doInclude(Composite $token, ReadableInterface $src): iterable
267
313
throw NotReadableException::fromSource ($ e ->getMessage (), $ src , $ token [0 ]);
268
314
}
269
315
316
+ yield from $ this ->debug ($ src , $ token , ['include ' . $ this ->sourceToString ($ inclusion )]);
270
317
yield from $ this ->execute ($ inclusion );
271
318
}
272
319
@@ -306,66 +353,82 @@ private function lookup(ReadableInterface $source, string $file, bool $withLocal
306
353
307
354
/**
308
355
* @param Composite $token
356
+ * @param ReadableInterface $source
357
+ * @return iterable<string>
309
358
*/
310
- private function doIfDefined (Composite $ token ): void
359
+ private function doIfDefined (Composite $ token, ReadableInterface $ source ): iterable
311
360
{
312
361
if (! $ this ->stack ->isEnabled ()) {
313
362
$ this ->stack ->push (false );
314
363
315
- return ;
364
+ return [] ;
316
365
}
317
366
318
367
$ body = $ this ->escape ($ token [0 ]->getValue ());
319
368
320
369
$ defined = $ this ->directives ->defined ($ body );
321
370
322
371
$ this ->stack ->push ($ defined );
372
+
373
+ return $ this ->debug ($ source , $ token , ['if defined ' . $ body ]);
323
374
}
324
375
325
376
/**
326
377
* @param Composite $token
378
+ * @param ReadableInterface $source
379
+ * @return iterable<string>
327
380
*/
328
- private function doIfNotDefined (Composite $ token ): void
381
+ private function doIfNotDefined (Composite $ token, ReadableInterface $ source ): iterable
329
382
{
330
383
if (! $ this ->stack ->isEnabled ()) {
331
384
$ this ->stack ->push (false );
332
385
333
- return ;
386
+ return [] ;
334
387
}
335
388
336
389
$ body = $ this ->escape ($ token [0 ]->getValue ());
337
390
338
391
$ defined = $ this ->directives ->defined ($ body );
339
392
340
393
$ this ->stack ->push (! $ defined );
394
+
395
+ return $ this ->debug ($ source , $ token , ['if not defined ' . $ body ]);
341
396
}
342
397
343
398
/**
344
- * @return void
399
+ * @param TokenInterface $token
400
+ * @param ReadableInterface $source
401
+ * @return iterable<string>
345
402
*/
346
- private function doEndIf (): void
403
+ private function doEndIf (TokenInterface $ token , ReadableInterface $ source ): iterable
347
404
{
348
405
try {
349
406
$ this ->stack ->pop ();
350
407
} catch (\LogicException $ e ) {
351
408
throw new \LogicException ('#endif directive without #if ' );
352
409
}
410
+
411
+ return $ this ->debug ($ source , $ token , ['endif ' ]);
353
412
}
354
413
355
414
/**
356
415
* @param Composite $token
416
+ * @param ReadableInterface $source
417
+ * @return iterable<string>
357
418
* @throws RuntimeExceptionInterface
358
419
* @throws \Throwable
359
420
*/
360
- private function doIf (Composite $ token ): void
421
+ private function doIf (Composite $ token, ReadableInterface $ source ): iterable
361
422
{
362
423
if (! $ this ->stack ->isEnabled ()) {
363
424
$ this ->stack ->push (false );
364
425
365
- return ;
426
+ return [] ;
366
427
}
367
428
368
429
$ this ->stack ->push ($ this ->eval ($ token ));
430
+
431
+ return $ this ->debug ($ source , $ token , ['if ' . $ token [0 ]->getValue ()]);
369
432
}
370
433
371
434
/**
@@ -398,23 +461,29 @@ private function replace(string $body, int $ctx): string
398
461
/**
399
462
* @param Composite $token
400
463
* @param ReadableInterface $source
464
+ * @return iterable<string>
465
+ * @throws RuntimeExceptionInterface
401
466
* @throws \Throwable
402
467
*/
403
- private function doElseIf (Composite $ token , ReadableInterface $ source ): void
468
+ private function doElseIf (Composite $ token , ReadableInterface $ source ): iterable
404
469
{
405
470
if (! $ this ->stack ->isCompleted () && $ this ->eval ($ token )) {
406
471
$ this ->stack ->complete ();
407
472
408
- return ;
473
+ return [] ;
409
474
}
410
475
411
476
$ this ->stack ->update (false , $ this ->stack ->isCompleted ());
477
+
478
+ return $ this ->debug ($ source , $ token , ['else if ' . $ token ->getValue ()]);
412
479
}
413
480
414
481
/**
415
- * @return void
482
+ * @param TokenInterface $token
483
+ * @param ReadableInterface $source
484
+ * @return iterable<string>
416
485
*/
417
- private function doElse (): void
486
+ private function doElse (TokenInterface $ token , ReadableInterface $ source ): iterable
418
487
{
419
488
try {
420
489
if (! $ this ->stack ->isCompleted ()) {
@@ -425,15 +494,19 @@ private function doElse(): void
425
494
} catch (\LogicException $ e ) {
426
495
throw new \LogicException ('#else directive without #if ' );
427
496
}
497
+
498
+ return $ this ->debug ($ source , $ token , ['else ' ]);
428
499
}
429
500
430
501
/**
431
502
* @param Composite $token
503
+ * @param ReadableInterface $source
504
+ * @return iterable<string>
432
505
*/
433
- private function doObjectLikeDirective (Composite $ token ): void
506
+ private function doObjectLikeDirective (Composite $ token, ReadableInterface $ source ): iterable
434
507
{
435
508
if (! $ this ->stack ->isEnabled ()) {
436
- return ;
509
+ return [] ;
437
510
}
438
511
439
512
// Name
@@ -444,15 +517,19 @@ private function doObjectLikeDirective(Composite $token): void
444
517
$ value = $ this ->replace ($ value , DirectiveExecutor::CTX_EXPRESSION );
445
518
446
519
$ this ->directives ->define ($ name , new ObjectLikeDirective ($ value ));
520
+
521
+ return $ this ->debug ($ source , $ token , ['define ' . $ name . ' = ' . ($ value ?: '"" ' )]);
447
522
}
448
523
449
524
/**
450
525
* @param Composite $token
526
+ * @param ReadableInterface $source
527
+ * @return iterable<string>
451
528
*/
452
- private function doFunctionLikeDirective (Composite $ token ): void
529
+ private function doFunctionLikeDirective (Composite $ token, ReadableInterface $ source ): iterable
453
530
{
454
531
if (! $ this ->stack ->isEnabled ()) {
455
- return ;
532
+ return [] ;
456
533
}
457
534
458
535
// Name
@@ -466,22 +543,30 @@ private function doFunctionLikeDirective(Composite $token): void
466
543
$ value = $ this ->replace ($ value , DirectiveExecutor::CTX_EXPRESSION );
467
544
468
545
$ this ->directives ->define ($ name , new FunctionLikeDirective ($ args , $ value ));
546
+
547
+ return $ this ->debug ($ source , $ token , [
548
+ 'define ' . $ name . '( ' . $ token [1 ]->getValue () . ') = ' . ($ value ?: '"" ' )
549
+ ]);
469
550
}
470
551
471
552
/**
472
553
* @param Composite $token
554
+ * @param ReadableInterface $source
555
+ * @return iterable<string>
473
556
*/
474
- private function doRemoveDefine (Composite $ token ): void
557
+ private function doRemoveDefine (Composite $ token, ReadableInterface $ source ): iterable
475
558
{
476
559
if (! $ this ->stack ->isEnabled ()) {
477
- return ;
560
+ return [] ;
478
561
}
479
562
480
563
$ body = $ this ->escape ($ token [0 ]->getValue ());
481
564
482
565
$ name = $ this ->replace ($ body , DirectiveExecutor::CTX_SOURCE );
483
566
484
567
$ this ->directives ->undef ($ name );
568
+
569
+ return $ this ->debug ($ source , $ token , ['undef ' . $ name ]);
485
570
}
486
571
487
572
/**
0 commit comments