11<?php namespace lang \ast \emit ;
22
3- use lang \ast \nodes \{CallableExpression , CallableNewExpression , Variable , Placeholder };
3+ use lang \ast \nodes \{CallableExpression , CallableNewExpression , Literal , Placeholder , Variable };
44
55/**
66 * Emulates pipelines / the pipe operator, including a null-safe version.
1010 * $in |> $expr;
1111 * ($expr)($in);
1212 *
13+ * // Optimize for string literals:
14+ * $in |> 'strlen';
15+ * strlen($in);
16+ *
1317 * // Optimize for first-class callables with single placeholder argument:
1418 * $in |> strlen(...);
1519 * strlen($in);
20+ *
21+ * // Optimize for partial functions with single placeholder argument:
22+ * $in |> str_replace('test', 'ok', ?);
23+ * strlen('test', 'ok', $in);
1624 * ```
1725 *
1826 * @see https://wiki.php.net/rfc/pipe-operator-v3
1927 * @see https://externals.io/message/107661#107670
28+ * @test lang.ast.unittest.emit.EmulatePipelinesTest
2029 * @test lang.ast.unittest.emit.PipelinesTest
2130 */
2231trait EmulatePipelines {
2332
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 ;
2645 }
2746
2847 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 ;
3150 $ this ->emitOne ($ result , $ target ->type );
3251 $ 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 ) )) {
3453 $ 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 (') ' );
3662 } else {
3763 $ result ->out ->write ('( ' );
3864 $ this ->emitOne ($ result , $ target );
39- $ result ->out ->write (')( ' .$ arg .') ' );
65+ $ result ->out ->write (')( ' );
66+ $ this ->emitOne ($ result , $ arg );
67+ $ result ->out ->write (') ' );
4068 }
4169 }
4270
4371 protected function emitPipe ($ result , $ pipe ) {
4472
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+ }
5285 }
5386
5487 protected function emitNullsafePipe ($ result , $ pipe ) {
5588
56- // $ expr ?|> strtoupper(...) => null === ($arg= $ expr) ? null : strtoupper($arg)
89+ // < expr> ?|> strtoupper(...) => null === ($arg= < expr> ) ? null : strtoupper($arg)
5790 $ t = $ result ->temp ();
5891 $ result ->out ->write ('null===( ' .$ t .'= ' );
5992 $ this ->emitOne ($ result , $ pipe ->expression );
6093 $ result ->out ->write (')?null: ' );
61- $ this ->emitPipeTarget ($ result , $ pipe ->target , $ t );
94+ $ this ->emitPipeTarget ($ result , $ pipe ->target , new Variable ( substr ( $ t , 1 )) );
6295 }
6396}
0 commit comments