SgfParser 0.9.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,111 +1,120 @@
1
- require 'stringio'
2
-
3
- module SgfParser
4
- class Tree
5
-
6
- #private
7
-
8
- def parse
9
- while char = next_character
10
- case char
11
- when '(' then store_branch
12
- when ')' then fetch_branch
13
- when ';' then store_node_and_create_new_node
14
- when '[' then get_and_store_property
15
- else store_character(char)
16
- end
17
- end
18
- end
19
-
20
- def next_character
21
- character_available? && @stream.sysread(1)
22
- end
23
-
24
- def character_available?
25
- @stream ||= StringIO.new clean_string, 'r'
26
- !@stream.eof?
27
- end
28
-
29
- def clean_string
30
- @sgf.gsub! "\\\\n\\\\r", ""
31
- @sgf.gsub! "\\\\r\\\\n", ""
32
- @sgf.gsub! "\\\\r", ""
33
- @sgf.gsub! "\\\\n", ""
34
- @sgf
35
- end
36
-
37
- def store_branch
38
- @branches ||= []
39
- @branches.unshift @current_node
40
- end
41
-
42
- def current_node
43
- @current_node ||= @root
44
- end
45
-
46
- def fetch_branch
47
- @current_node = @branches.shift
48
- clear_temporary_data
49
- end
50
-
51
- def store_node_and_create_new_node
52
- parent = current_node
53
- @current_node = Node.new :parent => parent
54
- parent.add_properties content
55
- parent.add_children @current_node
56
- clear_temporary_data
57
- end
58
-
59
- def get_and_store_property
60
- @content[@identity] ||= ""
61
- @content[@identity] << get_property
62
- @identity = ""
63
- end
64
-
65
- def get_property
66
- buffer = ""
67
- while char = next_character
68
- case char
69
- when "]" then break unless multiple_properties?
70
- when "\\" then
71
- char << next_character
72
- char = "]" if char == "\\]"
73
- end
74
-
75
- buffer << char
76
- end
77
- "[#{buffer}]"
78
- end
79
-
80
- def multiple_properties?
81
- multiple_properties = false
82
- if char = next_character
83
- char = next_character if char == "\n"
84
- if char == "["
85
- multiple_properties = true
86
- end
87
- @stream.pos -= 1
88
- multiple_properties
89
- end
90
- end
91
-
92
- def store_character(char)
93
- @identity << char unless char == "\n"
94
- end
95
-
96
- def clear_temporary_data
97
- @content.clear
98
- @identity = ""
99
- end
100
-
101
- def content
102
- @content ||= {}
103
- end
104
-
105
- def identity
106
- @identity ||= ""
107
- end
108
-
109
- end
110
- end
111
-
1
+ require 'stringio'
2
+
3
+ module SGF
4
+ class Parser
5
+
6
+ def initialize sgf
7
+ @sgf = stringified(sgf)
8
+ @tree = Tree.new sgf
9
+ @root = @tree.root
10
+ end
11
+
12
+ def stringified sgf
13
+ File.exist?(sgf) ? File.read(sgf) : sgf
14
+ end
15
+
16
+ def parse
17
+ while char = next_character
18
+ case char
19
+ when '(' then store_branch
20
+ when ')' then fetch_branch
21
+ when ';' then store_node_and_create_new_node
22
+ when '[' then get_and_store_property
23
+ else store_character(char)
24
+ end
25
+ end
26
+ @tree
27
+ end
28
+
29
+ def next_character
30
+ character_available? && @stream.sysread(1)
31
+ end
32
+
33
+ def character_available?
34
+ @stream ||= StringIO.new clean_string, 'r'
35
+ !@stream.eof?
36
+ end
37
+
38
+ def clean_string
39
+ @sgf.gsub! "\\\\n\\\\r", ""
40
+ @sgf.gsub! "\\\\r\\\\n", ""
41
+ @sgf.gsub! "\\\\r", ""
42
+ @sgf.gsub! "\\\\n", ""
43
+ @sgf
44
+ end
45
+
46
+ def store_branch
47
+ @branches ||= []
48
+ @branches.unshift @current_node
49
+ end
50
+
51
+ def current_node
52
+ @current_node ||= @root
53
+ end
54
+
55
+ def fetch_branch
56
+ @current_node = @branches.shift
57
+ clear_temporary_data
58
+ end
59
+
60
+ def store_node_and_create_new_node
61
+ parent = current_node
62
+ @current_node = Node.new :parent => parent
63
+ parent.add_properties content
64
+ parent.add_children @current_node
65
+ clear_temporary_data
66
+ end
67
+
68
+ def get_and_store_property
69
+ @content[@identity] ||= ""
70
+ @content[@identity] << get_property
71
+ @identity = ""
72
+ end
73
+
74
+ def get_property
75
+ buffer = ""
76
+ while char = next_character
77
+ case char
78
+ when "]" then break unless multiple_properties?
79
+ when "\\" then
80
+ char << next_character
81
+ char = "]" if char == "\\]"
82
+ end
83
+
84
+ buffer << char
85
+ end
86
+ "[#{buffer}]"
87
+ end
88
+
89
+ def multiple_properties?
90
+ multiple_properties = false
91
+ if char = next_character
92
+ char = next_character if char == "\n"
93
+ if char == "["
94
+ multiple_properties = true
95
+ end
96
+ @stream.pos -= 1
97
+ multiple_properties
98
+ end
99
+ end
100
+
101
+ def store_character(char)
102
+ @identity << char unless char == "\n"
103
+ end
104
+
105
+ def clear_temporary_data
106
+ @content.clear
107
+ @identity = ""
108
+ end
109
+
110
+ def content
111
+ @content ||= {}
112
+ end
113
+
114
+ def identity
115
+ @identity ||= ""
116
+ end
117
+
118
+ end
119
+ end
120
+
@@ -1,97 +1,97 @@
1
- # A parser for SGF Files. Main usage: SGF::Tree.new :filename => file_name
2
- module SgfParser
3
-
4
- # http://www.red-bean.com/sgf/proplist.html
5
-
6
- # Here we define SGF::Properties, so we can figure out what each property
7
- # is and does.
8
-
9
- property_string = %Q{AB Add Black setup list of stone
10
- AE Add Empty setup list of point
11
- AN Annotation game-info simpletext
12
- AP Application root composed simpletext ':' simpletext
13
- AR Arrow - list of composed point ':' point
14
- AS Who adds stones - (LOA) simpletext
15
- AW Add White setup list of stone
16
- B Black move move
17
- BL Black time left move real
18
- BM Bad move move double
19
- BR Black rank game-info simpletext
20
- BT Black team game-info simpletext
21
- C Comment - text
22
- CA Charset root simpletext
23
- CP Copyright game-info simpletext
24
- CR Circle - list of point
25
- DD Dim points - (inherit) elist of point
26
- DM Even position - double
27
- DO Doubtful move none
28
- DT Date game-info simpletext
29
- EV Event game-info simpletext
30
- FF Fileformat root number (range: 1-4)
31
- FG Figure - none | composed number ":" simpletext
32
- GB Good for Black - double
33
- GC Game comment game-info text
34
- GM Game root number (range: 1-5,7-16)
35
- GN Game name game-info simpletext
36
- GW Good for White - double
37
- HA Handicap game-info (Go) number
38
- HO Hotspot - double
39
- IP Initial pos. game-info (LOA) simpletext
40
- IT Interesting move none
41
- IY Invert Y-axis game-info (LOA) simpletext
42
- KM Komi game-info (Go) real
43
- KO Ko move none
44
- LB Label - list of composed point ':' simpletext
45
- LN Line - list of composed point ':' point
46
- MA Mark - list of point
47
- MN set move number move number
48
- N Nodename - simpletext
49
- OB OtStones Black move number
50
- ON Opening game-info simpletext
51
- OT Overtime game-info simpletext
52
- OW OtStones White move number
53
- PB Player Black game-info simpletext
54
- PC Place game-info simpletext
55
- PL Player to play setup color
56
- PM Print move mode - (inherit) number
57
- PW Player White game-info simpletext
58
- RE Result game-info simpletext
59
- RO Round game-info simpletext
60
- RU Rules game-info simpletext
61
- SE Markup - (LOA) point
62
- SL Selected - list of point
63
- SO Source game-info simpletext
64
- SQ Square - list of point
65
- ST Style root number (range: 0-3)
66
- SU Setup type game-info (LOA) simpletext
67
- SZ Size root (number | composed number ':' number)
68
- TB Territory Black - (Go) elist of point
69
- TE Tesuji move double
70
- TM Timelimit game-info real
71
- TR Triangle - list of point
72
- TW Territory White - (Go) elist of point
73
- UC Unclear pos - double
74
- US User game-info simpletext
75
- V Value - real
76
- VW View - (inherit) elist of point
77
- W White move move
78
- WL White time left move real
79
- WR White rank game-info simpletext
80
- WT White team game-info simpletext }
81
-
82
- property_array = property_string.split("\n")
83
- hash = {}
84
- property_array.each do |set|
85
- temp = set.gsub("\t", " ")
86
- id = temp[0..3].strip
87
- desc = temp[4..19].strip
88
- property_type = temp[20..35].strip
89
- property_value = temp[37..-1].strip
90
- hash[id] = [desc, property_type, property_value]
91
- end
92
-
93
- # All this work for this minuscule line!
94
- PROPERTIES = hash
95
-
96
- end
97
-
1
+ # A parser for SGF Files. Main usage: SGF::Tree.new :filename => file_name
2
+ module SgfParser
3
+
4
+ # http://www.red-bean.com/sgf/proplist.html
5
+
6
+ # Here we define SGF::Properties, so we can figure out what each property
7
+ # is and does.
8
+
9
+ property_string = %Q{AB Add Black setup list of stone
10
+ AE Add Empty setup list of point
11
+ AN Annotation game-info simpletext
12
+ AP Application root composed simpletext ':' simpletext
13
+ AR Arrow - list of composed point ':' point
14
+ AS Who adds stones - (LOA) simpletext
15
+ AW Add White setup list of stone
16
+ B Black move move
17
+ BL Black time left move real
18
+ BM Bad move move double
19
+ BR Black rank game-info simpletext
20
+ BT Black team game-info simpletext
21
+ C Comment - text
22
+ CA Charset root simpletext
23
+ CP Copyright game-info simpletext
24
+ CR Circle - list of point
25
+ DD Dim points - (inherit) elist of point
26
+ DM Even position - double
27
+ DO Doubtful move none
28
+ DT Date game-info simpletext
29
+ EV Event game-info simpletext
30
+ FF Fileformat root number (range: 1-4)
31
+ FG Figure - none | composed number ":" simpletext
32
+ GB Good for Black - double
33
+ GC Game comment game-info text
34
+ GM Game root number (range: 1-5,7-16)
35
+ GN Game name game-info simpletext
36
+ GW Good for White - double
37
+ HA Handicap game-info (Go) number
38
+ HO Hotspot - double
39
+ IP Initial pos. game-info (LOA) simpletext
40
+ IT Interesting move none
41
+ IY Invert Y-axis game-info (LOA) simpletext
42
+ KM Komi game-info (Go) real
43
+ KO Ko move none
44
+ LB Label - list of composed point ':' simpletext
45
+ LN Line - list of composed point ':' point
46
+ MA Mark - list of point
47
+ MN set move number move number
48
+ N Nodename - simpletext
49
+ OB OtStones Black move number
50
+ ON Opening game-info simpletext
51
+ OT Overtime game-info simpletext
52
+ OW OtStones White move number
53
+ PB Player Black game-info simpletext
54
+ PC Place game-info simpletext
55
+ PL Player to play setup color
56
+ PM Print move mode - (inherit) number
57
+ PW Player White game-info simpletext
58
+ RE Result game-info simpletext
59
+ RO Round game-info simpletext
60
+ RU Rules game-info simpletext
61
+ SE Markup - (LOA) point
62
+ SL Selected - list of point
63
+ SO Source game-info simpletext
64
+ SQ Square - list of point
65
+ ST Style root number (range: 0-3)
66
+ SU Setup type game-info (LOA) simpletext
67
+ SZ Size root (number | composed number ':' number)
68
+ TB Territory Black - (Go) elist of point
69
+ TE Tesuji move double
70
+ TM Timelimit game-info real
71
+ TR Triangle - list of point
72
+ TW Territory White - (Go) elist of point
73
+ UC Unclear pos - double
74
+ US User game-info simpletext
75
+ V Value - real
76
+ VW View - (inherit) elist of point
77
+ W White move move
78
+ WL White time left move real
79
+ WR White rank game-info simpletext
80
+ WT White team game-info simpletext }
81
+
82
+ property_array = property_string.split("\n")
83
+ hash = {}
84
+ property_array.each do |set|
85
+ temp = set.gsub("\t", " ")
86
+ id = temp[0..3].strip
87
+ desc = temp[4..19].strip
88
+ property_type = temp[20..35].strip
89
+ property_value = temp[37..-1].strip
90
+ hash[id] = [desc, property_type, property_value]
91
+ end
92
+
93
+ # All this work for this minuscule line!
94
+ PROPERTIES = hash
95
+
96
+ end
97
+
@@ -1,107 +1,91 @@
1
- module SgfParser
2
-
3
- class Tree
4
- include Enumerable
5
-
6
- attr_accessor :root
7
-
8
- def initialize args={}
9
- @root = Node.new
10
- @sgf = ""
11
- raise ArgumentError, "Both file and string provided" if args[:filename] && args[:string]
12
- if args[:filename]
13
- load_file args[:filename]
14
- elsif args[:string]
15
- load_string args[:string]
16
- end
17
-
18
- parse unless @sgf.empty?
19
- end
20
-
21
- def each order=:preorder, &block
22
- # I know, I know. SGF is only preorder. Well, it's implemented, ain't it?
23
- # Stop complaining.
24
- case order
25
- when :preorder
26
- preorder @root, &block
27
- end
28
- end
29
-
30
- # Compares a tree to another tree, node by node.
31
- # Nodes must be the same (same properties, parents and children).
32
- def == other_tree
33
- one = []
34
- two = []
35
- each { |node| one << node }
36
- other_tree.each { |node| two << node }
37
- one == two
38
- end
39
-
40
- # Saves the tree as an SGF file. raises an error if a filename is not given.
41
- # tree.save :filename => file_name
42
- def save args={}
43
- raise ArgumentError, "No file name provided" if args[:filename].nil?
44
- @savable_sgf = "("
45
- @root.children.each { |child| write_node child }
46
- @savable_sgf << ")"
47
-
48
- File.open(args[:filename], 'w') { |f| f << @savable_sgf }
49
- end
50
-
51
- private
52
-
53
- # Adds a stringified node to the variable @savable_sgf.
54
- def write_node node=@root
55
- @savable_sgf << ";"
56
- unless node.properties.empty?
57
- properties = ""
58
- node.properties.each do |identity, property|
59
- new_property = property[1...-1]
60
- new_property.gsub!("]", "\\]") if identity == "C"
61
- properties += "#{identity.to_s}[#{new_property}]"
62
- end
63
- @savable_sgf << properties
64
- end
65
-
66
- case node.children.size
67
- when 0
68
- @savable_sgf << ")"
69
- when 1
70
- write_node node.children[0]
71
- else
72
- node.each_child do |child|
73
- @savable_sgf << "("
74
- write_node child
75
- end
76
- end
77
- end
78
-
79
- def load_string string
80
- @sgf = string
81
- end
82
-
83
- def load_file filename
84
- File.open(filename, 'r') { |f| @sgf = f.read }
85
- end
86
-
87
- # Traverse the tree in preorder fashion, starting with the @root node if
88
- # no node is given, and activating the passed block on each.
89
- def preorder node=@root, &block
90
- # stop processing if the block returns false
91
- if yield node then
92
- node.each_child do |child|
93
- preorder(child, &block)
94
- end
95
- end
96
- end
97
-
98
- def method_missing method_name, *args
99
- output = @root.children[0].properties[method_name]
100
- super(method_name, args) if output.nil?
101
- output
102
- end
103
-
104
- end
105
-
106
- end
107
-
1
+ module SGF
2
+
3
+ class Tree
4
+ include Enumerable
5
+
6
+ attr_accessor :root
7
+
8
+ def initialize sgf
9
+ @root = Node.new
10
+ @sgf = sgf
11
+ end
12
+
13
+ def each order=:preorder, &block
14
+ # I know, I know. SGF is only preorder. Well, it's implemented, ain't it?
15
+ # Stop complaining.
16
+ case order
17
+ when :preorder
18
+ preorder @root, &block
19
+ end
20
+ end
21
+
22
+ # Compares a tree to another tree, node by node.
23
+ # Nodes must be the same (same properties, parents and children).
24
+ def == other_tree
25
+ one = []
26
+ two = []
27
+ each { |node| one << node }
28
+ other_tree.each { |node| two << node }
29
+ one == two
30
+ end
31
+
32
+ # Saves the tree as an SGF file. raises an error if a filename is not given.
33
+ # tree.save :filename => file_name
34
+ def save args={}
35
+ raise ArgumentError, "No file name provided" if args[:filename].nil?
36
+ @savable_sgf = "("
37
+ @root.children.each { |child| write_node child }
38
+ @savable_sgf << ")"
39
+
40
+ File.open(args[:filename], 'w') { |f| f << @savable_sgf }
41
+ end
42
+
43
+ private
44
+
45
+ # Adds a stringified node to the variable @savable_sgf.
46
+ def write_node node=@root
47
+ @savable_sgf << ";"
48
+ unless node.properties.empty?
49
+ properties = ""
50
+ node.properties.each do |identity, property|
51
+ new_property = property[1...-1]
52
+ new_property.gsub!("]", "\\]") if identity == "C"
53
+ properties += "#{identity.to_s}[#{new_property}]"
54
+ end
55
+ @savable_sgf << properties
56
+ end
57
+
58
+ case node.children.size
59
+ when 0
60
+ @savable_sgf << ")"
61
+ when 1
62
+ write_node node.children[0]
63
+ else
64
+ node.each_child do |child|
65
+ @savable_sgf << "("
66
+ write_node child
67
+ end
68
+ end
69
+ end
70
+
71
+ # Traverse the tree in preorder fashion, starting with the @root node if
72
+ # no node is given, and activating the passed block on each.
73
+ def preorder node=@root, &block
74
+ # stop processing if the block returns false
75
+ if yield node then
76
+ node.each_child do |child|
77
+ preorder(child, &block)
78
+ end
79
+ end
80
+ end
81
+
82
+ def method_missing method_name, *args
83
+ output = @root.children[0].properties[method_name]
84
+ super(method_name, args) if output.nil?
85
+ output
86
+ end
87
+
88
+ end
89
+
90
+ end
91
+