Skip to content
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

Pipe operator #17118

Draft
wants to merge 38 commits into
base: master
Choose a base branch
from
Draft

Pipe operator #17118

wants to merge 38 commits into from

Conversation

Crell
Copy link
Contributor

@Crell Crell commented Dec 11, 2024

@Crell Crell changed the title Pipe and function composition operators Pipe operator Feb 6, 2025
@Bilge
Copy link

Bilge commented Feb 7, 2025

Oh, nice. You're implementing your own RFCs now? 👍🏻

@Crell
Copy link
Contributor Author

Crell commented Feb 7, 2025

Well, with help. :-)

@Crell Crell force-pushed the func-composition branch from 51bd4cd to e98f4df Compare March 18, 2025 15:31
@Crell Crell force-pushed the func-composition branch from e98f4df to 2beea97 Compare March 18, 2025 16:39
arnaud-lb and others added 3 commits March 18, 2025 18:29
@@ -71,6 +71,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
%left T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG
%nonassoc T_IS_EQUAL T_IS_NOT_EQUAL T_IS_IDENTICAL T_IS_NOT_IDENTICAL T_SPACESHIP
%nonassoc '<' T_IS_SMALLER_OR_EQUAL '>' T_IS_GREATER_OR_EQUAL
%left T_PIPE
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at ast.phpt, I wonder if T_PIPE should move once more to sit between . and T_SL (<<).

It appears to be more useful for:

"foo" . $a |> bar(...) . "baz"

to mean:

"foo" . bar($a) . "baz"

instead of currently:

(bar(...) . "baz")("foo" . $a)

which is semantic non-sense. This would be reasonably consistent with the intention of https://wiki.php.net/rfc/concatenation_precedence.

Keeping T_PIPE below + makes sense to me, since with function composition, applying + to a callable is a well-defined operation.

It really is a mess that the two sides of |> have very different semantics, making it hard to find a single obviously correct position in the precedence list. And I don't have a strong opinion regarding making this suggested change. The correct solution is “use pipe only in an assignment, not in other expressions”.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have a strong opinion here. In practice I agree, one should not be writing "interesting" expressions with pipes in them. The most complex anyone SHOULD be doing is with + for composition (next RFC), and then parens may be a good idea anyway.

Anyone else have an opinion to argue?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like it gets messy quickly and an optimal precedence may not exist. I agree with Tim.

Copy link
Member

@arnaud-lb arnaud-lb Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the two sides of |> have very different semantics, making it hard to find a single obviously correct position in the precedence list

Yes. When considering the LHS, |> is more useful with a lower precedence, but when considering the RHS, a higher precedence would be better.

Can we use expr T_PIPE expr %prec PIPE to change the precedence of this rule, so that it reduces earlier after T_PIPE?

e.g. with the following change:

diff --git a/Zend/zend_language_parser.y b/Zend/zend_language_parser.y
index 7c4183ef736..845b187e489 100644
--- a/Zend/zend_language_parser.y
+++ b/Zend/zend_language_parser.y
@@ -81,6 +81,7 @@ static YYSIZE_T zend_yytnamerr(char*, const char*);
 %precedence '~' T_INT_CAST T_DOUBLE_CAST T_STRING_CAST T_ARRAY_CAST T_OBJECT_CAST T_BOOL_CAST T_UNSET_CAST '@'
 %right T_POW
 %precedence T_CLONE
+%precedence PIPE
 
 /* Resolve danging else conflict */
 %precedence T_NOELSE
@@ -1280,7 +1281,7 @@ expr:
 			{ $$ = zend_ast_create_binary_op(ZEND_IS_EQUAL, $1, $3); }
 	|	expr T_IS_NOT_EQUAL expr
 			{ $$ = zend_ast_create_binary_op(ZEND_IS_NOT_EQUAL, $1, $3); }
-	|	expr T_PIPE expr
+	|	expr T_PIPE expr %prec PIPE
 			{ $$ = zend_ast_create(ZEND_AST_PIPE, $1, $3); }
 	|	expr '<' expr
 			{ $$ = zend_ast_create_binary_op(ZEND_IS_SMALLER, $1, $3); }

The input

1 + $a |> bar(...) + 2;

is parsed as

((1 + $a) |> bar(...)) + 2;

I'm not a parser expert and I don't understand the full implications of this, however, so this may be a bad idea.

The correct solution is “use pipe only in an assignment, not in other expressions”.

I agree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

6 participants