TableBuilder   A
last analyzed

Complexity

Total Complexity 16

Size/Duplication

Total Lines 104
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 35
dl 0
loc 104
rs 10
c 0
b 0
f 0
wmc 16

8 Methods

Rating   Name   Duplication   Size   Complexity  
A getFollow() 0 7 2
A getFirst() 0 7 2
A addProductionFollows() 0 8 3
A checkGrammarConflicts() 0 4 1
A addProductionFirsts() 0 5 2
A addProductionsFromNonTerminalMap() 0 8 3
A __construct() 0 3 1
A getTable() 0 9 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Remorhaz\UniLex\Parser\LL1\Lookup;
6
7
use Remorhaz\UniLex\Exception;
8
use Remorhaz\UniLex\Grammar\ContextFree\GrammarInterface;
9
use Remorhaz\UniLex\Grammar\ContextFree\Production;
10
11
class TableBuilder
12
{
13
    private $grammar;
14
15
    private $first;
16
17
    private $follow;
18
19
    private $table;
20
21
    public function __construct(GrammarInterface $grammar)
22
    {
23
        $this->grammar = $grammar;
24
    }
25
26
    /**
27
     * @return Table
28
     * @throws Exception
29
     */
30
    public function getTable(): TableInterface
31
    {
32
        if (!isset($this->table)) {
33
            $this->checkGrammarConflicts();
34
            $table = new Table();
35
            $this->addProductionsFromNonTerminalMap($table);
36
            $this->table = $table;
37
        }
38
        return $this->table;
39
    }
40
41
    /**
42
     * @throws Exception
43
     */
44
    private function checkGrammarConflicts(): void
45
    {
46
        $checker = new TableConflictChecker($this->grammar, $this->getFirst(), $this->getFollow());
47
        $checker->check();
48
    }
49
50
    /**
51
     * @return FirstInterface
52
     */
53
    private function getFirst(): FirstInterface
54
    {
55
        if (!isset($this->first)) {
56
            $builder = new FirstBuilder($this->grammar);
57
            $this->first = $builder->getFirst();
58
        }
59
        return $this->first;
60
    }
61
62
    /**
63
     * @return FollowInterface
64
     */
65
    private function getFollow(): FollowInterface
66
    {
67
        if (!isset($this->follow)) {
68
            $builder = new FollowBuilder($this->grammar, $this->getFirst());
69
            $this->follow = $builder->getFollow();
70
        }
71
        return $this->follow;
72
    }
73
74
    /**
75
     * @param Table $table
76
     * @throws Exception
77
     */
78
    private function addProductionsFromNonTerminalMap(Table $table): void
79
    {
80
        foreach ($this->grammar->getFullProductionList() as $production) {
81
            if ($production->getHeaderId() == $this->grammar->getRootSymbol()) {
82
                continue;
83
            }
84
            $this->addProductionFirsts($table, $production);
85
            $this->addProductionFollows($table, $production);
86
        }
87
    }
88
89
    /**
90
     * @param Table $table
91
     * @param Production $production
92
     * @throws Exception
93
     */
94
    private function addProductionFirsts(Table $table, Production $production): void
95
    {
96
        $productionFirsts = $this->getFirst()->getProductionTokens(...$production->getSymbolList());
97
        foreach ($productionFirsts as $tokenId) {
98
            $table->addProduction($production->getHeaderId(), $tokenId, $production->getIndex());
99
        }
100
    }
101
102
    /**
103
     * @param Table $table
104
     * @param Production $production
105
     * @throws Exception
106
     */
107
    private function addProductionFollows(Table $table, Production $production): void
108
    {
109
        if (!$this->getFirst()->productionHasEpsilon(...$production->getSymbolList())) {
110
            return;
111
        }
112
        $productionFollows = $this->getFollow()->getTokens($production->getHeaderId());
113
        foreach ($productionFollows as $tokenId) {
114
            $table->addProduction($production->getHeaderId(), $tokenId, $production->getIndex());
115
        }
116
    }
117
}
118