Passed
Push — master ( 77aa03...453f7d )
by Nikita
02:16
created

Compress::computePreImages()   C

Complexity

Conditions 8
Paths 24

Size

Total Lines 32
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 32
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 19
nc 24
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\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
        for ($i = 0; $i < $this->context->countStates;) {
77
            $p = $preImages[$i];
78
            $this->context->prims[$this->context->countPrims] = $p;
79
            for (; $i < $this->context->countStates && PreImage::compare($p, $preImages[$i]) === 0; $i++) {
80
                $this->context->primof[$preImages[$i]->index] = $p;
81
            }
82
            $p->index = $this->context->countPrims++;
83
        }
84
    }
85
86
    /**
87
     * @param array $t
88
     * @param int   $count
89
     *
90
     * @return array
91
     */
92
    private function encodeShiftReduce(array $t, int $count): array
93
    {
94
        for ($i = 0; $i < $count; $i++) {
95
            if ($t[$i] >= $this->context->countNonLeafStates) {
96
                $t[$i] = $this->context->countNonLeafStates + $this->context->defaultAct[$t[$i]];
97
            }
98
        }
99
100
        return $t;
101
    }
102
103
    /**
104
     * @return void
105
     */
106
    private function makeupTable2()
107
    {
108
        $this->context->termAction = \array_fill(0, $this->context->countNonLeafStates, 0);
109
        $this->context->classAction = \array_fill(0, $this->context->countNonLeafStates * 2, 0);
110
        $this->context->nonTermGoto = \array_fill(0, $this->context->countNonLeafStates, 0);
111
        $this->context->defaultAct = \array_fill(0, $this->context->countStates, 0);
112
        $this->context->defaultGoto = \array_fill(0, $this->context->countNonTerminals, 0);
113
114
        $this->resetFrequency();
115
        $this->context->stateImageSorted = \array_fill(0, $this->context->countNonLeafStates, 0);
116
        $this->context->classOf = \array_fill(0, $this->context->countStates, 0);
117
118
        for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
119
            $this->context->termAction[$i] = \array_fill(0, $this->context->countTerminals, self::VACANT);
120
            $this->context->nonTermGoto[$i] = \array_fill(0, $this->context->countNonTerminals, self::VACANT);
121
122
            foreach ($this->context->states[$i]->shifts as $shift) {
123
                if ($shift->through->isTerminal) {
124
                    $this->context->termAction[$i][$shift->through->code] = $shift->number;
125
                } else {
126
                    $this->context->nonTermGoto[$i][$this->nb($shift->through)] = $shift->number;
127
                }
128
            }
129
            foreach ($this->context->states[$i]->reduce as $reduce) {
130
                if ($reduce->symbol->isNilSymbol()) {
131
                    break;
132
                }
133
                $this->context->termAction[$i][$reduce->symbol->code] = -$this->encodeRederr($reduce->number);
134
            }
135
            $this->context->stateImageSorted[$i] = $i;
136
        }
137
138
        foreach ($this->context->states as $key => $state) {
139
            $r = null;
140
            foreach ($state->reduce as $r) {
141
                if ($r->symbol->isNilSymbol()) {
142
                    break;
143
                }
144
            }
145
            $this->context->defaultAct[$key] = $this->encodeRederr($r->number);
146
        }
147
148
        for ($j = 0; $j < $this->context->countNonTerminals; $j++) {
149
            $max = 0;
150
            $maxst = self::VACANT;
151
            $this->resetFrequency();
152
153
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
154
                $st = $this->context->nonTermGoto[$i][$j];
155
                if ($st > 0) {
156
                    $this->context->frequency[$st]++;
157
                    if ($this->context->frequency[$st] > $max) {
158
                        $max = $this->context->frequency[$st];
159
                        $maxst = $st;
160
                    }
161
                }
162
            }
163
            $this->context->defaultGoto[$j] = $maxst;
164
        }
165
        // 847
166
167
        Utils::stableSort($this->context->stateImageSorted, [$this, 'cmpStates']);
168
169
        $j = 0;
170
171
        for ($i = 0; $i < $this->context->countNonLeafStates;) {
172
            $k = $this->context->stateImageSorted[$i];
173
            $this->context->classAction[$j] = $this->context->termAction[$k];
174
            for (; $i < $this->context->countNonLeafStates && $this->cmpStates($this->context->stateImageSorted[$i], $k) === 0; $i++) {
175
                $this->context->classOf[$this->context->stateImageSorted[$i]] = $j;
176
            }
177
            $j++;
178
        }
179
        $this->context->countClasses = $j;
180
181
        if ($this->context->debug) {
182
            $this->context->debug("State=>class:\n");
183
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
184
                if ($i % 10 === 0) {
185
                    $this->context->debug("\n");
186
                }
187
                $this->context->debug(\sprintf('%3d=>%-3d ', $i, $this->context->classOf[$i]));
188
            }
189
            $this->context->debug("\n");
190
        }
191
192
        $this->computePreImages();
193
194
        if ($this->context->debug) {
195
            $this->printTable();
196
        }
197
198
        $this->extractCommon();
199
200
        $this->authodoxTable();
201
    }
202
203
    /**
204
     * @return void
205
     */
206
    private function printTable()
207
    {
208
        $this->context->debug("\nTerminal action:\n");
209
        $this->context->debug(\sprintf('%8.8s', 'T\\S'));
210
        for ($i = 0; $i < $this->context->countClasses; $i++) {
211
            $this->context->debug(\sprintf('%4d', $i));
212
        }
213
        $this->context->debug("\n");
214
        for ($j = 0; $j < $this->context->countTerminals; $j++) {
215
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
216
                if (self::VACANT !== $this->context->termAction[$i][$j]) {
217
                    break;
218
                }
219
            }
220 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...
221
                $this->context->debug(\sprintf('%8.8s', $this->context->symbol($j)->name));
222
                for ($i = 0; $i < $this->context->countClasses; $i++) {
223
                    $this->context->debug(Utils::printAction($this->context->classAction[$i][$j]));
224
                }
225
                $this->context->debug("\n");
226
            }
227
        }
228
229
        $this->context->debug("\nNonterminal GOTO table:\n");
230
        $this->context->debug(\sprintf('%8.8s', 'T\\S'));
231 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...
232
            $this->context->debug(\sprintf('%4d', $i));
233
        }
234
        $this->context->debug("\n");
235
        foreach ($this->context->nonterminals as $symbol) {
236
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
237
                if ($this->context->nonTermGoto[$i][$this->nb($symbol)] > 0) {
238
                    break;
239
                }
240
            }
241 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...
242
                $this->context->debug(\sprintf('%8.8s', $symbol->name));
243
                for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
244
                    $this->context->debug(Utils::printAction($this->context->nonTermGoto[$i][$this->nb($symbol)]));
245
                }
246
                $this->context->debug("\n");
247
            }
248
        }
249
250
        $this->context->debug("\nNonterminal GOTO table:\n");
251
        $this->context->debug(\sprintf('%8.8s default', 'T\\S'));
252 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...
253
            $this->context->debug(\sprintf('%4d', $i));
254
        }
255
        $this->context->debug("\n");
256
        foreach ($this->context->nonterminals as $symbol) {
257
            $nb = $this->nb($symbol);
258
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
259
                if ($this->context->nonTermGoto[$i][$nb] > 0) {
260
                    break;
261
                }
262
            }
263
            if ($i < $this->context->countNonLeafStates) {
264
                $this->context->debug(\sprintf('%8.8s', $symbol->name));
265
                $this->context->debug(\sprintf('%8d', $this->context->defaultGoto[$nb]));
266
                for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
267
                    if ($this->context->nonTermGoto[$i][$nb] === $this->context->defaultGoto[$nb]) {
268
                        $this->context->debug('  = ');
269
                    } else {
270
                        $this->context->debug(Utils::printAction($this->context->nonTermGoto[$i][$nb]));
271
                    }
272
                }
273
                $this->context->debug("\n");
274
            }
275
        }
276
    }
277
278
    /**
279
     * @return void
280
     */
281
    private function extractCommon()
282
    {
283
        $this->context->class2nd = \array_fill(0, $this->context->countClasses, -1);
284
285
        $auxList = null;
286
        $n = 0;
287
288
        for ($i = 0; $i < $this->context->countPrims; $i++) {
289
            $preImage = $this->context->prims[$i];
290
            if ($preImage->length < 2) {
291
                continue;
292
            }
293
            $p = new Auxiliary();
294
            $this->bestCovering($p, $preImage);
295
            if ($p->gain < 1) {
296
                continue;
297
            }
298
            $p->preImage = $preImage;
299
            $p->next = $auxList;
300
            $auxList = $p;
301
            $n++;
302
        }
303
304
        if ($this->context->debug) {
305
            $this->context->debug("\nNumber of prims: {$this->context->countPrims}\n");
306
            $this->context->debug("\nCandidates of aux table:\n");
307
            for ($p = $auxList; $p !== null; $p = $p->next) {
308
                $this->context->debug(\sprintf('Aux = (%d) ', $p->gain));
309
                $f = 0;
310 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...
311
                    if (self::VACANT !== $p->table[$j]) {
312
                        $this->context->debug(\sprintf($f++ ? ',%d' : '%d', $p->table[$j]));
313
                    }
314
                }
315
                $this->context->debug(' * ');
316
                for ($j = 0; $j < $p->preImage->length; $j++) {
317
                    $this->context->debug(\sprintf($j ? ',%d' : '%d', $p->preImage->classes[$j]));
318
                }
319
                $this->context->debug("\n");
320
            }
321
            $this->context->debug("Used aux table:\n");
322
        }
323
        $this->context->countAux = $this->context->countClasses;
324
        for (;;) {
325
            $maxGain = 0;
326
            $maxAux = null;
327
            $pre = null;
328
            $maxPreImage = null;
329
            for ($p = $auxList; $p != null; $p = $p->next) {
330
                if ($p->gain > $maxGain) {
331
                    $maxGain = $p->gain;
332
                    $maxAux = $p;
333
                    $maxPreImage = $pre;
334
                }
335
                /** @var Auxiliary $pre */
336
                $pre = $p;
337
            }
338
339
            if ($maxAux === null) {
340
                break;
341
            }
342
343
            if ($maxPreImage) {
344
                $maxPreImage->next = $maxAux->next;
345
            } else {
346
                $auxList = $maxAux->next;
347
            }
348
349
            $maxAux->index = $this->context->countAux;
0 ignored issues
show
Bug introduced by
The property index does not seem to exist in PhpYacc\Compress\Auxiliary.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
350
351
            for ($j = 0; $j < $maxAux->preImage->length; $j++) {
352
                $cl = $maxAux->preImage->classes[$j];
353
                if (Utils::eqRow($this->context->classAction[$cl], $maxAux->table, $this->context->countTerminals)) {
354
                    $maxAux->index = $cl;
355
                }
356
            }
357
358
            if ($maxAux->index >= $this->context->countAux) {
359
                $this->context->classAction[$this->context->countAux++] = $maxAux->table;
360
            }
361
362
            for ($j = 0; $j < $maxAux->preImage->length; $j++) {
363
                $cl = $maxAux->preImage->classes[$j];
364
                if ($this->context->class2nd[$cl] < 0) {
365
                    $this->context->class2nd[$cl] = $maxAux->index;
366
                }
367
            }
368
369
            if ($this->context->debug) {
370
                $this->context->debug(\sprintf('Selected aux[%d]: (%d) ', $maxAux->index, $maxAux->gain));
371
                $f = 0;
372 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...
373
                    if (self::VACANT !== $maxAux->table[$j]) {
374
                        $this->context->debug(\sprintf($f++ ? ',%d' : '%d', $maxAux->table[$j]));
375
                    }
376
                }
377
                $this->context->debug(' * ');
378
                $f = 0;
379
                for ($j = 0; $j < $maxAux->preImage->length; $j++) {
380
                    $cl = $maxAux->preImage->classes[$j];
381
                    if ($this->context->class2nd[$cl] === $maxAux->index) {
382
                        $this->context->debug(\sprintf($f++ ? ',%d' : '%d', $cl));
383
                    }
384
                }
385
                $this->context->debug("\n");
386
            }
387
388
            for ($p = $auxList; $p != null; $p = $p->next) {
389
                $this->bestCovering($p, $p->preImage);
390
            }
391
        }
392
393
        if ($this->context->debug) {
394
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
395
                if ($this->context->class2nd[$this->context->classOf[$i]] >= 0 && $this->context->class2nd[$this->context->classOf[$i]] !== $this->context->classOf[$i]) {
396
                    $this->context->debug(\sprintf("state %d (class %d): aux[%d]\n", $i, $this->context->classOf[$i], $this->context->class2nd[$this->context->classOf[$i]]));
397
                } else {
398
                    $this->context->debug(\sprintf("state %d (class %d)\n", $i, $this->context->classOf[$i]));
399
                }
400
            }
401
        }
402
    }
403
404
    /**
405
     * @param Auxiliary $aux
406
     * @param PreImage  $prim
407
     */
408
    private function bestCovering(Auxiliary $aux, PreImage $prim)
409
    {
410
        $this->resetFrequency();
411
        $gain = 0;
412
        for ($i = 0; $i < $this->context->countTerminals; $i++) {
413
            $max = 0;
414
            $maxAction = -1;
415
            $countVacant = 0;
416
417
            for ($j = 0; $j < $prim->length; $j++) {
418
                if ($this->context->class2nd[$prim->classes[$j]] < 0) {
419
                    $c = $this->context->classAction[$prim->classes[$j]][$i];
420
                    if ($c > 0 && ++$this->context->frequency[$c] > $max) {
421
                        $maxAction = $c;
422
                        $max = $this->context->frequency[$c];
423
                    } elseif (self::VACANT === $c) {
424
                        $countVacant++;
425
                    }
426
                }
427
            }
428
429
            $n = $max - 1 - $countVacant;
430
431
            if ($n > 0) {
432
                $aux->table[$i] = $maxAction;
433
                $gain += $n;
434
            } else {
435
                $aux->table[$i] = self::VACANT;
436
            }
437
        }
438
        $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...
439
    }
440
441
    private function authodoxTable()
442
    {
443
        // TODO
444
        $this->context->cTermIndex = \array_fill(0, $this->context->countTerminals, -1);
445
        $this->context->oTermIndex = \array_fill(0, $this->context->countTerminals, 0);
446
447
        $countCTerms = 0;
448
        for ($j = 0; $j < $this->context->countTerminals; $j++) {
449
            if ($j === $this->context->errorToken->code) {
450
                $this->context->cTermIndex[$j] = $countCTerms;
451
                $this->context->oTermIndex[$countCTerms++] = $j;
452
                continue;
453
            }
454
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
455
                if ($this->context->termAction[$i][$j] !== self::VACANT) {
456
                    $this->context->cTermIndex[$j] = $countCTerms;
457
                    $this->context->oTermIndex[$countCTerms++] = $j;
458
                    break;
459
                }
460
            }
461
        }
462
463
        $cTermAction = \array_fill(
464
            0, $this->context->countAux, \array_fill(0, $countCTerms, 0)
465
        );
466
467 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...
468
            for ($j = 0; $j < $countCTerms; $j++) {
469
                $cTermAction[$i][$j] = $this->context->classAction[$i][$this->context->oTermIndex[$j]];
470
            }
471
        }
472
473
        //582
474
475
        for ($i = 0; $i < $this->context->countClasses; $i++) {
476
            if ($this->context->class2nd[$i] >= 0 && $this->context->class2nd[$i] != $i) {
477
                $table = $this->context->classAction[$this->context->class2nd[$i]];
478
                for ($j = 0; $j < $countCTerms; $j++) {
479
                    if (self::VACANT !== $table[$this->context->oTermIndex[$j]]) {
480
                        if ($cTermAction[$i][$j] === $table[$this->context->oTermIndex[$j]]) {
481
                            $cTermAction[$i][$j] = self::VACANT;
482
                        } elseif ($cTermAction[$i][$j] === self::VACANT) {
483
                            $cTermAction[$i][$j] = self::DEFAULT;
484
                        }
485
                    }
486
                }
487
            }
488
        }
489
490 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...
491
            for ($j = 0; $j < $countCTerms; $j++) {
492
                $cTermAction[$i][$j] = $this->context->classAction[$i][$this->context->oTermIndex[$j]];
493
            }
494
        }
495
        $base = [];
496
        $this->packTable(
497
            $cTermAction, $this->context->countAux, $countCTerms, false, false,
498
            $this->result->yyaction, $this->result->yycheck, $base
499
        );
500
        $this->result->yydefault = $this->context->defaultAct;
501
502
        $this->result->yybase = \array_fill(0, $this->context->countNonLeafStates * 2, 0);
503
        $this->result->yybasesize = $this->context->countNonLeafStates;
504
        for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
505
            $cl = $this->context->classOf[$i];
506
            $this->result->yybase[$i] = $base[$cl];
507
            if ($this->context->class2nd[$cl] >= 0 && $this->context->class2nd[$cl] != $cl) {
508
                $this->result->yybase[$i + $this->context->countNonLeafStates] = $base[$this->context->class2nd[$cl]];
509
                if ($i + $this->context->countNonLeafStates + 1 > $this->result->yybasesize) {
510
                    $this->result->yybasesize = $i + $this->context->countNonLeafStates + 1;
511
                }
512
            }
513
        }
514
515
        $this->result->yybase = \array_slice($this->result->yybase, 0, $this->result->yybasesize);
516
517
        //642
518
        $nonTermTransposed = \array_fill(0, $this->context->countNonTerminals, array_fill(0, $this->context->countNonLeafStates, 0));
519
        foreach ($nonTermTransposed as $j => $_dump) {
520
            for ($i = 0; $i < $this->context->countNonLeafStates; $i++) {
521
                $nonTermTransposed[$j][$i] = $this->context->nonTermGoto[$i][$j];
522
                if ($this->context->nonTermGoto[$i][$j] === $this->context->defaultGoto[$j]) {
523
                    $nonTermTransposed[$j][$i] = self::VACANT;
524
                }
525
            }
526
        }
527
528
        $this->packTable(
529
            $nonTermTransposed, $this->context->countNonTerminals, $this->context->countNonLeafStates,
530
            false, true, $this->result->yygoto, $this->result->yygcheck, $this->result->yygbase
531
        );
532
533
        $this->result->yygdefault = $this->context->defaultGoto;
534
535
        $this->result->yylhs = [];
536
        $this->result->yylen = [];
537
        foreach ($this->context->grams as $gram) {
538
            $this->result->yylhs[] = $this->nb($gram->body[0]);
539
            $this->result->yylen[] = \count($gram->body) - 1;
540
        }
541
542
        $this->result->yytranslatesize = 0;
543
544
        foreach ($this->context->terminals as $term) {
545
            $value = $term->value;
546
            if ($value + 1 > $this->result->yytranslatesize) {
547
                $this->result->yytranslatesize = $value + 1;
548
            }
549
        }
550
551
        $this->result->yytranslate = \array_fill(0, $this->result->yytranslatesize, $countCTerms);
552
        $this->result->yyncterms = $countCTerms;
553
554
        for ($i = 0; $i < $this->context->countTerminals; $i++) {
555
            if ($this->context->cTermIndex[$i] >= 0) {
556
                $symbol = $this->context->symbol($i);
557
                $this->result->yytranslate[$symbol->value] = $this->context->cTermIndex[$i];
558
            }
559
        }
560
561
        $this->result->yyaction = $this->encodeShiftReduce($this->result->yyaction, \count($this->result->yyaction));
562
        $this->result->yygoto = $this->encodeShiftReduce($this->result->yygoto, \count($this->result->yygoto));
563
        $this->result->yygdefault = $this->encodeShiftReduce($this->result->yygdefault, $this->context->countNonTerminals);
564
    }
565
566
    /**
567
     * @param array $transit
568
     * @param int   $nrows
569
     * @param int   $ncols
570
     * @param bool  $dontcare
571
     * @param bool  $checkrow
572
     * @param array $outtable
573
     * @param array $outcheck
574
     * @param array $outbase
575
     */
576
    private function packTable(array $transit, int $nrows, int $ncols, bool $dontcare, bool $checkrow, array &$outtable, array &$outcheck, array &$outbase)
577
    {
578
        $trow = [];
579
        for ($i = 0; $i < $nrows; $i++) {
580
            $trow[] = $p = new TRow($i);
581
            for ($j = 0; $j < $ncols; $j++) {
582
                if (self::VACANT !== $transit[$i][$j]) {
583
                    if ($p->mini < 0) {
584
                        $p->mini = $j;
585
                    }
586
                    $p->maxi = $j + 1;
587
                    $p->nent++;
588
                }
589
            }
590
            if ($p->mini < 0) {
591
                $p->mini = 0;
592
            }
593
        }
594
595
        Utils::stableSort($trow, [TRow::class, 'compare']);
596
597
        if ($this->context->debug) {
598
            $this->context->debug("Order:\n");
599
            for ($i = 0; $i < $nrows; $i++) {
600
                $this->context->debug(sprintf('%d,', $trow[$i]->index));
601
            }
602
            $this->context->debug("\n");
603
        }
604
605
        $poolsize = $nrows * $ncols;
606
        $actpool = \array_fill(0, $poolsize, 0);
607
        $check = \array_fill(0, $poolsize, -1);
608
        $base = \array_fill(0, $nrows, 0);
609
        $handledBases = [];
610
        $actpoolmax = 0;
611
612
        for ($ii = 0; $ii < $nrows; $ii++) {
613
            $i = $trow[$ii]->index;
614
            if (Utils::vacantRow($transit[$i], $ncols)) {
615
                $base[$i] = 0;
616
                goto ok;
617
            }
618
            for ($h = 0; $h < $ii; $h++) {
619
                if (Utils::eqRow($transit[$trow[$h]->index], $transit[$i], $ncols)) {
620
                    $base[$i] = $base[$trow[$h]->index];
621
                    goto ok;
622
                }
623
            }
624
            for ($j = 0; $j < $poolsize; $j++) {
625
                $jj = $j;
626
                $base[$i] = $j - $trow[$ii]->mini;
627
                if (!$dontcare) {
628
                    if ($base[$i] === 0) {
629
                        continue;
630
                    }
631
                    if (isset($handledBases[$base[$i]])) {
632
                        continue;
633
                    }
634
                }
635
636
                for ($k = $trow[$ii]->mini; $k < $trow[$ii]->maxi; $k++) {
637
                    if (self::VACANT !== $transit[$i][$k]) {
638
                        if ($jj >= $poolsize) {
639
                            die("Can't happen");
640
                        }
641
                        if ($check[$jj] >= 0 && !($dontcare && $actpool[$jj] === $transit[$i][$k])) {
642
                            goto next;
643
                        }
644
                    }
645
                    $jj++;
646
                }
647
                break;
648
                next:;
649
            }
650
651
            $handledBases[$base[$i]] = true;
652
            $jj = $j;
653
            for ($k = $trow[$ii]->mini; $k < $trow[$ii]->maxi; $k++) {
654
                if (self::VACANT !== $transit[$i][$k]) {
655
                    $actpool[$jj] = $transit[$i][$k];
656
                    $check[$jj] = $checkrow ? $i : $k;
657
                }
658
                $jj++;
659
            }
660
            if ($jj >= $actpoolmax) {
661
                $actpoolmax = $jj;
662
            }
663
            ok:;
664
        }
665
666
        $outtable = \array_slice($actpool, 0, $actpoolmax);
667
        $outcheck = \array_slice($check, 0, $actpoolmax);
668
        $outbase = $base;
669
    }
670
671
    /**
672
     * @param int $code
673
     *
674
     * @return int
675
     */
676
    public function encodeRederr(int $code): int
677
    {
678
        return $code < 0 ? self::UNEXPECTED : $code;
679
    }
680
681
    /**
682
     * @return void
683
     */
684
    public function resetFrequency()
685
    {
686
        $this->context->frequency = \array_fill(0, $this->context->countStates, 0);
687
    }
688
689
    /**
690
     * @param int $x
691
     * @param int $y
692
     *
693
     * @return int
694
     */
695
    public function cmpStates(int $x, int $y): int
696
    {
697
        for ($i = 0; $i < $this->context->countTerminals; $i++) {
698
            if ($this->context->termAction[$x][$i] != $this->context->termAction[$y][$i]) {
699
                return $this->context->termAction[$x][$i] - $this->context->termAction[$y][$i];
700
            }
701
        }
702
703
        return 0;
704
    }
705
706
    /**
707
     * @param Symbol $symbol
708
     *
709
     * @return int
710
     */
711
    private function nb(Symbol $symbol)
712
    {
713
        if ($symbol->isTerminal) {
714
            return $symbol->code;
715
        } else {
716
            return $symbol->code - $this->context->countTerminals;
717
        }
718
    }
719
}
720