parsica-php / parsica

Parsica - PHP Parser Combinators - The easiest way to build robust parsers.
https://parsica-php.github.io/
MIT License
405 stars 18 forks source link

Ternary operator #20

Open rezen opened 3 years ago

rezen commented 3 years ago

Wanted to move the discussion from Twitter to here. I looked at the source to evaluate making a PR, and processed through the docs and was struggling with a clear path for multiple reasons.

Firstly, ternary is almost always a ? b : c, so do you implement with the expectation of those specific tokens? This is a minor question, and the answer is probably, no, don't expect specific tokens, the user provides those.

Secondly, all the current operators work with one symbol, not two and you need two or more symbols so all the Verraes\Parsica\Expression\*Assoc classes would probably need to change? OR instead there would it be a new ExpressionType?

rezen commented 3 years ago

Here are some forms of ternary operators to think about

rezen commented 3 years ago

Throwing out some rough code for handling more complex expressions - maybe that is the answer as opposed to a ternary operator?

<?php
use  function Verraes\Parsica\Expression\compound;

// ...
compound(function ($previousPrecedenceLevel) {
 return collect([
    $token('if'),  
    $previousPrecedenceLevel,
    $token('do:'),
    $previousPrecedenceLevel,
    $token('else:'),
    $previousPrecedenceLevel,
 ])
 ->map(fn($v) => [$v[1], $v[3], $v[6]]);
})
->map(fn($test, $whenTrue, $whenFalse) => ... );
mathiasverraes commented 3 years ago

Thanks for this input.

Something else to consider: So far the expression handler can deal with expression operator expression and variations. Ternaries are typically booleanExpr operator expression operator expression. We'll need to consider whether the parser cares about the difference between booleanExpr and expression.

mathiasverraes commented 3 years ago

In math, a ternary operation has the type a -> a -> a -> a, so everything is the same type. In C-like PLs, the common pattern is bool -> a -> a -> a but I see no reason why we should only support conditional ternaries.

https://en.wikipedia.org/wiki/Ternary_operation

mathiasverraes commented 3 years ago

21 is an experiment to gain some insights into how ternary expressions could work.

rezen commented 3 years ago

In my case I didn't want to make a distinction between booleanExpr vs expression. I tried creating a custom ExpressionType and so far it seems to work the way I would expect it.

class Ternary implements ExpressionType {

    public function buildPrecedenceLevel(Parser $previousPrecedenceLevel): Parser
    {

        $question = keepFirst(char("?"), skipHSpace());
        $colon = keepFirst(char(":"), skipHSpace());
        return choice(
            map(
                collect(
                    $previousPrecedenceLevel->thenIgnore($question),
                    $previousPrecedenceLevel->thenIgnore($colon),
                    $previousPrecedenceLevel,
                ),
                function(array $v) {
                    $tern = new _Ternary;
                    $tern->test = $v[0];
                    $tern->whenTrue = $v[1];
                    $tern->whenFalse = $v[2];
                    return $tern;
                }
            ), 
            $previousPrecedenceLevel
        );
    }
}