forked from beldur/libaduk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsgf.go
148 lines (119 loc) · 3.52 KB
/
sgf.go
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
package libaduk
import (
"fmt"
"log"
)
const (
SEQUENCE_START = '('
SEQUENCE_END = ')'
NODE_START = ';'
PROPERTY_START = '['
PROPERTY_END = ']'
)
// Begin parse an sgf string
func parse(sgf string) (*Node, error) {
log.Printf("Parsing: %s\n", sgf)
tree := NewNode(nil)
lastNode := tree
sequenceNodes := make([]*Node, 0)
nodeStartIndex := -1
lastParsedType := SEQUENCE_END
isInProperty := false
// range on string handles unicode automatically
for i, value := range sgf {
// If value is not a control character, ignore it
if !(value == SEQUENCE_START || value == SEQUENCE_END || value == PROPERTY_START ||
value == PROPERTY_END || value == NODE_START) {
continue
}
// When in property, continue until end of property
if isInProperty {
if value == PROPERTY_END {
// Here we check if the end of property is really the end or just an escaped character
numberOfEscapes := 0
for j := i - 1; sgf[j] == '\\'; j-- {
numberOfEscapes++
}
// If number of escapes is even, the property really ends here
if numberOfEscapes%2 == 0 {
isInProperty = false
}
}
continue
}
if value == PROPERTY_START {
isInProperty = true
}
// Sequence starts
if value == SEQUENCE_START {
// Safe sgf string to current node before creating a new one
if lastParsedType != SEQUENCE_END && nodeStartIndex != -1 {
lastNode.sgfData = sgf[nodeStartIndex:i]
}
// Create new Node for Sequence
node := NewNode(lastNode)
// Has current node already a child, than node is a sibling of lastNode
if lastNode.Next != nil {
last := lastNode.Next
for last.Down != nil {
last = last.Down
}
node.Up = last
last.Down = node
node.level = last.level + 1
} else {
lastNode.Next = node
}
// Update current to new sequence
lastNode.numChildren++
// Add sequence to stack
sequenceNodes = append(sequenceNodes, lastNode)
lastNode = node
nodeStartIndex = -1
lastParsedType = SEQUENCE_START
}
// Sequence ends
if value == SEQUENCE_END {
// Safe sgf string to current node before creating a new one
if lastParsedType != SEQUENCE_END && nodeStartIndex != -1 {
lastNode.sgfData = sgf[nodeStartIndex:i]
}
// If we had sequences in the stack, set current node to last in stack
if len(sequenceNodes) > 0 {
lastNode = sequenceNodes[len(sequenceNodes)-1]
sequenceNodes = sequenceNodes[:len(sequenceNodes)-1]
} else {
// If there was no sequence start for this sequence end, the sgf is malformed
return nil, fmt.Errorf("Malformed SGF (No Sequence start found for sequence end at position %d)!", i)
}
lastParsedType = SEQUENCE_END
}
// Node starts
if value == NODE_START {
if nodeStartIndex != -1 {
// Safe sgf string to last node before creating a new one
lastNode.sgfData = sgf[nodeStartIndex:i]
// Create new node and update current
node := NewNode(lastNode)
lastNode.numChildren = 1
lastNode.Next = node
lastNode = node
}
nodeStartIndex = i
}
}
// If we are in a property or sequence after parsing, the sgf is malformed
if isInProperty || len(sequenceNodes) > 0 {
return nil, fmt.Errorf("Malformed SGF (Still in Property or Sequence after parsing)!")
}
// Last Node should now be the last item from the sequence stack, so it should be the root
// So we remove all ties from the first node to the root node
node := tree.Next
node.Previous = nil
node.Up = nil
for node.Down != nil {
node = node.Down
node.Previous = nil
}
return tree.Next, nil
}