Completed
Push — master ( 69f641...8fff44 )
by Richard
05:35
created

Parser::fetch_next_token()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 6
cts 6
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
crap 1
1
<?php
2
/******************************************************************************
3
 * An implementation of dicto (scg.unibe.ch/dicto) in and for PHP.
4
 *
5
 * Copyright (c) 2016 Richard Klees <[email protected]>
6
 * 
7
 * This software is licensed under The MIT License. You should have received
8
 * a copy of the license along with the code.
9
 */
10
11
namespace Lechimp\Dicto\Definition;
12
13
/**
14
 * Baseclass for Parsers.
15
 */
16
abstract class Parser {
17
    /**
18
     * @var SymbolTable
19
     */
20
    private $symbol_table;
21
22
    /**
23
     * @var Tokenizer|null
24
     */
25
    protected $tokenizer = null;
26
27
    /**
28
     * @var array|null   (Symbol, array $matches)
29
     */
30
    protected $token = null;
31
32 47
    public function __construct() {
33 47
        $this->symbol_table = $this->create_symbol_table();
34 47
        $this->add_symbols_to($this->symbol_table);
35 47
    }
36
37
    /**
38
     * Parse the string according to this parser.
39
     *
40
     * @return mixed
41
     */
42 47
    public function parse($source) {
43
        try {
44 47
            $this->tokenizer = $this->create_tokenizer($source);
45 47
            $this->token = $this->tokenizer->current();
46 47
            return $this->root();
47
        }
48 1
        catch (ParserException $e) {
49 1
            list($l, $c) = $this->tokenizer->source_position();
50 1
            $e->setPosition($l, $c);
51 1
            throw $e;
52
        }
53
        finally {
54 47
            $this->tokenizer = null;
55 47
            $this->token = null;
56 47
        }
57
    }
58
59
    /**
60
     * The root for the parse tree.
61
     *
62
     * @return  mixed
63
     */
64
    abstract protected function root();
65
66
    // Factory Methods
67
68
    /**
69
     * Build the Tokenizer.
70
     *
71
     * @return  Tokenizer
72
     */
73 47
    public function create_tokenizer($source) {
74 47
        assert('is_string($source)');
75 47
        return new Tokenizer($this->symbol_table, $source);
76
    }
77
78
    /**
79
     * Build the SymbolTable
80
     *
81
     * @return SymbolTable
82
     */
83 47
    protected function create_symbol_table() {
84 47
        return new SymbolTable();
85
    }
86
87
    /**
88
     * @param   SymbolTable
89
     * @return  null
90
     */
91
    abstract protected function add_symbols_to(SymbolTable $table);
92
93
    // Helpers for defining the grammar.
94
95
96
    // Helpers for actual parsing.
97
98
    /**
99
     * Set the current token to the next token from the tokenizer.
100
     *
101
     * @return  null
102
     */
103 45
    protected function fetch_next_token() {
104 45
        assert('is_array($this->token)');
105 45
        assert('$this->tokenizer !== null');
106 45
        $this->tokenizer->next();
107 45
        $this->token = $this->tokenizer->current();
108 45
    }
109
110
    /**
111
     * Get the current symbol.
112
     *
113
     * @return  Symbol
114
     */
115 41
    protected function current_symbol() {
116 41
        return $this->token[0];
117
    }
118
119
    /**
120
     * Get the current match.
121
     *
122
     * @return  string[] 
123
     */
124 45
    protected function current_match() {
125 45
        return $this->token[1];
126
    }
127
128
    /**
129
     * Advance the tokenizer to the next token if current token
130
     * was matched by the given regexp.
131
     *
132
     * @param   string  $regexp
133
     * @return  null
134
     */
135 21
    protected function advance($regexp) {
136 21
        assert('is_string($regexp)');
137 21
        assert('is_array($this->token)');
138 21
        assert('$this->tokenizer !== null');
139 21
        if (!$this->is_current_token_matched_by($regexp)) {
140
            $match = $this->current_match()[0];
141
            throw new ParserException("Syntax Error: Expected '$regexp', found '$match'");
142
        }
143 21
        $this->tokenizer->next();
144 21
        $this->token = $this->tokenizer->current();
145 21
    }
146
147
    /**
148
     * Advance the tokenizer to the next token if current token
149
     * was matched by the given operator.
150
     *
151
     * @param   string  $op
152
     * @return  null
153
     */
154 9
    protected function advance_operator($op) {
155 9
        $this->advance($this->symbol_table->operator_regexp($op));
156 9
    }
157
158
    /**
159
     * Is the end of the file reached?
160
     *
161
     * @return  bool
162
     */
163 37
    public function is_end_of_file_reached() {
164 37
        return $this->is_current_token_matched_by("");
165
    }
166
167
    /**
168
     * Check if the current token was matched by the given regexp.
169
     *
170
     * @param   string  $regexp
171
     * @return  bool
172
     */
173 44
    protected function is_current_token_matched_by($regexp) {
174 44
        assert('is_string($regexp)');
175 44
        return $this->token[0]->regexp() == $regexp;
176
    }
177
178
    /**
179
     * Check if the current token is the given operator.
180
     *
181
     * @param   string  $operator
182
     * @return  bool
183
     */
184 6
    protected function is_current_token_operator($operator) {
185 6
        return $this->is_current_token_matched_by
186 6
            ($this->symbol_table->operator_regexp($operator));
187
    }
188
}
189