Grammar   A
last analyzed

Complexity

Total Complexity 28

Size/Duplication

Total Lines 193
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 50
dl 0
loc 193
rs 10
c 0
b 0
f 0
wmc 28

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getRootSymbol() 0 3 1
A getFullProductionList() 0 10 3
A addProduction() 0 8 2
A getEoiSymbol() 0 3 1
A getEoiToken() 0 3 1
A getNonTerminalList() 0 3 1
A getStartSymbol() 0 3 1
A isEoiSymbol() 0 5 2
A __construct() 0 5 1
A isTerminal() 0 9 3
A getToken() 0 7 2
A getTerminalList() 0 3 1
A tokenMatchesTerminal() 0 7 2
A isEoiToken() 0 3 1
A addToken() 0 3 1
A getProduction() 0 11 3
A getProductionList() 0 7 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Remorhaz\UniLex\Grammar\ContextFree;
6
7
use Remorhaz\UniLex\Exception;
8
9
class Grammar implements GrammarInterface
10
{
11
    private $tokenMap = [];
12
13
    private $productionMap = [];
14
15
    private $rootSymbol;
16
17
    private $startSymbol;
18
19
    private $eoiSymbol;
20
21
    /**
22
     * Constructor. Accepts non-empty maps of terminal and non-terminal productions separately.
23
     *
24
     * @param int $rootSymbol
25
     * @param int $startSymbol
26
     * @param int $eoiSymbol
27
     */
28
    public function __construct(int $rootSymbol, int $startSymbol, int $eoiSymbol)
29
    {
30
        $this->rootSymbol = $rootSymbol;
31
        $this->startSymbol = $startSymbol;
32
        $this->eoiSymbol = $eoiSymbol;
33
    }
34
35
    /**
36
     * @param int $symbolId
37
     * @param int $tokenId
38
     */
39
    public function addToken(int $symbolId, int $tokenId): void
40
    {
41
        $this->tokenMap[$symbolId] = $tokenId;
42
    }
43
44
    public function addProduction(int $headerId, int ...$symbolIdList): void
45
    {
46
        $isFirstProduction = !isset($this->productionMap[$headerId]);
47
        $index = $isFirstProduction
48
            ? 0
49
            : count($this->productionMap[$headerId]);
50
        $production = new Production($headerId, $index, ...$symbolIdList);
51
        $this->productionMap[$production->getHeaderId()][$production->getIndex()] = $production;
52
    }
53
54
    public function getRootSymbol(): int
55
    {
56
        return $this->rootSymbol;
57
    }
58
59
    public function getStartSymbol(): int
60
    {
61
        return $this->startSymbol;
62
    }
63
64
    public function getEoiSymbol(): int
65
    {
66
        return $this->eoiSymbol;
67
    }
68
69
    /**
70
     * @return int
71
     * @throws Exception
72
     */
73
    public function getEoiToken(): int
74
    {
75
        return $this->getToken($this->getEoiSymbol());
76
    }
77
78
    /**
79
     * @param int $symbolId
80
     * @return bool
81
     * @throws Exception
82
     */
83
    public function isTerminal(int $symbolId): bool
84
    {
85
        if (isset($this->tokenMap[$symbolId])) {
86
            return true;
87
        }
88
        if (isset($this->productionMap[$symbolId])) {
89
            return false;
90
        }
91
        throw new Exception("Symbol {$symbolId} is not defined");
92
    }
93
94
    /**
95
     * @param int $tokenId
96
     * @return bool
97
     * @throws Exception
98
     */
99
    public function isEoiToken(int $tokenId): bool
100
    {
101
        return $this->tokenMatchesTerminal($this->getEoiSymbol(), $tokenId);
102
    }
103
104
    /**
105
     * @param int $symbolId
106
     * @return bool
107
     * @throws Exception
108
     */
109
    public function isEoiSymbol(int $symbolId): bool
110
    {
111
        return $this->isTerminal($symbolId)
112
            ? $this->getEoiSymbol() == $symbolId
113
            : false;
114
    }
115
116
    /**
117
     * @param int $symbolId
118
     * @param int $tokenId
119
     * @return bool
120
     * @throws Exception
121
     */
122
    public function tokenMatchesTerminal(int $symbolId, int $tokenId): bool
123
    {
124
        if (!in_array($tokenId, $this->tokenMap)) {
125
            throw new Exception("Token {$tokenId} is not defined");
126
        }
127
128
        return $this->getToken($symbolId) == $tokenId;
129
    }
130
131
    /**
132
     * @param int $symbolId
133
     * @return int
134
     * @throws Exception
135
     */
136
    public function getToken(int $symbolId): int
137
    {
138
        if (!$this->isTerminal($symbolId)) {
139
            throw new Exception("Symbol {$symbolId} is not defined as terminal");
140
        }
141
142
        return $this->tokenMap[$symbolId];
143
    }
144
145
    public function getTerminalList(): array
146
    {
147
        return array_keys($this->tokenMap);
148
    }
149
150
    public function getNonTerminalList(): array
151
    {
152
        return array_keys($this->productionMap);
153
    }
154
155
    /**
156
     * @param int $symbolId
157
     * @return Production[]
158
     * @throws Exception
159
     */
160
    public function getProductionList(int $symbolId): array
161
    {
162
        if ($this->isTerminal($symbolId)) {
163
            throw new Exception("Symbol {$symbolId} is terminal and can't have productions");
164
        }
165
166
        return $this->productionMap[$symbolId];
167
    }
168
169
    /**
170
     * @param int $symbolId
171
     * @param int $productionIndex
172
     * @return Production
173
     * @throws Exception
174
     */
175
    public function getProduction(int $symbolId, int $productionIndex): Production
176
    {
177
        if ($symbolId == $this->getRootSymbol()) {
178
            return new Production($this->getRootSymbol(), 0, $this->getStartSymbol(), $this->getEoiSymbol());
179
        }
180
        $productionList = $this->getProductionList($symbolId);
181
        if (!isset($productionList[$productionIndex])) {
182
            throw new Exception("Symbol {$symbolId} has no production at index {$productionIndex}");
183
        }
184
185
        return $productionList[$productionIndex];
186
    }
187
188
    /**
189
     * @return Production[]
190
     * @throws Exception
191
     */
192
    public function getFullProductionList(): array
193
    {
194
        $productionList = [];
195
        foreach ($this->getNonTerminalList() as $symbolId) {
196
            foreach ($this->getProductionList($symbolId) as $production) {
197
                $productionList[] = $production;
198
            }
199
        }
200
201
        return $productionList;
202
    }
203
}
204