11<?php namespace lang \ast \emit ;
22
3- use lang \ast \nodes \{CallableExpression , CallableNewExpression , Variable };
3+ use lang \ast \nodes \{CallableExpression , CallableNewExpression , Literal , Variable };
44
55/**
66 * Emulates pipelines / the pipe operator, including a null-safe version.
77 *
8+ * ```php
9+ * // Enclose expressions as follows:
10+ * $in |> $expr;
11+ * ($expr)($in);
12+ *
13+ * // Optimize for string literals:
14+ * $in |> 'strlen';
15+ * strlen($in);
16+ *
17+ * // Optimize for first-class callables:
18+ * $in |> strlen(...);
19+ * strlen($in);
20+ * ```
21+ *
822 * @see https://wiki.php.net/rfc/pipe-operator-v3
923 * @see https://externals.io/message/107661#107670
24+ * @test lang.ast.unittest.emit.EmulatePipelinesTest
1025 * @test lang.ast.unittest.emit.PipelinesTest
1126 */
1227trait EmulatePipelines {
1328
1429 protected function emitPipeTarget ($ result , $ target , $ arg ) {
1530 if ($ target instanceof CallableNewExpression) {
16- $ target ->type ->arguments = [new Variable ( substr ( $ arg, 1 )) ];
31+ $ target ->type ->arguments = [$ arg ];
1732 $ this ->emitOne ($ result , $ target ->type );
1833 $ target ->type ->arguments = null ;
1934 } else if ($ target instanceof CallableExpression) {
2035 $ this ->emitOne ($ result , $ target ->expression );
21- $ result ->out ->write ('( ' .$ arg .') ' );
36+ $ result ->out ->write ('( ' );
37+ $ this ->emitOne ($ result , $ arg );
38+ $ result ->out ->write (') ' );
39+ } else if ($ target instanceof Literal) {
40+ $ result ->out ->write (trim ($ target ->expression , '" \'' ));
41+ $ result ->out ->write ('( ' );
42+ $ this ->emitOne ($ result , $ arg );
43+ $ result ->out ->write (') ' );
2244 } else {
2345 $ result ->out ->write ('( ' );
2446 $ this ->emitOne ($ result , $ target );
25- $ result ->out ->write (')( ' .$ arg .') ' );
47+ $ result ->out ->write (')( ' );
48+ $ this ->emitOne ($ result , $ arg );
49+ $ result ->out ->write (') ' );
2650 }
2751 }
2852
2953 protected function emitPipe ($ result , $ pipe ) {
3054
31- // $expr |> strtoupper(...) => [$arg= $expr, strtoupper($arg)][1]
32- $ t = $ result ->temp ();
33- $ result ->out ->write ('[ ' .$ t .'= ' );
34- $ this ->emitOne ($ result , $ pipe ->expression );
35- $ result ->out ->write (', ' );
36- $ this ->emitPipeTarget ($ result , $ pipe ->target , $ t );
37- $ result ->out ->write ('][1] ' );
55+ // <const> |> strtoupper(...) => strtoupper(<const>)
56+ // <expr> |> strtoupper(...) => [$arg= <expr>, strtoupper($arg)][1]
57+ if ($ this ->isConstant ($ result , $ pipe ->expression )) {
58+ $ this ->emitPipeTarget ($ result , $ pipe ->target , $ pipe ->expression );
59+ } else {
60+ $ t = $ result ->temp ();
61+ $ result ->out ->write ('[ ' .$ t .'= ' );
62+ $ this ->emitOne ($ result , $ pipe ->expression );
63+ $ result ->out ->write (', ' );
64+ $ this ->emitPipeTarget ($ result , $ pipe ->target , new Variable (substr ($ t , 1 )));
65+ $ result ->out ->write ('][1] ' );
66+ }
3867 }
3968
4069 protected function emitNullsafePipe ($ result , $ pipe ) {
4170
42- // $ expr ?|> strtoupper(...) => null === ($arg= $ expr) ? null : strtoupper($arg)
71+ // < expr> ?|> strtoupper(...) => null === ($arg= < expr> ) ? null : strtoupper($arg)
4372 $ t = $ result ->temp ();
4473 $ result ->out ->write ('null===( ' .$ t .'= ' );
4574 $ this ->emitOne ($ result , $ pipe ->expression );
4675 $ result ->out ->write (')?null: ' );
47- $ this ->emitPipeTarget ($ result , $ pipe ->target , $ t );
76+ $ this ->emitPipeTarget ($ result , $ pipe ->target , new Variable ( substr ( $ t , 1 )) );
4877 }
4978}
0 commit comments