Skip to content

Commit 3c86b71

Browse files
committed
Add ability to generate int opcodes
Signed-off-by: Jack Cherng <jfcherng@gmail.com>
1 parent 05263b1 commit 3c86b71

File tree

2 files changed

+96
-18
lines changed

2 files changed

+96
-18
lines changed

src/SequenceMatcher.php

Lines changed: 90 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,18 @@
1313
*/
1414
final class SequenceMatcher
1515
{
16-
const OPCODE_DELETE = 'del';
16+
// people may prefer this for debugging
1717
const OPCODE_EQUAL = 'eq';
18+
const OPCODE_DELETE = 'del';
1819
const OPCODE_INSERT = 'ins';
1920
const OPCODE_REPLACE = 'rep';
2021

22+
// people may prefer this for bit operations
23+
const OPCODE_INT_EQUAL = 1 << 0;
24+
const OPCODE_INT_DELETE = 1 << 1;
25+
const OPCODE_INT_INSERT = 1 << 2;
26+
const OPCODE_INT_REPLACE = 1 << 3;
27+
2128
/**
2229
* @var null|callable either a string or an array containing a callback function to determine if a line is "junk" or not
2330
*/
@@ -34,7 +41,7 @@ final class SequenceMatcher
3441
private $b = [];
3542

3643
/**
37-
* @var array Array of characters that are considered junk from the second sequence. Characters are the array key.
44+
* @var array array of characters that are considered junk from the second sequence. Characters are the array key.
3845
*/
3946
private $junkDict = [];
4047

@@ -54,6 +61,17 @@ final class SequenceMatcher
5461
private static $defaultOptions = [
5562
'ignoreWhitespace' => false,
5663
'ignoreCase' => false,
64+
'useIntOpcodes' => false,
65+
];
66+
67+
/**
68+
* @var array opcode constants
69+
*/
70+
private $ops = [
71+
// 'eq' => ?,
72+
// 'del' => ?,
73+
// 'ins' => ?,
74+
// 'rep' => ?,
5775
];
5876

5977
/**
@@ -67,7 +85,7 @@ final class SequenceMatcher
6785
private $matchingBlocks = [];
6886

6987
/**
70-
* @var array
88+
* @var array generated opcodes which manipulates seq1 to seq2
7189
*/
7290
private $opcodes = [];
7391

@@ -97,7 +115,23 @@ public function __construct(array $a, array $b, ?callable $junkCallback = null,
97115
*/
98116
public function setOptions(array $options): self
99117
{
100-
$this->options = $options + static::$defaultOptions;
118+
$this->options = $options + self::$defaultOptions;
119+
120+
$this->setOps($this->options['useIntOpcodes']);
121+
$this->resetchedResults();
122+
123+
return $this;
124+
}
125+
126+
/**
127+
* Reset cached results.
128+
*
129+
* @return self
130+
*/
131+
public function resetchedResults(): self
132+
{
133+
$this->matchingBlocks = [];
134+
$this->opcodes = [];
101135

102136
return $this;
103137
}
@@ -129,8 +163,7 @@ public function setSeq1(array $a): self
129163
{
130164
if ($this->a !== $a) {
131165
$this->a = $a;
132-
$this->matchingBlocks = [];
133-
$this->opcodes = [];
166+
$this->resetchedResults();
134167
}
135168

136169
return $this;
@@ -148,15 +181,25 @@ public function setSeq2(array $b): self
148181
{
149182
if ($this->b !== $b) {
150183
$this->b = $b;
151-
$this->matchingBlocks = [];
152-
$this->opcodes = [];
184+
$this->resetchedResults();
185+
153186
$this->fullBCount = [];
154187
$this->chainB();
155188
}
156189

157190
return $this;
158191
}
159192

193+
/**
194+
* Get the ops.
195+
*
196+
* @return array the ops
197+
*/
198+
public function getOps(): array
199+
{
200+
return $this->ops;
201+
}
202+
160203
/**
161204
* Find the longest matching block in the two sequences, as defined by the
162205
* lower and upper constraints for each sequence. (for the first sequence,
@@ -403,11 +446,11 @@ public function getOpcodes(): array
403446

404447
foreach ($this->getMatchingBlocks() as [$ai, $bj, $size]) {
405448
if ($i < $ai && $j < $bj) {
406-
$tag = static::OPCODE_REPLACE;
449+
$tag = $this->ops['rep'];
407450
} elseif ($i < $ai) {
408-
$tag = static::OPCODE_DELETE;
451+
$tag = $this->ops['del'];
409452
} elseif ($j < $bj) {
410-
$tag = static::OPCODE_INSERT;
453+
$tag = $this->ops['ins'];
411454
} else {
412455
$tag = null;
413456
}
@@ -420,7 +463,7 @@ public function getOpcodes(): array
420463
$j = $bj + $size;
421464

422465
if ($size) {
423-
$this->opcodes[] = [static::OPCODE_EQUAL, $ai, $i, $bj, $j];
466+
$this->opcodes[] = [$this->ops['eq'], $ai, $i, $bj, $j];
424467
}
425468
}
426469

@@ -448,11 +491,11 @@ public function getGroupedOpcodes(int $context = 3): array
448491

449492
if (empty($opcodes)) {
450493
$opcodes = [
451-
[static::OPCODE_EQUAL, 0, 1, 0, 1],
494+
[$this->ops['eq'], 0, 1, 0, 1],
452495
];
453496
}
454497

455-
if ($opcodes[0][0] === static::OPCODE_EQUAL) {
498+
if ($opcodes[0][0] === $this->ops['eq']) {
456499
$opcodes[0] = [
457500
$opcodes[0][0],
458501
\max($opcodes[0][1], $opcodes[0][2] - $context),
@@ -463,7 +506,7 @@ public function getGroupedOpcodes(int $context = 3): array
463506
}
464507

465508
$lastItem = \count($opcodes) - 1;
466-
if ($opcodes[$lastItem][0] === static::OPCODE_EQUAL) {
509+
if ($opcodes[$lastItem][0] === $this->ops['eq']) {
467510
[$tag, $i1, $i2, $j1, $j2] = $opcodes[$lastItem];
468511
$opcodes[$lastItem] = [
469512
$tag,
@@ -477,7 +520,7 @@ public function getGroupedOpcodes(int $context = 3): array
477520
$maxRange = $context << 1;
478521
$groups = $group = [];
479522
foreach ($opcodes as [$tag, $i1, $i2, $j1, $j2]) {
480-
if ($tag === static::OPCODE_EQUAL && $i2 - $i1 > $maxRange) {
523+
if ($tag === $this->ops['eq'] && $i2 - $i1 > $maxRange) {
481524
$group[] = [
482525
$tag,
483526
$i1,
@@ -496,7 +539,7 @@ public function getGroupedOpcodes(int $context = 3): array
496539

497540
if (
498541
!empty($group) &&
499-
(\count($group) !== 1 || $group[0][0] !== static::OPCODE_EQUAL)
542+
(\count($group) !== 1 || $group[0][0] !== $this->ops['eq'])
500543
) {
501544
$groups[] = $group;
502545
}
@@ -529,6 +572,36 @@ public function ratio(): float
529572
return $this->calculateRatio($matchesCount, \count($this->a) + \count($this->b));
530573
}
531574

575+
/**
576+
* Set the ops.
577+
*
578+
* @param bool $useIntOpcodes to use int opcodes or not
579+
*
580+
* @return self
581+
*/
582+
private function setOps(bool $useIntOpcodes): self
583+
{
584+
if ($useIntOpcodes) {
585+
$this->ops = [
586+
'del' => self::OPCODE_INT_DELETE,
587+
'eq' => self::OPCODE_INT_EQUAL,
588+
'ins' => self::OPCODE_INT_INSERT,
589+
'rep' => self::OPCODE_INT_REPLACE,
590+
];
591+
} else {
592+
$this->ops = [
593+
'del' => self::OPCODE_DELETE,
594+
'eq' => self::OPCODE_EQUAL,
595+
'ins' => self::OPCODE_INSERT,
596+
'rep' => self::OPCODE_REPLACE,
597+
];
598+
}
599+
600+
$this->resetchedResults();
601+
602+
return $this;
603+
}
604+
532605
/**
533606
* Generate the internal arrays containing the list of junk and non-junk
534607
* characters for the second ($b) sequence.

tests/SequenceMatcherTest.php

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,12 @@ class SequenceMatcherTest extends TestCase
2424
*/
2525
protected function setUp(): void
2626
{
27-
$this->sm = new SequenceMatcher([], []);
27+
$this->sm = new SequenceMatcher(
28+
[],
29+
[],
30+
null,
31+
['useIntOpcodes' => false]
32+
);
2833
}
2934

3035
/**

0 commit comments

Comments
 (0)