aboutsummaryrefslogtreecommitdiff
path: root/grammar/lexical/dfa/dfa_test.go
blob: ae71875611ca96783671011801058cfb2737d3a6 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package dfa

import (
	"strings"
	"testing"

	"github.com/nihei9/vartan/grammar/lexical/parser"
	spec "github.com/nihei9/vartan/spec/grammar"
)

func TestGenDFA(t *testing.T) {
	p := parser.NewParser(spec.LexKindName("test"), strings.NewReader("(a|b)*abb"))
	cpt, err := p.Parse()
	if err != nil {
		t.Fatal(err)
	}
	bt, symTab, err := ConvertCPTreeToByteTree(map[spec.LexModeKindID]parser.CPTree{
		spec.LexModeKindIDMin: cpt,
	})
	if err != nil {
		t.Fatal(err)
	}
	dfa := GenDFA(bt, symTab)
	if dfa == nil {
		t.Fatalf("DFA is nil")
	}

	symPos := func(n uint16) symbolPosition {
		pos, err := newSymbolPosition(n, false)
		if err != nil {
			panic(err)
		}
		return pos
	}

	endPos := func(n uint16) symbolPosition {
		pos, err := newSymbolPosition(n, true)
		if err != nil {
			panic(err)
		}
		return pos
	}

	s0 := newSymbolPositionSet().add(symPos(1)).add(symPos(2)).add(symPos(3))
	s1 := newSymbolPositionSet().add(symPos(1)).add(symPos(2)).add(symPos(3)).add(symPos(4))
	s2 := newSymbolPositionSet().add(symPos(1)).add(symPos(2)).add(symPos(3)).add(symPos(5))
	s3 := newSymbolPositionSet().add(symPos(1)).add(symPos(2)).add(symPos(3)).add(endPos(6))

	rune2Int := func(char rune, index int) uint8 {
		return uint8([]byte(string(char))[index])
	}

	tranS0 := [256]string{}
	tranS0[rune2Int('a', 0)] = s1.hash()
	tranS0[rune2Int('b', 0)] = s0.hash()

	tranS1 := [256]string{}
	tranS1[rune2Int('a', 0)] = s1.hash()
	tranS1[rune2Int('b', 0)] = s2.hash()

	tranS2 := [256]string{}
	tranS2[rune2Int('a', 0)] = s1.hash()
	tranS2[rune2Int('b', 0)] = s3.hash()

	tranS3 := [256]string{}
	tranS3[rune2Int('a', 0)] = s1.hash()
	tranS3[rune2Int('b', 0)] = s0.hash()

	expectedTranTab := map[string][256]string{
		s0.hash(): tranS0,
		s1.hash(): tranS1,
		s2.hash(): tranS2,
		s3.hash(): tranS3,
	}
	if len(dfa.TransitionTable) != len(expectedTranTab) {
		t.Errorf("transition table is mismatched: want: %v entries, got: %v entries", len(expectedTranTab), len(dfa.TransitionTable))
	}
	for h, eTranTab := range expectedTranTab {
		tranTab, ok := dfa.TransitionTable[h]
		if !ok {
			t.Errorf("no entry; hash: %v", h)
			continue
		}
		if len(tranTab) != len(eTranTab) {
			t.Errorf("transition table is mismatched: hash: %v, want: %v entries, got: %v entries", h, len(eTranTab), len(tranTab))
		}
		for c, eNext := range eTranTab {
			if eNext == "" {
				continue
			}

			next := tranTab[c]
			if next == "" {
				t.Errorf("no enatry: hash: %v, char: %v", h, c)
			}
			if next != eNext {
				t.Errorf("next state is mismatched: want: %v, got: %v", eNext, next)
			}
		}
	}

	if dfa.InitialState != s0.hash() {
		t.Errorf("initial state is mismatched: want: %v, got: %v", s0.hash(), dfa.InitialState)
	}

	accTab := map[string]spec.LexModeKindID{
		s3.hash(): 1,
	}
	if len(dfa.AcceptingStatesTable) != len(accTab) {
		t.Errorf("accepting states are mismatched: want: %v entries, got: %v entries", len(accTab), len(dfa.AcceptingStatesTable))
	}
	for eState, eID := range accTab {
		id, ok := dfa.AcceptingStatesTable[eState]
		if !ok {
			t.Errorf("accepting state is not found: state: %v", eState)
		}
		if id != eID {
			t.Errorf("ID is mismatched: state: %v, want: %v, got: %v", eState, eID, id)
		}
	}
}