Passed
Push — master ( 852e13...6d4e0e )
by Edward
03:37
created

LanguageBuilder::splitSymbolInTransitions()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 1
nop 2
dl 0
loc 10
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Remorhaz\UniLex\RegExp\FSM;
4
5
use Remorhaz\UniLex\Exception as UniLexException;
6
7
class LanguageBuilder
8
{
9
10
    private $symbolTable;
11
12
    private $transitionMap;
13
14
    public function __construct(SymbolTable $symbolTable, TransitionMap $transitionMap)
15
    {
16
        $this->symbolTable = $symbolTable;
17
        $this->transitionMap = $transitionMap;
18
    }
19
20
    public static function forNfa(Nfa $nfa): self
21
    {
22
        return new self($nfa->getSymbolTable(), $nfa->getSymbolTransitionMap());
23
    }
24
25
    /**
26
     * @param int   $stateIn
27
     * @param int   $stateOut
28
     * @param Range ...$ranges
29
     * @throws UniLexException
30
     */
31
    public function addTransition(int $stateIn, int $stateOut, Range ...$ranges): void
32
    {
33
        $this->transitionMap->addTransition($stateIn, $stateOut, $this->getSymbolList(...$ranges));
34
    }
35
36
    /**
37
     * @param Range ...$ranges
38
     * @return array
39
     * @throws UniLexException
40
     */
41
    public function getSymbolList(Range ...$ranges): array
42
    {
43
        $rangeSetCalc = new RangeSetCalc();
44
        $newRangeSet = new RangeSet(...$ranges);
45
        $symbolList = [];
46
        $shouldAddNewSymbol = true;
47
        foreach ($this->symbolTable->getRangeSetList() as $symbolId => $oldRangeSet) {
48
            if ($rangeSetCalc->equals($oldRangeSet, $newRangeSet)) {
49
                $symbolList[] = $symbolId;
50
                $shouldAddNewSymbol = false;
51
                break;
52
            }
53
            $rangeSetDiff = $rangeSetCalc->xor($oldRangeSet, $newRangeSet);
54
            $onlyInOldRangeSet = $rangeSetCalc->and($oldRangeSet, $rangeSetDiff);
55
            if ($onlyInOldRangeSet->isEmpty()) {
56
                $symbolList[] = $symbolId;
57
                $newRangeSet = $rangeSetDiff;
58
                continue;
59
            }
60
            if ($rangeSetCalc->equals($onlyInOldRangeSet, $oldRangeSet)) {
61
                continue;
62
            }
63
            $splitSymbolId = $this
64
                ->symbolTable
65
                ->replaceSymbol($symbolId, $onlyInOldRangeSet)
66
                ->addSymbol($and = $rangeSetCalc->and($oldRangeSet, $newRangeSet));
67
            $this->splitSymbolInTransitions($symbolId, $splitSymbolId);
68
            $symbolList[] = $splitSymbolId;
69
            $newRangeSet = $rangeSetCalc->and($newRangeSet, $rangeSetDiff);
70
        }
71
        if ($shouldAddNewSymbol && !$newRangeSet->isEmpty()) {
72
            $newSymbolId = $this->symbolTable->addSymbol($newRangeSet);
73
            $symbolList[] = $newSymbolId;
74
        }
75
76
        return $symbolList;
77
    }
78
79
    private function splitSymbolInTransitions(int $symbolId, int $symbolToAdd): void
80
    {
81
        $addSymbol = function (array $symbolList) use ($symbolId, $symbolToAdd) {
82
            if (in_array($symbolId, $symbolList)) {
83
                $symbolList[] = $symbolToAdd;
84
            }
85
86
            return $symbolList;
87
        };
88
        $this->transitionMap->replaceEachTransition($addSymbol);
89
    }
90
}
91