11<?php namespace lang \ast \unittest \emit ;
22
3- use test \{Assert , Test , Values };
3+ use lang \Error ;
4+ use test \verify \Runtime ;
5+ use test \{Assert , Expect , Test , Values };
46
57/** @see https://wiki.php.net/rfc/pipe-operator-v3 */
68class PipelinesTest extends EmittingTest {
@@ -98,6 +100,24 @@ public function run() {
98100 Assert::equals ('test: OK ' , $ r );
99101 }
100102
103+ #[Test, Expect(Error::class)]
104+ public function pipe_to_throw () {
105+ $ this ->run ('use lang\Error; class %T {
106+ public function run() {
107+ return "test" |> throw new Error("Test");
108+ }
109+ } ' );
110+ }
111+
112+ #[Test, Expect(Error::class)]
113+ public function pipe_to_missing () {
114+ $ this ->run ('class %T {
115+ public function run() {
116+ return "test" |> "__missing";
117+ }
118+ } ' );
119+ }
120+
101121 #[Test]
102122 public function pipe_chain () {
103123 $ r = $ this ->run ('class %T {
@@ -109,7 +129,7 @@ public function run() {
109129 Assert::equals ('TEST ' , $ r );
110130 }
111131
112- #[Test, Values([[['test ' ], 'TEST ' ], [[], null ]])]
132+ #[Test, Values([[['test ' ], 'TEST ' ], [['' ], '' ], [[ ], null ]])]
113133 public function nullsafe_pipe ($ input , $ expected ) {
114134 $ r = $ this ->run ('class %T {
115135 public function run($arg) {
@@ -120,7 +140,7 @@ public function run($arg) {
120140 Assert::equals ($ expected , $ r );
121141 }
122142
123- #[Test, Values([[null , null ], ['test ' , 'TEST ' ], [' test ' , 'TEST ' ]])]
143+ #[Test, Values([[null , null ], ['' , '' ], [ ' test ' , 'TEST ' ], [' test ' , 'TEST ' ]])]
124144 public function nullsafe_chain ($ input , $ expected ) {
125145 $ r = $ this ->run ('class %T {
126146 public function run($arg) {
@@ -132,7 +152,7 @@ public function run($arg) {
132152 }
133153
134154 #[Test]
135- public function precedence () {
155+ public function concat_precedence () {
136156 $ r = $ this ->run ('class %T {
137157 public function run() {
138158 return "te"."st" |> strtoupper(...);
@@ -142,6 +162,54 @@ public function run() {
142162 Assert::equals ('TEST ' , $ r );
143163 }
144164
165+ #[Test]
166+ public function addition_precedence () {
167+ $ r = $ this ->run ('class %T {
168+ public function run() {
169+ return 5 + 2 |> fn($i) => $i * 2;
170+ }
171+ } ' );
172+
173+ Assert::equals (14 , $ r );
174+ }
175+
176+ #[Test]
177+ public function comparison_precedence () {
178+ $ r = $ this ->run ('class %T {
179+ public function run() {
180+ return 5 |> fn($i) => $i * 2 === 10;
181+ }
182+ } ' );
183+
184+ Assert::true ($ r );
185+ }
186+
187+ #[Test, Values([[0 , 'even ' ], [1 , 'odd ' ], [2 , 'even ' ]])]
188+ public function ternary_precedence ($ arg , $ expected ) {
189+ $ r = $ this ->run ('class %T {
190+ public function run($arg) {
191+ return $arg |> fn($i) => $i % 2 ? "odd" : "even";
192+ }
193+ } ' , $ arg );
194+
195+ Assert::equals ($ expected , $ r );
196+ }
197+
198+ #[Test, Values([[0 , 'root ' ], [1001 , 'test ' ], [1002 , '#unknown ' ]])]
199+ public function coalesce_precedence ($ arg , $ expected ) {
200+ $ r = $ this ->run ('class %T {
201+ private $users= [0 => "root", 1001 => "test"];
202+
203+ private function user($id) { return $this->users[$id] ?? null; }
204+
205+ public function run($arg) {
206+ return $arg |> $this->user(...) ?? "#unknown";
207+ }
208+ } ' , $ arg );
209+
210+ Assert::equals ($ expected , $ r );
211+ }
212+
145213 #[Test]
146214 public function rfc_example () {
147215 $ r = $ this ->run ('class %T {
@@ -156,4 +224,94 @@ public function run() {
156224 } ' );
157225 Assert::equals (['H ' , 'E ' , 'L ' , 'L ' , ' ' , 'W ' , 'R ' , 'L ' , 'D ' ], array_values ($ r ));
158226 }
227+
228+ #[Test, Expect(Error::class), Runtime(php: '>=8.5.0 ' )]
229+ public function rejects_by_reference_functions () {
230+ $ this ->run ('class %T {
231+ private function modify(&$arg) { $arg++; }
232+
233+ public function run() {
234+ $val= 1;
235+ return $val |> $this->modify(...);
236+ }
237+ } ' );
238+ }
239+
240+ #[Test]
241+ public function accepts_prefer_by_reference_functions () {
242+ $ r = $ this ->run ('class %T {
243+ public function run() {
244+ return ["hello", "world"] |> array_multisort(...);
245+ }
246+ } ' );
247+
248+ Assert::true ($ r );
249+ }
250+
251+ #[Test]
252+ public function execution_order () {
253+ $ r = $ this ->run ('class %T {
254+ public function run() {
255+ $invoked= [];
256+
257+ $first= function() use(&$invoked) { $invoked[]= "first"; return 1; };
258+ $second= function() use(&$invoked) { $invoked[]= "second"; return false; };
259+ $skipped= function() use(&$invoked) { $invoked[]= "skipped"; return $in; };
260+ $third= function($in) use(&$invoked) { $invoked[]= "third"; return $in; };
261+ $capture= function($result) use(&$invoked) { $invoked[]= $result; };
262+
263+ $first() |> ($second() ? $skipped : $third) |> $capture;
264+ return $invoked;
265+ }
266+ } ' );
267+
268+ Assert::equals (['first ' , 'second ' , 'third ' , 1 ], $ r );
269+ }
270+
271+ #[Test]
272+ public function interrupted_by_exception () {
273+ $ r = $ this ->run ('use lang\Error; class %T {
274+ public function run() {
275+ $invoked= [];
276+
277+ $provide= function() use(&$invoked) { $invoked[]= "provide"; return 1; };
278+ $transform= function($in) use(&$invoked) { $invoked[]= "transform"; return $in * 2; };
279+ $throw= function() use(&$invoked) { $invoked[]= "throw"; throw new Error("Break"); };
280+
281+ try {
282+ $provide() |> $transform |> $throw |> throw new Error("Unreachable");
283+ } catch (Error $e) {
284+ $invoked[]= $e->compoundMessage();
285+ }
286+ return $invoked;
287+ }
288+ } ' );
289+
290+ Assert::equals (['provide ' , 'transform ' , 'throw ' , 'Exception lang.Error (Break) ' ], $ r );
291+ }
292+
293+ #[Test]
294+ public function generators () {
295+ $ r = $ this ->run ('class %T {
296+ private function range($lo, $hi) {
297+ for ($i= $lo; $i <= $hi; $i++) {
298+ yield $i;
299+ }
300+ }
301+
302+ private function map($fn) {
303+ return function($it) use($fn) {
304+ foreach ($it as $element) {
305+ yield $fn($element);
306+ }
307+ };
308+ }
309+
310+ public function run() {
311+ return $this->range(1, 3) |> $this->map(fn($e) => $e + 1) |> iterator_to_array(...);
312+ }
313+ } ' );
314+
315+ Assert::equals ([2 , 3 , 4 ], $ r );
316+ }
159317}
0 commit comments