-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Pipe operator #17118
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Crell
wants to merge
38
commits into
php:master
Choose a base branch
from
Crell:func-composition
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+734
−1
Open
Pipe operator #17118
Changes from all commits
Commits
Show all changes
38 commits
Select commit
Hold shift + click to select a range
9585c04
Implement pipe operator.
Crell 529ba11
Include FCC in tested callable formats.
Crell 0988adc
Move the code generation from the lexer to a compile function, courte…
Crell 6612361
Fix pipe error output.
Crell 499b111
Remove dead comment.
Crell b04e422
Wrap pipe LHS in a QM_ASSIGN opcode to implicitly block references.
Crell 4dbf197
Add test to check behaviour of prefer-by-ref parameters
Girgias 97db4bc
Use echo instead.
Crell f07fea2
Don't use printf, either.
Crell 1443311
Correct addition-precedence test so it would be a different output if…
Crell 3b6a9b3
Tweak style.
Crell 46be67d
Expand ternary test.
Crell 77af087
Add a test for comparison operators.
Crell d395181
Update comparison precedence test.
Crell c8e0051
Whitespace fixes.
Crell 4ddf259
Add single quotes for consistency.
Crell cf26b9f
Increase precedence of pipe operator.
Crell c02447c
Remove dead code.
Crell ec94530
Add test for generator and pipe behavior.
Crell f1cb86d
Add regenerated file.
Crell 0d9e407
Add test for complex ordering.
Crell c932837
Add test for exceptions.
Crell d608250
Use BINARY_OP macro for printing Pipe AST.
Crell 241dda7
Improve tests for AST printing, still doesn't work.
Crell 63c7b63
Improved optimzations and namespace handling, courtesy Arnaud.
Crell d7b7a99
Improve docs.
Crell 82bae3c
Ensure higher order functions stil work.
Crell 0d432c6
AST: Update priorities for ZEND_AST_PIPE
arnaud-lb 93ea77b
Remove vestigial comment.
Crell d53cade
Add test for namespaced functions.
Crell 7b13753
Remove dead code
Crell 036cf3c
More namespace tests.
Crell cfa3e3f
Add test for static method FCC.
Crell 2ba6512
Add more AST examples.
Crell 7b6365d
Optimize static method calls in pipes.
Crell ba646f5
Typo fix.
Crell 9915924
Add tests for pipe optimizations.
Crell ffa7735
Make test output more portable.
Crell File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
--TEST-- | ||
A pipe operator displays as a pipe operator when outputting syntax, with correct parens. | ||
--FILE-- | ||
<?php | ||
|
||
print "Concat, which binds higher\n"; | ||
|
||
try { | ||
assert(false && foo() . bar() |> baz() . quux()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && (foo() . bar()) |> baz() . quux()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && foo() . (bar() |> baz()) . quux()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && foo() . bar() |> (baz() . quux())); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && (foo() . bar() |> baz()) . quux()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && foo() . (bar() |> baz() . quux())); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
print "<, which binds lower\n"; | ||
|
||
try { | ||
assert(false && foo() < bar() |> baz()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && (foo() < bar()) |> baz()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && foo() < (bar() |> baz())); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && foo() |> bar() < baz()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && (foo() |> bar()) < baz()); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
assert(false && foo() |> (bar() < baz())); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
|
||
|
||
print "misc examples\n"; | ||
|
||
try { | ||
assert(false && foo() |> (bar() |> baz(...))); | ||
} catch (AssertionError $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
?> | ||
--EXPECT-- | ||
Concat, which binds higher | ||
assert(false && foo() . bar() |> baz() . quux()) | ||
assert(false && foo() . bar() |> baz() . quux()) | ||
assert(false && foo() . (bar() |> baz()) . quux()) | ||
assert(false && foo() . bar() |> baz() . quux()) | ||
assert(false && (foo() . bar() |> baz()) . quux()) | ||
assert(false && foo() . (bar() |> baz() . quux())) | ||
<, which binds lower | ||
assert(false && foo() < bar() |> baz()) | ||
assert(false && (foo() < bar()) |> baz()) | ||
assert(false && foo() < bar() |> baz()) | ||
assert(false && foo() |> bar() < baz()) | ||
assert(false && foo() |> bar() < baz()) | ||
assert(false && foo() |> (bar() < baz())) | ||
misc examples | ||
assert(false && foo() |> (bar() |> baz(...))) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
--TEST-- | ||
Pipe operator rejects by-reference functions. | ||
--FILE-- | ||
<?php | ||
|
||
function _modify(int &$a): string { | ||
$a += 1; | ||
return "foo"; | ||
} | ||
|
||
function _append(array &$a): string { | ||
$a['bar'] = 'beep'; | ||
} | ||
|
||
// Simple variables | ||
try { | ||
$a = 5; | ||
$res1 = $a |> _modify(...); | ||
var_dump($res1); | ||
} catch (\Error $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
// Complex variables. | ||
try { | ||
$a = ['foo' => 'beep']; | ||
$res2 = $a |> _append(...); | ||
var_dump($res2); | ||
} catch (\Error $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
|
||
?> | ||
--EXPECTF-- | ||
_modify(): Argument #1 ($a) could not be passed by reference | ||
_append(): Argument #1 ($a) could not be passed by reference |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
--TEST-- | ||
Pipe operator accepts prefer-by-reference functions. | ||
--FILE-- | ||
<?php | ||
|
||
$a = ['hello', 'world']; | ||
|
||
try { | ||
$r = $a |> array_multisort(...); | ||
var_dump($r); | ||
} catch (\Error $e) { | ||
echo $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
?> | ||
--EXPECT-- | ||
bool(true) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
--TEST-- | ||
Functions are executed in the expected order | ||
--FILE-- | ||
<?php | ||
|
||
function foo() { echo __FUNCTION__, PHP_EOL; return 1; } | ||
function bar() { echo __FUNCTION__, PHP_EOL; return false; } | ||
function baz($in) { echo __FUNCTION__, PHP_EOL; return $in; } | ||
function quux($in) { echo __FUNCTION__, PHP_EOL; return $in; } | ||
|
||
$result = foo() | ||
|> (bar() ? baz(...) : quux(...)) | ||
|> var_dump(...); | ||
|
||
?> | ||
--EXPECT-- | ||
foo | ||
bar | ||
quux | ||
int(1) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
--TEST-- | ||
Pipe operator chains | ||
--FILE-- | ||
<?php | ||
|
||
function _test1(int $a): int { | ||
return $a + 1; | ||
} | ||
|
||
function _test2(int $a): int { | ||
return $a * 2; | ||
} | ||
|
||
$res1 = 5 |> '_test1' |> '_test2'; | ||
|
||
var_dump($res1); | ||
?> | ||
--EXPECT-- | ||
int(12) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
--TEST-- | ||
A pipe interrupted by an exception, to demonstrate correct order of execution. | ||
--FILE-- | ||
<?php | ||
|
||
function foo() { echo __FUNCTION__, PHP_EOL; return 1; } | ||
function bar() { echo __FUNCTION__, PHP_EOL; return false; } | ||
function baz($in) { echo __FUNCTION__, PHP_EOL; return $in; } | ||
function quux($in) { echo __FUNCTION__, PHP_EOL; throw new \Exception('Oops'); } | ||
|
||
try { | ||
$result = foo() | ||
|> (bar() ? baz(...) : quux(...)) | ||
|> var_dump(...); | ||
} | ||
catch (Throwable $e) { | ||
echo $e::class, ": ", $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
try { | ||
$result = foo() | ||
|> (throw new Exception('Break')) | ||
|> (bar() ? baz(...) : quux(...)) | ||
|> var_dump(...); | ||
} | ||
catch (Throwable $e) { | ||
echo $e::class, ": ", $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
?> | ||
--EXPECTF-- | ||
foo | ||
bar | ||
quux | ||
Exception: Oops | ||
foo | ||
Exception: Break |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
--TEST-- | ||
Pipe operator throws normally on missing function | ||
--FILE-- | ||
<?php | ||
|
||
try { | ||
$res1 = 5 |> '_test'; | ||
} | ||
catch (Throwable $e) { | ||
echo $e::class, ": ", $e->getMessage(), PHP_EOL; | ||
} | ||
|
||
?> | ||
--EXPECT-- | ||
Error: Call to undefined function _test() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
--TEST-- | ||
Generators | ||
--FILE-- | ||
<?php | ||
|
||
function producer(): \Generator { | ||
yield 1; | ||
yield 2; | ||
yield 3; | ||
} | ||
|
||
function map_incr(iterable $it): \Generator { | ||
foreach ($it as $val) { | ||
yield $val +1; | ||
} | ||
} | ||
|
||
$result = producer() |> map_incr(...) |> iterator_to_array(...); | ||
|
||
var_dump($result); | ||
?> | ||
--EXPECT-- | ||
array(3) { | ||
[0]=> | ||
int(2) | ||
[1]=> | ||
int(3) | ||
[2]=> | ||
int(4) | ||
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
--TEST-- | ||
Pipe operator handles all callable styles | ||
--FILE-- | ||
<?php | ||
|
||
function _add(int $x, int $y): int { | ||
return $x + $y; | ||
} | ||
|
||
function _area(int $x, int $y): int { | ||
return $x * $y; | ||
} | ||
|
||
class _Test | ||
{ | ||
public function message(string $which): string | ||
{ | ||
if ($which == 1) { | ||
return "Hello"; | ||
} | ||
else if ($which == 2) { | ||
return "Goodbye"; | ||
} | ||
else { | ||
return "World"; | ||
} | ||
} | ||
} | ||
|
||
class StaticTest { | ||
public static function oneMore(int $x): int { | ||
return $x + 1; | ||
} | ||
} | ||
|
||
function _double(int $x): int { | ||
return $x * 2; | ||
} | ||
|
||
function multiplier(int $x): \Closure | ||
{ | ||
return fn($y) => $x * $y; | ||
} | ||
|
||
$test = new _Test(); | ||
|
||
$add3 = fn($x) => _add($x, 3); | ||
|
||
$res1 = 2 | ||
|> [$test, 'message'] | ||
|> 'strlen' | ||
|> $add3 | ||
|> fn($x) => _area($x, 2) | ||
|> _double(...) | ||
|> multiplier(3) | ||
|> StaticTest::oneMore(...) | ||
; | ||
|
||
var_dump($res1); | ||
?> | ||
--EXPECT-- | ||
int(121) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
--TEST-- | ||
Pipe operator handles all callable styles | ||
--FILE-- | ||
<?php | ||
|
||
namespace Beep { | ||
function test(int $x) { | ||
echo $x, PHP_EOL; | ||
} | ||
} | ||
|
||
namespace Bar { | ||
use function \Beep\test; | ||
|
||
5 |> test(...); | ||
Crell marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
5 |> \Beep\test(...); | ||
} | ||
?> | ||
--EXPECT-- | ||
5 | ||
5 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.