1
1
<?php namespace lang \ast \emit ;
2
2
3
- use lang \ast \nodes \{CallableExpression , CallableNewExpression , Variable , Placeholder };
3
+ use lang \ast \nodes \{CallableExpression , CallableNewExpression , Literal , Placeholder , Variable };
4
4
5
5
/**
6
6
* Emulates pipelines / the pipe operator, including a null-safe version.
10
10
* $in |> $expr;
11
11
* ($expr)($in);
12
12
*
13
+ * // Optimize for string literals:
14
+ * $in |> 'strlen';
15
+ * strlen($in);
16
+ *
13
17
* // Optimize for first-class callables with single placeholder argument:
14
18
* $in |> strlen(...);
15
19
* strlen($in);
20
+ *
21
+ * // Optimize for partial functions with single placeholder argument:
22
+ * $in |> str_replace('test', 'ok', ?);
23
+ * strlen('test', 'ok', $in);
16
24
* ```
17
25
*
18
26
* @see https://wiki.php.net/rfc/pipe-operator-v3
19
27
* @see https://externals.io/message/107661#107670
28
+ * @test lang.ast.unittest.emit.EmulatePipelinesTest
20
29
* @test lang.ast.unittest.emit.PipelinesTest
21
30
*/
22
31
trait EmulatePipelines {
23
32
24
- private function singlePlaceholder ($ arguments ) {
25
- return 1 === sizeof ($ arguments ) && $ arguments [0 ] instanceof Placeholder;
33
+ private function passSingle ($ arguments , $ arg ) {
34
+ $ placeholder = -1 ;
35
+ foreach ($ arguments as $ n => $ argument ) {
36
+ if ($ argument instanceof Placeholder) {
37
+ if ($ placeholder > -1 ) return null ;
38
+ $ placeholder = $ n ;
39
+ }
40
+ }
41
+
42
+ $ r = $ arguments ;
43
+ $ r [$ placeholder ]= $ arg ;
44
+ return $ r ;
26
45
}
27
46
28
47
protected function emitPipeTarget ($ result , $ target , $ arg ) {
29
- if ($ target instanceof CallableNewExpression && $ this ->singlePlaceholder ($ target ->arguments )) {
30
- $ target ->type ->arguments = [ new Variable ( substr ( $ arg , 1 ))] ;
48
+ if ($ target instanceof CallableNewExpression && ( $ pass = $ this ->passSingle ($ target ->arguments , $ arg ) )) {
49
+ $ target ->type ->arguments = $ pass ;
31
50
$ this ->emitOne ($ result , $ target ->type );
32
51
$ target ->type ->arguments = null ;
33
- } else if ($ target instanceof CallableExpression && $ this ->singlePlaceholder ($ target ->arguments )) {
52
+ } else if ($ target instanceof CallableExpression && ( $ pass = $ this ->passSingle ($ target ->arguments , $ arg ) )) {
34
53
$ this ->emitOne ($ result , $ target ->expression );
35
- $ result ->out ->write ('( ' .$ arg .') ' );
54
+ $ result ->out ->write ('( ' );
55
+ $ this ->emitArguments ($ result , $ pass );
56
+ $ result ->out ->write (') ' );
57
+ } else if ($ target instanceof Literal) {
58
+ $ result ->out ->write (trim ($ target ->expression , '" \'' ));
59
+ $ result ->out ->write ('( ' );
60
+ $ this ->emitOne ($ result , $ arg );
61
+ $ result ->out ->write (') ' );
36
62
} else {
37
63
$ result ->out ->write ('( ' );
38
64
$ this ->emitOne ($ result , $ target );
39
- $ result ->out ->write (')( ' .$ arg .') ' );
65
+ $ result ->out ->write (')( ' );
66
+ $ this ->emitOne ($ result , $ arg );
67
+ $ result ->out ->write (') ' );
40
68
}
41
69
}
42
70
43
71
protected function emitPipe ($ result , $ pipe ) {
44
72
45
- // $expr |> strtoupper(...) => [$arg= $expr, strtoupper($arg)][1]
46
- $ t = $ result ->temp ();
47
- $ result ->out ->write ('[ ' .$ t .'= ' );
48
- $ this ->emitOne ($ result , $ pipe ->expression );
49
- $ result ->out ->write (', ' );
50
- $ this ->emitPipeTarget ($ result , $ pipe ->target , $ t );
51
- $ result ->out ->write ('][1] ' );
73
+ // <const> |> strtoupper(...) => strtoupper(<const>)
74
+ // <expr> |> strtoupper(...) => [$arg= <expr>, strtoupper($arg)][1]
75
+ if ($ this ->isConstant ($ result , $ pipe ->expression )) {
76
+ $ this ->emitPipeTarget ($ result , $ pipe ->target , $ pipe ->expression );
77
+ } else {
78
+ $ t = $ result ->temp ();
79
+ $ result ->out ->write ('[ ' .$ t .'= ' );
80
+ $ this ->emitOne ($ result , $ pipe ->expression );
81
+ $ result ->out ->write (', ' );
82
+ $ this ->emitPipeTarget ($ result , $ pipe ->target , new Variable (substr ($ t , 1 )));
83
+ $ result ->out ->write ('][1] ' );
84
+ }
52
85
}
53
86
54
87
protected function emitNullsafePipe ($ result , $ pipe ) {
55
88
56
- // $ expr ?|> strtoupper(...) => null === ($arg= $ expr) ? null : strtoupper($arg)
89
+ // < expr> ?|> strtoupper(...) => null === ($arg= < expr> ) ? null : strtoupper($arg)
57
90
$ t = $ result ->temp ();
58
91
$ result ->out ->write ('null===( ' .$ t .'= ' );
59
92
$ this ->emitOne ($ result , $ pipe ->expression );
60
93
$ result ->out ->write (')?null: ' );
61
- $ this ->emitPipeTarget ($ result , $ pipe ->target , $ t );
94
+ $ this ->emitPipeTarget ($ result , $ pipe ->target , new Variable ( substr ( $ t , 1 )) );
62
95
}
63
96
}
0 commit comments