Compress::cmpStates()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 3
nc 3
nop 2
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\Compress;
11
12
use PhpYacc\Grammar\Context;
13
use PhpYacc\Grammar\Symbol;
14
use PhpYacc\Support\Utils;
15
16
/**
17
 * Class Compress.
18
 */
19
class Compress
20
{
21
    const UNEXPECTED = 32767;
22
    const DEFAULT = -32766;
23
    const VACANT = -32768;
24
25
    /**
26
     * @var Context
27
     */
28
    private $context;
29
30
    /**
31
     * @var CompressResult
32
     */
33
    private $result;
34
35
    /**
36
     * @param Context $context
37
     *
38
     * @return CompressResult
39
     */
40
    public function compress(Context $context)
0 ignored issues
show
Coding Style Best Practice introduced by
Please use __construct() instead of a PHP4-style constructor that is named after the class.
Loading history...
41
    {
42
        $this->result = new CompressResult();
43
        $this->context = $context;
44
45
        $this->makeupTable2();
46
47
        return $this->result;
48
    }
49
50
    /**
51
     * @return void
52
     */
53
    private function computePreImages()
54
    {
55
        /** @var PreImage[] $preImages */
56
        $preImages = [];
57
58
        for ($i = 0; $i < $this->context->countStates; $i++) {
59
            $preImages[$i] = new PreImage($i);
60
        }
61
62
        for ($i = 0; $i < $this->context->countClasses; $i++) {
63
            for ($j = 0; $j < $this->context->countTerminals; $j++) {
64
                $s = $this->context->classAction[$i][$j];
65
                if ($s > 0) {
66
                    $preImages[$s]->classes[$preImages[$s]->length++] = $i;
67
                }
68
            }
69
        }
70
71
        Utils::stableSort($preImages, PreImage::class.'::compare');
72
73
        $this->context->primof = \array_fill(0, $this->context->countStates, 0);
74
        $this->context->prims = \array_fill(0, $this->context->countStates, 0);
75
        $this->context->countPrims = 0;
76
77
        for ($i = 0; $i < $this->context->countStates;) {
78
            $p = $preImages[$i];
79
            $this->context->prims[$this->context->countPrims] = $p;
80
81
            for (; $i < $this->context->countStates && PreImage::compare($p, $preImages[$i]) === 0; $i++) {
82
                $this->context->primof[$preImages[$i]->index] = $p;
83
            }
84
85
            $p->index = $this->context->countPrims++;
86
        }
87
    }
88
89
    /**
90
     * @param array $t
91
     * @param int   $count
92
     *
93
     * @return array
94
     */
95
    private function encodeShiftReduce(array $t, int $count): array
96
    {
97
        for ($i = 0; $i < $count; $i++) {
98
            if ($t[$i] >= $this->context->countNonLeafStates) {
99
                $t[$i] = $this->context->countNonLeafStates + $this->context->defaultAct[$t[$i]];
100
            }
101
        }
102
103
        return $t;
104
    }
105
106
    /**
107
     * @return void
108
     */
109
    private function makeupTable2()
110
    {
111
        $this->context->termAction = \array_fill(0, $this->context->countNonLeafStates, 0);
112
        $this->context->classAction = \array_fill(0, $this->context->countNonLeafStates * 2, 0);
113
        $this->context->nonTermGoto = \array_fill(0, $this->context->countNonLeafStates, 0);
114
        $this->context->defaultAct = \array_fill(0, $this->context->countStates, 0);
115
        $this->context->defaultGoto = \array_fill(0, $this->context->countNonTerminals, 0);
116
117
        $this->resetFrequency();
118
        $this->context->stateImageSorted = \array_fill(0, $this->context->countNonLeafStates, 0);
119
        $this->context->classOf = \array_fill(0, $this->context->countStates, 0);
120
121
        for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
122
            $this->context->termAction[$i] = \array_fill(0, $this->context->countTerminals, self::VACANT);
123
            $this->context->nonTermGoto[$i] = \array_fill(0, $this->context->countNonTerminals, self::VACANT);
124
125
            foreach ($this->context->states[$i]->shifts as $shift) {
126
                if ($shift->through->isTerminal) {
127
                    $this->context->termAction[$i][$shift->through->code] = $shift->number;
128
                } else {
129
                    $this->context->nonTermGoto[$i][$this->nb($shift->through)] = $shift->number;
130
                }
131
            }
132
            foreach ($this->context->states[$i]->reduce as $reduce) {
133
                if ($reduce->symbol->isNilSymbol()) {
134
                    break;
135
                }
136
                $this->context->termAction[$i][$reduce->symbol->code] = -$this->encodeRederr($reduce->number);
137
            }
138
            $this->context->stateImageSorted[$i] = $i;
139
        }
140
141
        foreach ($this->context->states as $key => $state) {
142
            $r = null;
143
            foreach ($state->reduce as $r) {
144
                if ($r->symbol->isNilSymbol()) {
145
                    break;
146
                }
147
            }
148
            $this->context->defaultAct[$key] = $this->encodeRederr($r->number);
149
        }
150
151
        for ($j = 0; $j < $this->context->countNonTerminals; $j++) {
152
            $max = 0;
153
            $maxst = self::VACANT;
154
            $this->resetFrequency();
155
156
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
157
                $st = $this->context->nonTermGoto[$i][$j];
158
                if ($st > 0) {
159
                    $this->context->frequency[$st]++;
160
                    if ($this->context->frequency[$st] > $max) {
161
                        $max = $this->context->frequency[$st];
162
                        $maxst = $st;
163
                    }
164
                }
165
            }
166
            $this->context->defaultGoto[$j] = $maxst;
167
        }
168
        // 847
169
170
        Utils::stableSort($this->context->stateImageSorted, [$this, 'cmpStates']);
171
172
        $j = 0;
173
174
        for ($i = 0; $i < $this->context->countNonLeafStates;) {
175
            $k = $this->context->stateImageSorted[$i];
176
            $this->context->classAction[$j] = $this->context->termAction[$k];
177
            for (; $i < $this->context->countNonLeafStates && $this->cmpStates($this->context->stateImageSorted[$i], $k) === 0; $i++) {
178
                $this->context->classOf[$this->context->stateImageSorted[$i]] = $j;
179
            }
180
            $j++;
181
        }
182
        $this->context->countClasses = $j;
183
184
        if ($this->context->debug) {
185
            $this->context->debug("State=>class:\n");
186
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
187
                if ($i % 10 === 0) {
188
                    $this->context->debug("\n");
189
                }
190
                $this->context->debug(\sprintf('%3d=>%-3d ', $i, $this->context->classOf[$i]));
191
            }
192
            $this->context->debug("\n");
193
        }
194
195
        $this->computePreImages();
196
197
        if ($this->context->debug) {
198
            $this->printTable();
199
        }
200
201
        $this->extractCommon();
202
203
        $this->authodoxTable();
204
    }
205
206
    /**
207
     * @return void
208
     */
209
    private function printTable()
210
    {
211
        $this->context->debug("\nTerminal action:\n");
212
        $this->context->debug(\sprintf('%8.8s', 'T\\S'));
213
        for ($i = 0; $i < $this->context->countClasses; $i++) {
214
            $this->context->debug(\sprintf('%4d', $i));
215
        }
216
        $this->context->debug("\n");
217
        for ($j = 0; $j < $this->context->countTerminals; $j++) {
218
            $symbol = $this->context->symbol($j);
219
220
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
221
                if ($this->context->termAction[$i][$j] !== self::VACANT) {
222
                    break;
223
                }
224
            }
225 View Code Duplication
            if ($i < $this->context->countNonLeafStates) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
226
                $this->context->debug(\sprintf('%8.8s', $symbol->name));
227
                for ($i = 0; $i < $this->context->countClasses; $i++) {
228
                    $this->context->debug(Utils::printAction($this->context->classAction[$i][$j]));
229
                }
230
                $this->context->debug("\n");
231
            }
232
        }
233
234
        $this->context->debug("\nNonterminal GOTO table:\n");
235
        $this->context->debug(\sprintf('%8.8s', 'T\\S'));
236 View Code Duplication
        for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
237
            $this->context->debug(\sprintf('%4d', $i));
238
        }
239
        $this->context->debug("\n");
240
        foreach ($this->context->nonterminals as $symbol) {
241
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
242
                if ($this->context->nonTermGoto[$i][$this->nb($symbol)] > 0) {
243
                    break;
244
                }
245
            }
246 View Code Duplication
            if ($i < $this->context->countNonLeafStates) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
247
                $this->context->debug(\sprintf('%8.8s', $symbol->name));
248
                for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
249
                    $this->context->debug(Utils::printAction($this->context->nonTermGoto[$i][$this->nb($symbol)]));
250
                }
251
                $this->context->debug("\n");
252
            }
253
        }
254
255
        $this->context->debug("\nNonterminal GOTO table:\n");
256
        $this->context->debug(\sprintf('%8.8s default', 'T\\S'));
257
258 View Code Duplication
        for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
259
            $this->context->debug(\sprintf('%4d', $i));
260
        }
261
262
        $this->context->debug("\n");
263
264
        foreach ($this->context->nonterminals as $symbol) {
265
            $nb = $this->nb($symbol);
266
267
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
268
                if ($this->context->nonTermGoto[$i][$nb] > 0) {
269
                    break;
270
                }
271
            }
272
273
            if ($i < $this->context->countNonLeafStates) {
274
                $this->context->debug(\sprintf('%8.8s', $symbol->name));
275
                $this->context->debug(\sprintf('%8d', $this->context->defaultGoto[$nb]));
276
277
                for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
278
                    if ($this->context->nonTermGoto[$i][$nb] === $this->context->defaultGoto[$nb]) {
279
                        $this->context->debug('  = ');
280
                    } else {
281
                        $this->context->debug(Utils::printAction($this->context->nonTermGoto[$i][$nb]));
282
                    }
283
                }
284
                $this->context->debug("\n");
285
            }
286
        }
287
    }
288
289
    /**
290
     * @return void
291
     */
292
    private function extractCommon()
293
    {
294
        $this->context->class2nd = \array_fill(0, $this->context->countClasses, -1);
295
296
        $auxList = null;
297
        $n = 0;
298
299
        for ($i = 0; $i < $this->context->countPrims; $i++) {
300
            $preImage = $this->context->prims[$i];
301
            if ($preImage->length < 2) {
302
                continue;
303
            }
304
            $p = new Auxiliary();
305
            $this->bestCovering($p, $preImage);
306
            if ($p->gain < 1) {
307
                continue;
308
            }
309
            $p->preImage = $preImage;
310
            $p->next = $auxList;
311
            $auxList = $p;
312
            $n++;
313
        }
314
315
        if ($this->context->debug) {
316
            $this->context->debug("\nNumber of prims: {$this->context->countPrims}\n");
317
            $this->context->debug("\nCandidates of aux table:\n");
318
            for ($p = $auxList; $p !== null; $p = $p->next) {
319
                $this->context->debug(\sprintf('Aux = (%d) ', $p->gain));
320
                $f = 0;
321 View Code Duplication
                for ($j = 0; $j < $this->context->countTerminals; $j++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
                    if (self::VACANT !== $p->table[$j]) {
323
                        $this->context->debug(\sprintf($f++ ? ',%d' : '%d', $p->table[$j]));
324
                    }
325
                }
326
                $this->context->debug(' * ');
327
                for ($j = 0; $j < $p->preImage->length; $j++) {
328
                    $this->context->debug(\sprintf($j ? ',%d' : '%d', $p->preImage->classes[$j]));
329
                }
330
                $this->context->debug("\n");
331
            }
332
            $this->context->debug("Used aux table:\n");
333
        }
334
        $this->context->countAux = $this->context->countClasses;
335
        for (; ;) {
336
            $maxGain = 0;
337
            $maxAux = null;
338
            $pre = null;
339
            $maxPreImage = null;
340
            for ($p = $auxList; $p != null; $p = $p->next) {
341
                if ($p->gain > $maxGain) {
342
                    $maxGain = $p->gain;
343
                    $maxAux = $p;
344
                    $maxPreImage = $pre;
345
                }
346
                /** @var Auxiliary $pre */
347
                $pre = $p;
348
            }
349
350
            if ($maxAux === null) {
351
                break;
352
            }
353
354
            if ($maxPreImage) {
355
                $maxPreImage->next = $maxAux->next;
356
            } else {
357
                $auxList = $maxAux->next;
358
            }
359
360
            $maxAux->index = $this->context->countAux;
361
362 View Code Duplication
            for ($j = 0; $j < $maxAux->preImage->length; $j++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
363
                $cl = $maxAux->preImage->classes[$j];
364
                if (Utils::eqRow($this->context->classAction[$cl], $maxAux->table, $this->context->countTerminals)) {
365
                    $maxAux->index = $cl;
366
                }
367
            }
368
369
            if ($maxAux->index >= $this->context->countAux) {
370
                $this->context->classAction[$this->context->countAux++] = $maxAux->table;
371
            }
372
373 View Code Duplication
            for ($j = 0; $j < $maxAux->preImage->length; $j++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
374
                $cl = $maxAux->preImage->classes[$j];
375
                if ($this->context->class2nd[$cl] < 0) {
376
                    $this->context->class2nd[$cl] = $maxAux->index;
377
                }
378
            }
379
380
            if ($this->context->debug) {
381
                $this->context->debug(\sprintf('Selected aux[%d]: (%d) ', $maxAux->index, $maxAux->gain));
382
                $f = 0;
383 View Code Duplication
                for ($j = 0; $j < $this->context->countTerminals; $j++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
384
                    if (self::VACANT !== $maxAux->table[$j]) {
385
                        $this->context->debug(\sprintf($f++ ? ',%d' : '%d', $maxAux->table[$j]));
386
                    }
387
                }
388
                $this->context->debug(' * ');
389
                $f = 0;
390
                for ($j = 0; $j < $maxAux->preImage->length; $j++) {
391
                    $cl = $maxAux->preImage->classes[$j];
392
                    if ($this->context->class2nd[$cl] === $maxAux->index) {
393
                        $this->context->debug(\sprintf($f++ ? ',%d' : '%d', $cl));
394
                    }
395
                }
396
                $this->context->debug("\n");
397
            }
398
399
            for ($p = $auxList; $p != null; $p = $p->next) {
400
                $this->bestCovering($p, $p->preImage);
401
            }
402
        }
403
404
        if ($this->context->debug) {
405
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
406
                if ($this->context->class2nd[$this->context->classOf[$i]] >= 0 && $this->context->class2nd[$this->context->classOf[$i]] !== $this->context->classOf[$i]) {
407
                    $this->context->debug(\sprintf("state %d (class %d): aux[%d]\n", $i, $this->context->classOf[$i], $this->context->class2nd[$this->context->classOf[$i]]));
408
                } else {
409
                    $this->context->debug(\sprintf("state %d (class %d)\n", $i, $this->context->classOf[$i]));
410
                }
411
            }
412
        }
413
    }
414
415
    /**
416
     * @param Auxiliary $aux
417
     * @param PreImage  $prim
418
     */
419
    private function bestCovering(Auxiliary $aux, PreImage $prim)
420
    {
421
        $this->resetFrequency();
422
        $gain = 0;
423
        for ($i = 0; $i < $this->context->countTerminals; $i++) {
424
            $max = 0;
425
            $maxAction = -1;
426
            $countVacant = 0;
427
428
            for ($j = 0; $j < $prim->length; $j++) {
429
                if ($this->context->class2nd[$prim->classes[$j]] < 0) {
430
                    $c = $this->context->classAction[$prim->classes[$j]][$i];
431
                    if ($c > 0 && ++$this->context->frequency[$c] > $max) {
432
                        $maxAction = $c;
433
                        $max = $this->context->frequency[$c];
434
                    } elseif (self::VACANT === $c) {
435
                        $countVacant++;
436
                    }
437
                }
438
            }
439
440
            $n = $max - 1 - $countVacant;
441
442
            if ($n > 0) {
443
                $aux->table[$i] = $maxAction;
444
                $gain += $n;
445
            } else {
446
                $aux->table[$i] = self::VACANT;
447
            }
448
        }
449
        $aux->gain = $gain;
0 ignored issues
show
Documentation Bug introduced by
It seems like $gain can also be of type double. However, the property $gain is declared as type integer. 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...
450
    }
451
452
    /**
453
     * @return void
454
     */
455
    private function authodoxTable()
456
    {
457
        // TODO
458
        $this->context->cTermIndex = \array_fill(0, $this->context->countTerminals, -1);
459
        $this->context->oTermIndex = \array_fill(0, $this->context->countTerminals, 0);
460
461
        $countCTerms = 0;
462
        for ($j = 0; $j < $this->context->countTerminals; $j++) {
463
            if ($j === $this->context->errorToken->code) {
464
                $this->context->cTermIndex[$j] = $countCTerms;
465
                $this->context->oTermIndex[$countCTerms++] = $j;
466
                continue;
467
            }
468
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
469
                if ($this->context->termAction[$i][$j] !== self::VACANT) {
470
                    $this->context->cTermIndex[$j] = $countCTerms;
471
                    $this->context->oTermIndex[$countCTerms++] = $j;
472
                    break;
473
                }
474
            }
475
        }
476
477
        $cTermAction = \array_fill(
478
            0, $this->context->countAux, \array_fill(0, $countCTerms, 0)
479
        );
480
481 View Code Duplication
        for ($i = 0; $i < $this->context->countClasses; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
482
            for ($j = 0; $j < $countCTerms; $j++) {
483
                $cTermAction[$i][$j] = $this->context->classAction[$i][$this->context->oTermIndex[$j]];
484
            }
485
        }
486
487
        //582
488
489
        for ($i = 0; $i < $this->context->countClasses; $i++) {
490
            if ($this->context->class2nd[$i] >= 0 && $this->context->class2nd[$i] != $i) {
491
                $table = $this->context->classAction[$this->context->class2nd[$i]];
492
                for ($j = 0; $j < $countCTerms; $j++) {
493
                    if (self::VACANT !== $table[$this->context->oTermIndex[$j]]) {
494
                        if ($cTermAction[$i][$j] === $table[$this->context->oTermIndex[$j]]) {
495
                            $cTermAction[$i][$j] = self::VACANT;
496
                        } elseif ($cTermAction[$i][$j] === self::VACANT) {
497
                            $cTermAction[$i][$j] = self::DEFAULT;
498
                        }
499
                    }
500
                }
501
            }
502
        }
503
504 View Code Duplication
        for ($i = $this->context->countClasses; $i < $this->context->countAux; $i++) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
505
            for ($j = 0; $j < $countCTerms; $j++) {
506
                $cTermAction[$i][$j] = $this->context->classAction[$i][$this->context->oTermIndex[$j]];
507
            }
508
        }
509
        $base = [];
510
        $this->packTable(
511
            $cTermAction, $this->context->countAux, $countCTerms, false, false,
512
            $this->result->yyaction, $this->result->yycheck, $base
513
        );
514
        $this->result->yydefault = $this->context->defaultAct;
515
516
        $this->result->yybase = \array_fill(0, $this->context->countNonLeafStates * 2, 0);
517
        $this->result->yybasesize = $this->context->countNonLeafStates;
518
        for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
519
            $cl = $this->context->classOf[$i];
520
            $this->result->yybase[$i] = $base[$cl];
521
            if ($this->context->class2nd[$cl] >= 0 && $this->context->class2nd[$cl] != $cl) {
522
                $this->result->yybase[$i + $this->context->countNonLeafStates] = $base[$this->context->class2nd[$cl]];
523
                if ($i + $this->context->countNonLeafStates + 1 > $this->result->yybasesize) {
524
                    $this->result->yybasesize = $i + $this->context->countNonLeafStates + 1;
525
                }
526
            }
527
        }
528
529
        $this->result->yybase = \array_slice($this->result->yybase, 0, $this->result->yybasesize);
530
531
        //642
532
        $nonTermTransposed = \array_fill(0, $this->context->countNonTerminals, array_fill(0, $this->context->countNonLeafStates, 0));
533
        foreach ($nonTermTransposed as $j => $_dump) {
534
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
535
                $nonTermTransposed[$j][$i] = $this->context->nonTermGoto[$i][$j];
536
                if ($this->context->nonTermGoto[$i][$j] === $this->context->defaultGoto[$j]) {
537
                    $nonTermTransposed[$j][$i] = self::VACANT;
538
                }
539
            }
540
        }
541
542
        $this->packTable(
543
            $nonTermTransposed, $this->context->countNonTerminals, $this->context->countNonLeafStates,
544
            false, true, $this->result->yygoto, $this->result->yygcheck, $this->result->yygbase
545
        );
546
547
        $this->result->yygdefault = $this->context->defaultGoto;
548
549
        $this->result->yylhs = [];
550
        $this->result->yylen = [];
551
        foreach ($this->context->grams as $gram) {
552
            $this->result->yylhs[] = $this->nb($gram->body[0]);
553
            $this->result->yylen[] = \count($gram->body) - 1;
554
        }
555
556
        $this->result->yytranslatesize = 0;
557
558
        foreach ($this->context->terminals as $term) {
559
            $value = $term->value;
560
            if ($value + 1 > $this->result->yytranslatesize) {
561
                $this->result->yytranslatesize = $value + 1;
562
            }
563
        }
564
565
        $this->result->yytranslate = \array_fill(0, $this->result->yytranslatesize, $countCTerms);
566
        $this->result->yyncterms = $countCTerms;
567
568
        for ($i = 0; $i < $this->context->countTerminals; $i++) {
569
            if ($this->context->cTermIndex[$i] >= 0) {
570
                $symbol = $this->context->symbol($i);
571
                $this->result->yytranslate[$symbol->value] = $this->context->cTermIndex[$i];
572
            }
573
        }
574
575
        $this->result->yyaction = $this->encodeShiftReduce($this->result->yyaction, \count($this->result->yyaction));
576
        $this->result->yygoto = $this->encodeShiftReduce($this->result->yygoto, \count($this->result->yygoto));
577
        $this->result->yygdefault = $this->encodeShiftReduce($this->result->yygdefault, $this->context->countNonTerminals);
578
    }
579
580
    /**
581
     * @param array $transit
582
     * @param int   $nrows
583
     * @param int   $ncols
584
     * @param bool  $dontcare
585
     * @param bool  $checkrow
586
     * @param array $outtable
587
     * @param array $outcheck
588
     * @param array $outbase
589
     */
590
    private function packTable(array $transit, int $nrows, int $ncols, bool $dontcare, bool $checkrow, array &$outtable, array &$outcheck, array &$outbase)
591
    {
592
        $trow = [];
593
        for ($i = 0; $i < $nrows; $i++) {
594
            $trow[] = $p = new TRow($i);
595
            for ($j = 0; $j < $ncols; $j++) {
596
                if (self::VACANT !== $transit[$i][$j]) {
597
                    if ($p->mini < 0) {
598
                        $p->mini = $j;
599
                    }
600
                    $p->maxi = $j + 1;
601
                    $p->nent++;
602
                }
603
            }
604
            if ($p->mini < 0) {
605
                $p->mini = 0;
606
            }
607
        }
608
609
        Utils::stableSort($trow, [TRow::class, 'compare']);
610
611
        if ($this->context->debug) {
612
            $this->context->debug("Order:\n");
613
            for ($i = 0; $i < $nrows; $i++) {
614
                $this->context->debug(sprintf('%d,', $trow[$i]->index));
615
            }
616
            $this->context->debug("\n");
617
        }
618
619
        $poolsize = $nrows * $ncols;
620
        $actpool = \array_fill(0, $poolsize, 0);
621
        $check = \array_fill(0, $poolsize, -1);
622
        $base = \array_fill(0, $nrows, 0);
623
        $handledBases = [];
624
        $actpoolmax = 0;
625
626
        for ($ii = 0; $ii < $nrows; $ii++) {
627
            $i = $trow[$ii]->index;
628
            if (Utils::vacantRow($transit[$i], $ncols)) {
629
                $base[$i] = 0;
630
                goto ok;
631
            }
632
            for ($h = 0; $h < $ii; $h++) {
633
                if (Utils::eqRow($transit[$trow[$h]->index], $transit[$i], $ncols)) {
634
                    $base[$i] = $base[$trow[$h]->index];
635
                    goto ok;
636
                }
637
            }
638
            for ($j = 0; $j < $poolsize; $j++) {
639
                $jj = $j;
640
                $base[$i] = $j - $trow[$ii]->mini;
641
                if (!$dontcare) {
642
                    if ($base[$i] === 0) {
643
                        continue;
644
                    }
645
                    if (isset($handledBases[$base[$i]])) {
646
                        continue;
647
                    }
648
                }
649
650
                for ($k = $trow[$ii]->mini; $k < $trow[$ii]->maxi; $k++) {
651
                    if (self::VACANT !== $transit[$i][$k]) {
652
                        if ($jj >= $poolsize) {
653
                            die("Can't happen");
654
                        }
655
                        if ($check[$jj] >= 0 && !($dontcare && $actpool[$jj] === $transit[$i][$k])) {
656
                            goto next;
657
                        }
658
                    }
659
                    $jj++;
660
                }
661
                break;
662
                next:;
663
            }
664
665
            $handledBases[$base[$i]] = true;
666
            $jj = $j;
667
            for ($k = $trow[$ii]->mini; $k < $trow[$ii]->maxi; $k++) {
668
                if (self::VACANT !== $transit[$i][$k]) {
669
                    $actpool[$jj] = $transit[$i][$k];
670
                    $check[$jj] = $checkrow ? $i : $k;
671
                }
672
                $jj++;
673
            }
674
            if ($jj >= $actpoolmax) {
675
                $actpoolmax = $jj;
676
            }
677
            ok:;
678
        }
679
680
        $outtable = \array_slice($actpool, 0, $actpoolmax);
681
        $outcheck = \array_slice($check, 0, $actpoolmax);
682
        $outbase = $base;
683
    }
684
685
    /**
686
     * @param int $code
687
     *
688
     * @return int
689
     */
690
    public function encodeRederr(int $code): int
691
    {
692
        return $code < 0 ? self::UNEXPECTED : $code;
693
    }
694
695
    /**
696
     * @return void
697
     */
698
    public function resetFrequency()
699
    {
700
        $this->context->frequency = \array_fill(0, $this->context->countStates, 0);
701
    }
702
703
    /**
704
     * @param int $x
705
     * @param int $y
706
     *
707
     * @return int
708
     */
709
    public function cmpStates(int $x, int $y): int
710
    {
711
        for ($i = 0; $i < $this->context->countTerminals; $i++) {
712
            if ($this->context->termAction[$x][$i] != $this->context->termAction[$y][$i]) {
713
                return $this->context->termAction[$x][$i] - $this->context->termAction[$y][$i];
714
            }
715
        }
716
717
        return 0;
718
    }
719
720
    /**
721
     * @param Symbol $symbol
722
     *
723
     * @return int
724
     */
725
    private function nb(Symbol $symbol)
726
    {
727
        if ($symbol->isTerminal) {
728
            return $symbol->code;
729
        } else {
730
            return $symbol->code - $this->context->countTerminals;
731
        }
732
    }
733
}
734