Test Setup Failed
Push — master ( dae5b9...d54cec )
by Martijn
02:12
created

Choice::parseLongest()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 16
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 16
rs 9.2222
c 0
b 0
f 0
cc 6
nc 6
nop 3
1
<?php
2
3
namespace vanderlee\comprehend\parser\structure;
4
5
use vanderlee\comprehend\core\ArgumentsTrait;
6
use vanderlee\comprehend\core\Context;
7
use vanderlee\comprehend\directive\Prefer;
8
use vanderlee\comprehend\match\Match;
9
use vanderlee\comprehend\match\Success;
10
use vanderlee\comprehend\parser\Parser;
11
12
/**
13
 * Match one of the provided parsers.
14
 *
15
 * @author Martijn
16
 */
17
class Choice extends IterableParser
18
{
19
20
    use ArgumentsTrait;
21
    use PreferTrait;
22
23
    public function __construct(...$arguments)
24
    {
25
        if (empty($arguments)) {
26
            throw new \InvalidArgumentException('No arguments');
27
        }
28
29
        $this->parsers = self::getArguments($arguments);
30
    }
31
32
    public static function first(...$arguments)
33
    {
34
        return (new self(...$arguments))->preferFirst();
35
    }
36
37
    public static function shortest(...$arguments)
38
    {
39
        return (new self(...$arguments))->preferShortest();
40
    }
41
42
    public static function longest(...$arguments)
43
    {
44
        return (new self(...$arguments))->preferLongest();
45
    }
46
47
    private function parseFirst(&$input, $offset, Context $context)
48
    {
49
        $max = 0;
50
        foreach ($this->parsers as $parser) {
51
            $match = $parser->parse($input, $offset, $context);
52
            if ($match->match) {
53
                return $this->success($input, $offset, $match->length, $match);
54
            }
55
            $max = max($max, $match->length);
56
        }
57
        return $this->failure($input, $offset, $max);
58
    }
59
60
    private function parseLongest(&$input, $offset, Context $context)
61
    {
62
        $max_match = $this->failure($input, $offset);
63
        foreach ($this->parsers as $parser) {
64
            $match = $parser->parse($input, $offset, $context);
65
            if ($match->match == $max_match->match) {
66
                if ($match->length > $max_match->length) {
67
                    $max_match = ($match instanceof Success)
68
                        ? $this->success($input, $offset, $match->length, $match)
69
                        : $this->failure($input, $offset, $match->length);
70
                }
71
            } elseif ($match instanceof Success) {
72
                $max_match = $this->success($input, $offset, $match->length, $match);
73
            }
74
        }
75
        return $max_match;
76
    }
77
78
    private function parseShortest(&$input, $offset, Context $context)
79
    {
80
        /** @var Match $match */
81
        $match = null;
82
        foreach ($this->parsers as $parser) {
83
            $attempt = $parser->parse($input, $offset, $context);
84
85
            switch (true) {
86
                case!$match: // Keep attempt if first.
87
                case $attempt->match && !$match->match: // Keep attempt if first match
88
                case $attempt->match === $match->match && $attempt->length < $match->length: // Keep attempt if equally successful but shorter
89
                    $match = $attempt;
90
            }
91
        }
92
93
        // This will fail! $match is not necessarily the shortest
94
        return ($match instanceof Success)
95
            ? $this->success($input, $offset, $match->length, $match)
96
            : $this->failure($input, $offset, $match->length);
97
    }
98
99
    protected function parse(&$input, $offset, Context $context)
100
    {
101
        $this->pushPreferenceToContext($context);
102
103
        switch ($context->getPreference()) {
104
            default:
105
            case Prefer::FIRST:
106
                $match = $this->parseFirst($input, $offset, $context);
107
                break;
108
109
            case Prefer::LONGEST:
110
                $match = $this->parseLongest($input, $offset, $context);
111
                break;
112
113
            case Prefer::SHORTEST:
114
                $match = $this->parseShortest($input,$offset, $context);
115
                break;
116
        }
117
118
        $this->popPreferenceFromContext($context);
119
120
        return $match;
121
    }
122
123
    /**
124
     * Add one or more parsers as choices
125
     *
126
     * @param string[]|int[]|Parser[] $arguments
127
     * @return $this
128
     */
129
    public function add(...$arguments)
130
    {
131
        $this->parsers = array_merge($this->parsers, self::getArguments($arguments));
132
133
        return $this;
134
    }
135
136
    public function __toString()
137
    {
138
        $prefix = $this->preference === Prefer::LONGEST
0 ignored issues
show
introduced by
The condition $this->preference is always false. If $this->preference can have other possible types, add them to src/Vanderlee/Comprehend...er/Structure/Choice.php:18
Loading history...
139
            ? 'longest-of'
140
            :
141
            ($this->preference === Prefer::SHORTEST
0 ignored issues
show
introduced by
The condition $this->preference is always false. If $this->preference can have other possible types, add them to src/Vanderlee/Comprehend...er/Structure/Choice.php:18
Loading history...
142
                ? 'shortest-of'
143
                :
144
                '');
145
146
        return $prefix . '( ' . join(' | ', $this->parsers) . ' )';
147
    }
148
}
149