Skip to content

Commit 324bfb5

Browse files
authored
Refactoring (#356)
1 parent 0706c2e commit 324bfb5

25 files changed

+703
-166
lines changed

automata/automata.go

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,14 @@ func cmpStates(a, b States) int {
6464
return 1
6565
}
6666

67-
// Assume a and b are sorted.
6867
lhs := generic.Collect1(a.All())
6968
rhs := generic.Collect1(b.All())
7069

70+
// Sorting is not needed since the sets are sorted.
71+
// Uncomment if you switch to unsorted sets.
72+
// sort.Quick(lhs, CmpState)
73+
// sort.Quick(rhs, CmpState)
74+
7175
for i := 0; i < len(lhs) && i < len(rhs); i++ {
7276
if c := CmpState(lhs[i], rhs[i]); c != 0 {
7377
return c
@@ -126,10 +130,14 @@ func cmpSymbols(a, b Symbols) int {
126130
return 1
127131
}
128132

129-
// Assume a and b are sorted.
130133
lhs := generic.Collect1(a.All())
131134
rhs := generic.Collect1(b.All())
132135

136+
// Sorting is not needed since the sets are sorted.
137+
// Uncomment if you switch to unsorted sets.
138+
// sort.Quick(lhs, CmpSymbol)
139+
// sort.Quick(rhs, CmpSymbol)
140+
133141
for i := 0; i < len(lhs) && i < len(rhs); i++ {
134142
if c := CmpSymbol(lhs[i], rhs[i]); c != 0 {
135143
return c

automata/common.go

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,18 +12,16 @@ import (
1212
"github.com/moorara/algo/symboltable"
1313
)
1414

15-
func formatRangeSlice(rs []disc.Range[Symbol]) string {
16-
var b bytes.Buffer
15+
func hashRange(r disc.Range[Symbol]) uint64 {
16+
var hash uint64
1717

18-
for _, r := range rs {
19-
fmt.Fprintf(&b, "%s, ", formatRange(r))
20-
}
18+
// Use a polynomial rolling hash to combine the individual hashes.
19+
const B = 0x9E3779B185EBCA87
2120

22-
if b.Len() >= 2 {
23-
b.Truncate(b.Len() - 2)
24-
}
21+
hash = hash*B + HashSymbol(r.Lo)
22+
hash = hash*B + HashSymbol(r.Hi)
2523

26-
return b.String()
24+
return hash
2725
}
2826

2927
func formatRange(r disc.Range[Symbol]) string {
@@ -53,18 +51,26 @@ func formatRangeBound(a Symbol) string {
5351
}
5452
}
5553

54+
func formatRangeSlice(rs []disc.Range[Symbol]) string {
55+
var b bytes.Buffer
56+
57+
for _, r := range rs {
58+
fmt.Fprintf(&b, "%s, ", formatRange(r))
59+
}
60+
61+
if b.Len() >= 2 {
62+
b.Truncate(b.Len() - 2)
63+
}
64+
65+
return b.String()
66+
}
67+
5668
// rangeSet represents a set of symbol ranges.
5769
type rangeSet set.Set[disc.Range[Symbol]]
5870

5971
// newRangeSet creates a new set of symbol ranges.
6072
func newRangeSet(rs ...disc.Range[Symbol]) rangeSet {
61-
return set.NewStableSetWithFormat(
62-
func(a, b disc.Range[Symbol]) bool {
63-
return a.Lo == b.Lo && a.Hi == b.Hi
64-
},
65-
formatRangeSlice,
66-
rs...,
67-
)
73+
return set.NewSortedSetWithFormat(disc.CmpRange[Symbol], formatRangeSlice, rs...)
6874
}
6975

7076
// rangeList represents a list of symbol ranges.

automata/common_test.go

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,37 @@ import (
99
"github.com/moorara/algo/range/disc"
1010
)
1111

12-
func TestFormatRangeSlice(t *testing.T) {
12+
func TestHashRange(t *testing.T) {
1313
tests := []struct {
14-
name string
15-
rs []disc.Range[Symbol]
16-
expectedString string
14+
name string
15+
r disc.Range[Symbol]
16+
expectedHash uint64
1717
}{
1818
{
19-
name: "OK",
20-
rs: []disc.Range[Symbol]{
21-
{Lo: '0', Hi: '9'},
22-
{Lo: 'A', Hi: 'F'},
23-
{Lo: 'a', Hi: 'f'},
24-
},
25-
expectedString: "[0..9], [A..F], [a..f]",
19+
name: "Empty",
20+
r: disc.Range[Symbol]{Lo: E, Hi: E},
21+
expectedHash: 0x51174ad049a677c8,
22+
},
23+
{
24+
name: "Zero",
25+
r: disc.Range[Symbol]{Lo: 0, Hi: 0},
26+
expectedHash: 0xa2172c400c20ec28,
27+
},
28+
{
29+
name: "Digit",
30+
r: disc.Range[Symbol]{Lo: '0', Hi: '9'},
31+
expectedHash: 0x26d68f6462ac85a5,
32+
},
33+
{
34+
name: "Letter",
35+
r: disc.Range[Symbol]{Lo: 'A', Hi: 'Z'},
36+
expectedHash: 0xf18c054b3e7f9e9d,
2637
},
2738
}
2839

2940
for _, tc := range tests {
3041
t.Run(tc.name, func(t *testing.T) {
31-
assert.Equal(t, tc.expectedString, formatRangeSlice(tc.rs))
42+
assert.Equal(t, tc.expectedHash, hashRange(tc.r))
3243
})
3344
}
3445
}
@@ -124,6 +135,30 @@ func TestNewRangeSet(t *testing.T) {
124135
}
125136
}
126137

138+
func TestFormatRangeSlice(t *testing.T) {
139+
tests := []struct {
140+
name string
141+
rs []disc.Range[Symbol]
142+
expectedString string
143+
}{
144+
{
145+
name: "OK",
146+
rs: []disc.Range[Symbol]{
147+
{Lo: '0', Hi: '9'},
148+
{Lo: 'A', Hi: 'F'},
149+
{Lo: 'a', Hi: 'f'},
150+
},
151+
expectedString: "[0..9], [A..F], [a..f]",
152+
},
153+
}
154+
155+
for _, tc := range tests {
156+
t.Run(tc.name, func(t *testing.T) {
157+
assert.Equal(t, tc.expectedString, formatRangeSlice(tc.rs))
158+
})
159+
}
160+
}
161+
127162
func TestNewRangeList(t *testing.T) {
128163
tests := []struct {
129164
name string

automata/dfa.go

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ type dfaTransitionEnds struct {
2323
Next State
2424
}
2525

26-
var cmpDFATransitionEnds = func(lhs, rhs dfaTransitionEnds) int {
26+
func cmpDFATransitionEnds(lhs, rhs dfaTransitionEnds) int {
2727
if c := CmpState(lhs.State, rhs.State); c != 0 {
2828
return c
2929
}
@@ -51,7 +51,7 @@ func newDFATransitionVector() dfaTransitionVector {
5151
return set.NewSortedSet(cmpDFATransitionEnds)
5252
}
5353

54-
var cmpDFATransitionVector = func(lhs, rhs dfaTransitionVector) int {
54+
func cmpDFATransitionVector(lhs, rhs dfaTransitionVector) int {
5555
v1 := generic.Collect1(lhs.All())
5656
v2 := generic.Collect1(rhs.All())
5757

@@ -66,7 +66,7 @@ var cmpDFATransitionVector = func(lhs, rhs dfaTransitionVector) int {
6666

6767
/* ------------------------------------------------------------------------------------------------------------------------ */
6868

69-
var eqClassIDStateTable = func(a, b symboltable.SymbolTable[classID, State]) bool {
69+
func eqClassIDStateTable(a, b symboltable.SymbolTable[classID, State]) bool {
7070
if a == nil && b == nil {
7171
return true
7272
}
@@ -250,8 +250,8 @@ func (b *DFABuilder) Build() *DFA {
250250
ranges.Add(sub.Key, cid)
251251

252252
// Build class-based transitions for the current range and its transitions.
253-
for ends := range sub.Val.All() {
254-
transitions.Add(ends.State, cid, ends.Next)
253+
for e := range sub.Val.All() {
254+
transitions.Add(e.State, cid, e.Next)
255255
}
256256
}
257257

@@ -284,6 +284,7 @@ type DFA struct {
284284
trans *dfaTransitionTable
285285

286286
// Derived values (computed lazily)
287+
_final []State
287288
_states []State
288289
_symbols []disc.Range[Symbol]
289290
_classes classMapping
@@ -296,7 +297,7 @@ func (d *DFA) String() string {
296297
fmt.Fprintf(&b, "Start state: %d\n", d.start)
297298
fmt.Fprintf(&b, "Final states: ")
298299

299-
for s := range d.final.All() {
300+
for _, s := range d.Final() {
300301
fmt.Fprintf(&b, "%d, ", s)
301302
}
302303

@@ -439,7 +440,7 @@ func (d *DFA) getSortedDegreeSequence() []int {
439440
sortedDegrees[i] = degree
440441
}
441442

442-
sort.Quick3Way[int](sortedDegrees, generic.NewCompareFunc[int]())
443+
sort.Quick3Way(sortedDegrees, generic.NewCompareFunc[int]())
443444

444445
return sortedDegrees
445446
}
@@ -451,7 +452,12 @@ func (d *DFA) Start() State {
451452

452453
// Final returns the final (accepting) states of the DFA.
453454
func (d *DFA) Final() []State {
454-
return generic.Collect1(d.final.All())
455+
if d._final == nil {
456+
d._final = generic.Collect1(d.final.All())
457+
// sort.Quick(d._final, CmpState)
458+
}
459+
460+
return d._final
455461
}
456462

457463
// States returns all states in the DFA.
@@ -467,6 +473,7 @@ func (d *DFA) States() []State {
467473
}
468474

469475
d._states = generic.Collect1(states.All())
476+
// sort.Quick(d._states, CmpState)
470477
}
471478

472479
return d._states
@@ -595,8 +602,11 @@ func (d *DFA) Minimize() *DFA {
595602
for {
596603
Πnew := newPartition()
597604

605+
groups := generic.Collect1(Π.groups.All())
606+
sort.Quick(groups, cmpGroup)
607+
598608
// For every group in the current partition
599-
for G := range Π.groups.All() {
609+
for _, G := range groups {
600610
Gtrans := buildGroupTransitions(d, Π, G)
601611
partitionGroup(Πnew, Gtrans)
602612
}
@@ -623,15 +633,18 @@ func (d *DFA) Minimize() *DFA {
623633
start := Π.FindRep(d.start)
624634

625635
final := NewStates()
626-
for f := range d.final.All() {
636+
for _, f := range d.Final() {
627637
g := Π.FindRep(f)
628638
final.Add(g)
629639
}
630640

631641
b := NewDFABuilder().SetStart(start)
632642
b.final = final
633643

634-
for G := range Π.groups.All() {
644+
groups := generic.Collect1(Π.groups.All())
645+
sort.Quick(groups, cmpGroup)
646+
647+
for _, G := range groups {
635648
// Get any state in the group
636649
s, _ := G.States.FirstMatch(func(State) bool {
637650
return true
@@ -981,7 +994,9 @@ func UnionDFA(ds ...*DFA) (*DFA, [][]State) {
981994
}
982995
}
983996
}
997+
984998
finalMap[id] = generic.Collect1(mapped.All())
999+
sort.Quick(finalMap[id], CmpState)
9851000
}
9861001
}
9871002

@@ -1046,7 +1061,9 @@ func UnionDFA(ds ...*DFA) (*DFA, [][]State) {
10461061
ff := sm.GetOrCreateState(0, f)
10471062
mapped.Add(ff)
10481063
}
1064+
10491065
finalMap[id] = generic.Collect1(mapped.All())
1066+
sort.Quick(finalMap[id], CmpState)
10501067
}
10511068
}
10521069

0 commit comments

Comments
 (0)