Parser::parse()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 6
dl 0
loc 10
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
1
<?php
2
3
/**
4
 * This file is part of Railt package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Railt\SDL\Frontend;
13
14
use Phplrt\Contracts\Grammar\RuleInterface;
15
use Phplrt\Contracts\Lexer\BufferInterface;
16
use Phplrt\Contracts\Lexer\Exception\LexerRuntimeExceptionInterface;
17
use Phplrt\Contracts\Lexer\LexerInterface;
18
use Phplrt\Contracts\Lexer\TokenInterface;
19
use Phplrt\Contracts\Source\ReadableInterface;
20
use Phplrt\Lexer\Lexer;
21
use Phplrt\Parser\Builder\BuilderInterface;
22
use Phplrt\Parser\Exception\ParserRuntimeException;
23
use Phplrt\Parser\Parser as BaseParser;
24
use Phplrt\Source\File;
25
use Railt\SDL\Exception\SyntaxErrorException;
26
use Railt\SDL\Frontend\Ast\Location;
27
use Railt\SDL\Frontend\Ast\Node;
28
29
/**
30
 * Class Parser
31
 */
32
final class Parser extends BaseParser implements BuilderInterface
33
{
34
    /**
35
     * @var array|\Closure[]
36
     */
37
    private array $reducers;
38
39
    /**
40
     * Parser constructor.
41
     */
42
    public function __construct()
43
    {
44
        $grammar = require __DIR__ . '/grammar.php';
45
46
        $this->reducers = $grammar['reducers'];
47
48
        parent::__construct($this->bootLexer($grammar), $grammar['grammar'], [
49
            parent::CONFIG_AST_BUILDER  => $this,
50
            parent::CONFIG_INITIAL_RULE => $grammar['initial'],
51
        ]);
52
    }
53
54
    /**
55
     * @param array $grammar
56
     * @return LexerInterface
57
     */
58
    private function bootLexer(array $grammar): LexerInterface
59
    {
60
        return new Lexer($grammar['lexemes'], $grammar['skips']);
61
    }
62
63
    /**
64
     * {@inheritDoc}
65
     * @throws SyntaxErrorException
66
     * @throws \Throwable
67
     */
68
    public function build(
69
        ReadableInterface $file,
70
        RuleInterface $rule,
71
        TokenInterface $token,
72
        $state,
73
        $children
74
    ) {
75
        try {
76
            if (isset($this->reducers[$state])) {
77
                return $this->reducers[$state]($children);
78
            }
79
80
            return null;
81
        } catch (SyntaxErrorException $e) {
82
            throw $e;
83
        } catch (\Exception $e) {
84
            throw SyntaxErrorException::fromToken($e->getMessage(), $file, $token);
85
        }
86
    }
87
88
    /**
89
     * {@inheritDoc}
90
     */
91
    public function parse($source): iterable
92
    {
93
        $source = File::new($source);
94
95
        try {
96
            return parent::parse($source);
97
        } catch (ParserRuntimeException|LexerRuntimeExceptionInterface $e) {
98
            $token = $e->getToken();
99
100
            throw SyntaxErrorException::fromToken($e->getMessage(), $source, $token);
101
        }
102
    }
103
104
    /**
105
     * {@inheritDoc}
106
     */
107
    protected function next(ReadableInterface $source, BufferInterface $buffer, $state)
108
    {
109
        $from = $buffer->current()->getOffset();
110
111
        $result = parent::next($source, $buffer, $state);
112
113
        if ($result instanceof Node) {
114
            $result->loc = new Location($source, $from, $buffer->current()->getOffset());
115
        }
116
117
        return $result;
118
    }
119
}
120