> > "Cheating" in the sense that you wrote out a "general syntax", > I got it.
> That's not the problem; the problem is that the following are all equivalent expressions: > (((foo()))) In principle, this is not a problem because the expression in parentheses preceded by `spawn` is unambiguously a call expression. That is, from an analysis perspective, everything is fine here because the expression `spawn ()` is a syntax error. On the other hand, the expression `spawn (())` is also an error, but a semantic one, because there is nothing to call. The expression spawn ((), (), ()) is also an error because it attempts to specify a list of parameters without a call expression. It seems I didn't make any mistakes anywhere? > > But from a user's point of view, I hate rules like that which mean subtle changes completely change the meaning of the code, in a very specific context. > >From the user's perspective, what is the difference between these two expressions? ```php spawn function(): string { ... }; spawn (function(): string { ... }); ``` > > All of these are examples of the keyword being followed by *a function call*, not *any expression*. Which honestly I think is the right way to go. > I created these examples according to the syntax rules from the Bison file for `function_call`. Of course, I might have missed something, but overall, the list above essentially represents what `function_call` should expand into. If we define the rule `spawn function_call`, then `spawn` acts as a prefix in this expression. However, everything that is already defined for `function_call` in PHP must remain valid. If we take the most complex part: ```bison | callable_expr { $<num>$ = CG(zend_lineno); } argument_list { $$ = zend_ast_create(ZEND_AST_CALL, $1, $3); $$->lineno = $<num>2; } callable_expr: callable_variable { $$ = $1; } | '(' expr ')' { $$ = $2; } | dereferenceable_scalar { $$ = $1; } | new_dereferenceable { $$ = $1; } ; ``` we can see that the expression (((some()))) corresponds to the second line, and arguments must follow. However, in this case, arguments become *mandatory*. This means that after (((some()))), there must also be (). And another good thing about this syntax: a coroutine is an execution context, and a function call is a transition to a block of code. This means that the expression `spawn function_call` can be understood literally as: * Create a new execution context * Execute `function_call` The syntax for `function_call` is already defined in PHP, and nothing changes in it. The second form of the `spawn` expression is: ```php spawn inline_function ``` where inline_function is a definition from bison: ```bison inline_function: function returns_ref backup_doc_comment '(' parameter_list ')' lexical_vars return_type ...; ``` Another advantage of this solution is that if `function_call` constructs change in the future, we won’t need to modify the definitions for `spawn`. > > If it's going to be a special case for an "inline coroutine", just use a keyword other than "function", so it doesn't look like an expression when it's not, like "spawn block { ... }"; or no keyword at all, just "spawn { ... }" > Well, yes, but here we again face the issue with `returnType` syntax, which ends up hanging in the air...