Context::nonTerminals()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 3
nc 3
nop 0
1
<?php
2
/**
3
 * This file is part of PHP-Yacc package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace PhpYacc\Grammar;
11
12
use PhpYacc\Exception\LogicException;
13
use PhpYacc\Support\Utils;
14
use PhpYacc\Yacc\Macro\DollarExpansion;
15
use PhpYacc\Yacc\Production;
16
17
/**
18
 * Class Context.
19
 *
20
 * @property Symbol $nilsymbol
21
 * @property array|Symbol[] $symbols
22
 * @property array|State[] $states
23
 * @property array|Production[] $grams
24
 * @property \Generator $nonterminals
25
 * @property \Generator $terminals
26
 */
27
class Context
28
{
29
    /**
30
     * @var array
31
     */
32
    public $macros = [
33
        DollarExpansion::SEMVAL_LHS_TYPED   => '',
34
        DollarExpansion::SEMVAL_LHS_UNTYPED => '',
35
        DollarExpansion::SEMVAL_RHS_TYPED   => '',
36
        DollarExpansion::SEMVAL_RHS_UNTYPED => '',
37
    ];
38
39
    /**
40
     * @var int
41
     */
42
    public $countSymbols = 0;
43
44
    /**
45
     * @var int
46
     */
47
    public $countTerminals = 0;
48
49
    /**
50
     * @var int
51
     */
52
    public $countNonTerminals = 0;
53
54
    /**
55
     * @var array
56
     */
57
    protected $symbolHash = [];
58
59
    /**
60
     * @var array|Symbol[]
61
     */
62
    protected $_symbols = [];
63
64
    /**
65
     * @var Symbol
66
     */
67
    protected $_nilsymbol;
68
69
    /**
70
     * @var bool
71
     */
72
    protected $finished = false;
73
74
    /**
75
     * @var array|State[]
76
     */
77
    protected $_states;
78
79
    /**
80
     * @var int
81
     */
82
    public $countStates = 0;
83
84
    /**
85
     * @var int
86
     */
87
    public $countNonLeafStates = 0;
88
89
    /**
90
     * @var bool
91
     */
92
    public $aflag = false;
93
94
    /**
95
     * @var bool
96
     */
97
    public $tflag = false;
98
99
    /**
100
     * @var string
101
     */
102
    public $className = '';
103
104
    /**
105
     * @var bool
106
     */
107
    public $debug = false;
108
109
    /**
110
     * @var string
111
     */
112
    public $filename = 'YY';
113
114
    /**
115
     * @var bool
116
     */
117
    public $pureFlag = false;
118
119
    /**
120
     * @var Symbol
121
     */
122
    public $startSymbol;
123
124
    /**
125
     * @var int
126
     */
127
    public $expected;
128
129
    /**
130
     * @var bool
131
     */
132
    public $unioned = false;
133
134
    /**
135
     * @var Symbol
136
     */
137
    public $eofToken;
138
139
    /**
140
     * @var Symbol
141
     */
142
    public $errorToken;
143
144
    /**
145
     * @var Symbol
146
     */
147
    public $startPrime;
148
149
    /**
150
     * @var array|Production[]
151
     */
152
    protected $_grams = [];
153
154
    /**
155
     * @var int
156
     */
157
    public $countGrams = 0;
158
159
    /**
160
     * @var array
161
     */
162
    public $defaultAct = [];
163
164
    /**
165
     * @var array
166
     */
167
    public $defaultGoto = [];
168
169
    /**
170
     * @var array
171
     */
172
    public $termAction = [];
173
174
    /**
175
     * @var array
176
     */
177
    public $classAction = [];
178
179
    /**
180
     * @var array
181
     */
182
    public $nonTermGoto = [];
183
184
    /**
185
     * @var array
186
     */
187
    public $classOf = [];
188
189
    /**
190
     * @var array
191
     */
192
    public $cTermIndex = [];
193
194
    /**
195
     * @var array
196
     */
197
    public $oTermIndex = [];
198
199
    /**
200
     * @var array
201
     */
202
    public $frequency = [];
203
204
    /**
205
     * @var array
206
     */
207
    public $stateImageSorted = [];
208
209
    /**
210
     * @var int
211
     */
212
    public $countPrims = 0;
213
214
    /**
215
     * @var array
216
     */
217
    public $prims = [];
218
219
    /**
220
     * @var array
221
     */
222
    public $primof = [];
223
224
    /**
225
     * @var array
226
     */
227
    public $class2nd = [];
228
229
    /**
230
     * @var int
231
     */
232
    public $countClasses = 0;
233
234
    /**
235
     * @var int
236
     */
237
    public $countAux = 0;
238
239
    /**
240
     * @var null|resource
241
     */
242
    public $debugFile;
243
244
    /**
245
     * Context constructor.
246
     *
247
     * @param string        $filename
248
     * @param resource|null $debugFile
249
     */
250
    public function __construct(string $filename = 'YY', resource $debugFile = null)
251
    {
252
        $this->filename = $filename;
253
        $this->debugFile = $debugFile;
0 ignored issues
show
Documentation Bug introduced by
It seems like $debugFile can also be of type object<PhpYacc\Grammar\resource>. However, the property $debugFile is declared as type null|resource. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
254
    }
255
256
    /**
257
     * @param string $data
258
     */
259
    public function debug(string $data)
260
    {
261
        if ($this->debugFile) {
262
            \fwrite($this->debugFile, $data);
263
        }
264
    }
265
266
    /**
267
     * @param $name
268
     *
269
     * @throws LogicException
270
     *
271
     * @return mixed
272
     */
273
    public function __get($name)
274
    {
275
        switch ($name) {
276
            case 'terminals': return $this->terminals();
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
277
            case 'nonterminals': return $this->nonTerminals();
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Coding Style introduced by
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
278
        }
279
280
        if (!isset($this->{'_'.$name})) {
281
            throw new LogicException("Should never happen: unknown property $name");
282
        }
283
284
        return $this->{'_'.$name};
285
    }
286
287
    /**
288
     * @param $name
289
     * @param $value
290
     */
291
    public function __set($name, $value)
292
    {
293
        $this->{'set'.$name}($value);
294
    }
295
296
    /**
297
     * @return void
298
     */
299
    public function finish()
300
    {
301
        if ($this->finished) {
302
            return;
303
        }
304
305
        $this->finished = true;
306
        $code = 0;
307
308
        foreach ($this->terminals() as $term) {
309
            $term->code = $code++;
310
        }
311
312
        foreach ($this->nonTerminals() as $nonterm) {
313
            $nonterm->code = $code++;
314
        }
315
316
        foreach ($this->nilSymbols() as $nil) {
317
            $nil->code = $code++;
318
        }
319
320
        \usort($this->_symbols, function ($a, $b) {
321
            return $a->code <=> $b->code;
322
        });
323
    }
324
325
    /**
326
     * @return Symbol
327
     */
328
    public function nilSymbol(): Symbol
329
    {
330
        if ($this->_nilsymbol === null) {
331
            $this->_nilsymbol = $this->intern('@nil');
332
        }
333
334
        return $this->_nilsymbol;
335
    }
336
337
    /**
338
     * @return \Generator
339
     */
340
    public function terminals(): \Generator
341
    {
342
        foreach ($this->_symbols as $symbol) {
343
            if ($symbol->isTerminal) {
344
                yield $symbol;
345
            }
346
        }
347
    }
348
349
    /**
350
     * @return \Generator
351
     */
352
    public function nilSymbols(): \Generator
353
    {
354
        foreach ($this->_symbols as $symbol) {
355
            if ($symbol->isNilSymbol()) {
356
                yield $symbol;
357
            }
358
        }
359
    }
360
361
    /**
362
     * @return \Generator
363
     */
364
    public function nonTerminals(): \Generator
365
    {
366
        foreach ($this->_symbols as $symbol) {
367
            if ($symbol->isnonterminal) {
368
                yield $symbol;
369
            }
370
        }
371
    }
372
373
    /**
374
     * @return Symbol
375
     */
376
    public function genNonTerminal(): Symbol
377
    {
378
        $buffer = \sprintf('@%d', $this->countNonTerminals);
379
380
        return $this->internSymbol($buffer, false);
381
    }
382
383
    /**
384
     * @param string $s
385
     * @param bool   $isTerm
386
     *
387
     * @return Symbol
388
     */
389
    public function internSymbol(string $s, bool $isTerm): Symbol
390
    {
391
        $p = $this->intern($s);
392
393
        if (!$p->isNilSymbol()) {
394
            return $p;
395
        }
396
397
        if ($isTerm || $s[0] === "'") {
398
            if ($s[0] === "'") {
399
                $p->value = Utils::characterValue(\mb_substr($s, 1, -1));
400
            } else {
401
                $p->value = -1;
402
            }
403
            $p->terminal = Symbol::TERMINAL;
404
        } else {
405
            $p->value = null;
406
            $p->terminal = Symbol::NON_TERMINAL;
407
        }
408
409
        $p->associativity = Symbol::UNDEF;
410
        $p->precedence = Symbol::UNDEF;
411
412
        return $p;
413
    }
414
415
    /**
416
     * @param string $s
417
     *
418
     * @return Symbol
419
     */
420
    public function intern(string $s): Symbol
421
    {
422
        if (isset($this->symbolHash[$s])) {
423
            return $this->symbolHash[$s];
424
        }
425
        $p = new Symbol($this->countSymbols++, $s);
426
427
        return $this->addSymbol($p);
428
    }
429
430
    /**
431
     * @param Symbol $symbol
432
     *
433
     * @return Symbol
434
     */
435
    public function addSymbol(Symbol $symbol): Symbol
436
    {
437
        $this->finished = false;
438
        $this->_symbols[] = $symbol;
439
        $this->symbolHash[$symbol->name] = $symbol;
440
        $this->countTerminals = 0;
441
        $this->countNonTerminals = 0;
442
443
        foreach ($this->_symbols as $symbol) {
444
            if ($symbol->isTerminal) {
445
                $this->countTerminals++;
446
            } elseif ($symbol->isnonterminal) {
447
                $this->countNonTerminals++;
448
            }
449
        }
450
451
        return $symbol;
452
    }
453
454
    /**
455
     * @return array
456
     */
457
    public function symbols(): array
458
    {
459
        return $this->_symbols;
460
    }
461
462
    /**
463
     * @param int $code
464
     *
465
     * @return Symbol
466
     */
467
    public function symbol(int $code): Symbol
468
    {
469
        foreach ($this->_symbols as $symbol) {
470
            if ($symbol->code === $code) {
471
                return $symbol;
472
            }
473
        }
474
475
        return null;
476
    }
477
478
    /**
479
     * @param Production $p
480
     *
481
     * @return Production
482
     */
483
    public function addGram(Production $p)
484
    {
485
        $p->num = $this->countGrams++;
486
        $this->_grams[] = $p;
487
488
        return $p;
489
    }
490
491
    /**
492
     * @param int $i
493
     *
494
     * @return Production
495
     */
496
    public function gram(int $i): Production
497
    {
498
        \assert($i < $this->countGrams);
499
500
        return $this->_grams[$i];
501
    }
502
503
    /**
504
     * @param array $states
505
     */
506
    public function setStates(array $states)
507
    {
508
        foreach ($states as $state) {
509
            \assert($state instanceof State);
510
        }
511
        $this->_states = $states;
512
        $this->countStates = \count($states);
513
    }
514
515
    /**
516
     * @param int $n
517
     */
518
    public function setCountNonLeafStates(int $n)
519
    {
520
        $this->countNonLeafStates = $n;
521
    }
522
}
523