SgfParser 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+