SgfParser 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ source "http://rubygems.org"
5
5
  group :development do
6
6
  gem "jeweler"
7
7
  gem "rcov"
8
+ gem "rdoc"
8
9
  end
9
10
 
10
11
  group :test do
@@ -9,6 +9,7 @@ GEM
9
9
  rake
10
10
  rake (0.9.2)
11
11
  rcov (0.9.9)
12
+ rdoc (3.9)
12
13
  rspec (2.6.0)
13
14
  rspec-core (~> 2.6.0)
14
15
  rspec-expectations (~> 2.6.0)
@@ -24,4 +25,5 @@ PLATFORMS
24
25
  DEPENDENCIES
25
26
  jeweler
26
27
  rcov
28
+ rdoc
27
29
  rspec
@@ -1,38 +1,44 @@
1
- =INFORMATION
2
- Author: Aldric Giacomoni
3
-
4
- Email : aldric~at~trevoke.net (feedback very welcome!)
5
-
6
- SGF: all formats (but untested with FF < 4)
7
-
8
- Ruby: >=1.8.7 (may work with 1.8.6)
9
-
10
- =QUICK HOWTO
11
- Example:
12
- require 'sgf_parser'
13
- tree = SgfParser::Tree.new :filename => File
14
- tree = SgfParser::Tree.new :string => String
15
-
16
- All trees begin with an empty node ( @root) which allows a simple support of multiple gametrees.
17
-
18
- Most games will just care about, say,
19
- tree.root.children[0] which is the first node of the first gametree.
20
-
21
- For any node, one can summon the properties as such:
22
- node.properties # => returns a hash of the properties.
23
- A single property can be called, like the comments, for instance, like so:
24
- node.C # => returns the comments for this node.
25
-
26
- The library currently uses method_missing to painlessly return the data. I must admit that this is both clever coding and laziness on my part.
27
-
28
- There is also a SGF Indenter. Its purpose is to make SGF files more readable to humans at a glance.
29
- require 'sgf_parser/sgfindent' # Done automatically if you require 'sgf_parser'
30
- sgf = SgfParser::Indenter.new 'some_ugly_file.sgf' # Will output to the console
31
- sgf = SgfParser::Indenter.new 'some_ugly_file.sgf' 'pretty.sgf' # Sends the result to a new file.
32
-
33
- ___
34
-
35
- TODO
36
- ? Create a "Game" class, and if a whole set of () exists, then I have a game?
37
- That way maybe I can easily go to multiple games stored in a single SGF file?
38
- Mostly syntactic sugar, but may be worth implementing.
1
+ =INFORMATION
2
+ Author: Aldric Giacomoni
3
+
4
+ Email : aldric@at@trevoke.net (feedback very welcome!)
5
+
6
+ SGF: all formats (but untested with FF < 4)
7
+
8
+ Ruby: >=1.8.7 (may work with 1.8.6)
9
+
10
+ =QUICK HOWTO
11
+ Example:
12
+ require 'sgf'
13
+ tree = SGF::Parser.new file_or_string
14
+
15
+
16
+ All trees begin with an empty node ( @root) which allows a simple support of multiple gametrees.
17
+
18
+ Most games will just care about, say,
19
+ tree.root.children[0] which is the first node of the first gametree.
20
+
21
+ For any node, one can summon the properties as such:
22
+ node.properties # => returns a hash of the properties.
23
+ A single property can be called, like the comments, for instance, like so:
24
+ node.C # => returns the comments for this node.
25
+ node.comments # => syntactic sugar
26
+
27
+ The library currently uses method_missing to painlessly return the data. I must admit that this is both clever coding and laziness on my part.
28
+
29
+ There is also a SGF Indenter. Its purpose is to make SGF files more readable to humans at a glance.
30
+ require 'sgf/sgfindent' # Done automatically if you require 'sgf_parser'
31
+ sgf = SGF::Indenter.new 'some_ugly_file.sgf' # Will output to the console
32
+ sgf = SGF::Indenter.new 'some_ugly_file.sgf' 'pretty.sgf' # Sends the result to a new file.
33
+
34
+ ___
35
+
36
+ TODO
37
+ - Create a "Game" class that wraps a complete set of () for ease of use
38
+ - implement node.next to go to node.children[0] because who wants to type that anyway?
39
+ - implement a 'current' node in Tree so we don't have to jump from node to node. This means..
40
+ - implement tree.next instead of node.next for tree.current_node.children[0]
41
+ - examine/fix the smell that when you use add_children, it also sets the parent on the passed-in nodes.
42
+ - fix the multiple properties bug in the SGF indenter as well
43
+ - see how much of the parser code the indenter can use, since it's basically the same logic
44
+
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{SgfParser}
8
- s.version = "0.9.1"
8
+ s.version = "1.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Aldric Giacomoni"]
12
- s.date = %q{2011-07-29}
12
+ s.date = %q{2011-08-01}
13
13
  s.description = %q{SGFParser is a library that parses and saves SGF (Smart Game Format) files.}
14
14
  s.email = %q{aldric@trevoke.net}
15
15
  s.extra_rdoc_files = [
@@ -27,22 +27,21 @@ Gem::Specification.new do |s|
27
27
  "Rakefile",
28
28
  "SgfParser.gemspec",
29
29
  "VERSION",
30
- "lib/sgf/parser/node.rb",
31
- "lib/sgf/parser/properties.rb",
32
- "lib/sgf/parser/tree.rb",
33
- "lib/sgf/parser/tree_parse.rb",
34
- "lib/sgf/sgf_indent.rb",
35
- "lib/sgf/sgfindent.rb",
36
- "lib/sgf_parser.rb",
37
- "sample_sgf/ff4_ex.sgf",
38
- "sample_sgf/ff4_ex_saved.sgf",
39
- "sample_sgf/redrose-tartrate.sgf",
40
- "sample_sgf/simple.sgf",
41
- "sample_sgf/simple_saved.sgf",
30
+ "lib/sgf.rb",
31
+ "lib/sgf/indenter.rb",
32
+ "lib/sgf/node.rb",
33
+ "lib/sgf/parser.rb",
34
+ "lib/sgf/properties.rb",
35
+ "lib/sgf/tree.rb",
42
36
  "sample_usage/parsing_files.rb",
37
+ "spec/data/ff4_ex.sgf",
38
+ "spec/data/ff4_ex_saved.sgf",
39
+ "spec/data/redrose-tartrate.sgf",
40
+ "spec/data/simple.sgf",
41
+ "spec/data/simple_saved.sgf",
43
42
  "spec/node_spec.rb",
43
+ "spec/parser_spec.rb",
44
44
  "spec/spec_helper.rb",
45
- "spec/tree_parser_spec.rb",
46
45
  "spec/tree_spec.rb"
47
46
  ]
48
47
  s.homepage = %q{http://github.com/Trevoke/SGFParser}
@@ -56,15 +55,18 @@ Gem::Specification.new do |s|
56
55
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
57
56
  s.add_development_dependency(%q<jeweler>, [">= 0"])
58
57
  s.add_development_dependency(%q<rcov>, [">= 0"])
58
+ s.add_development_dependency(%q<rdoc>, [">= 0"])
59
59
  s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
60
60
  else
61
61
  s.add_dependency(%q<jeweler>, [">= 0"])
62
62
  s.add_dependency(%q<rcov>, [">= 0"])
63
+ s.add_dependency(%q<rdoc>, [">= 0"])
63
64
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
64
65
  end
65
66
  else
66
67
  s.add_dependency(%q<jeweler>, [">= 0"])
67
68
  s.add_dependency(%q<rcov>, [">= 0"])
69
+ s.add_dependency(%q<rdoc>, [">= 0"])
68
70
  s.add_dependency(%q<rspec>, [">= 1.2.9"])
69
71
  end
70
72
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.9.1
1
+ 1.0.0
@@ -0,0 +1,8 @@
1
+ $: << File.dirname(__FILE__)
2
+
3
+ require 'yaml'
4
+ require 'sgf/properties'
5
+ require 'sgf/node'
6
+ require 'sgf/tree'
7
+ require 'sgf/parser'
8
+ require 'sgf/indenter'
@@ -1,108 +1,108 @@
1
- require 'stringio'
2
-
3
- module SgfParser
4
-
5
- # This indents an SGF file to make it more readable. It outputs to the screen
6
- # by default, but can be given a file as output.
7
- # Usage: SgfParser::Indenter.new infile, outfile
8
- class Indenter
9
-
10
- def initialize file, out=$stdout
11
- @stream = load_file_to_stream(file)
12
- @new_string = ""
13
- @indentation = 0
14
- @out = (out == $stdout) ? $stdout : File.open(out, 'w')
15
- parse
16
- end
17
-
18
- def next_character
19
- !@stream.eof? && @stream.sysread(1)
20
- end
21
-
22
- def parse
23
- while char = next_character
24
- case char
25
- when '(' then open_branch
26
- when ')' then close_branch
27
- when ';' then new_node
28
- when '[' then add_property
29
- else add_identity(char)
30
- end
31
- end
32
-
33
- @out << @new_string
34
- @out.close unless @out == $stdout
35
- #if out == $stdout
36
- # $stdout << @new_string
37
- #else
38
- # File.open(out, 'w') { |file| file << @new_string }
39
- #end
40
- end
41
-
42
- def open_branch
43
- next_line
44
- @indentation += 2
45
- @new_string << " " * @indentation
46
- @new_string << "("
47
- end
48
-
49
- def close_branch
50
- @new_string << ")"
51
- next_line
52
- @indentation -= 2
53
- @indentation = 0 if @indentation < 0
54
- @new_string << " " * @indentation
55
- end
56
-
57
- def new_node
58
- next_line
59
- @new_string << " " * @indentation
60
- @new_string << ";"
61
- end
62
-
63
- def next_line
64
- @new_string << "\n"
65
- end
66
-
67
- #TODO Fix it more. Add _ONE_ set of indentation if there are newlines,
68
- #TODO Not one set for every newline.
69
- def add_property
70
- buffer = "["
71
- while true
72
- next_bit = @stream.sysread(1)
73
- next_bit << @stream.sysread(1) if next_bit == "\\"
74
- buffer << next_bit
75
- buffer << " " * @indentation if next_bit == "\n"
76
- break if next_bit == "]"
77
- end
78
- buffer << "\n"
79
- buffer << " " * @indentation
80
- @new_string << buffer
81
- end
82
-
83
- def add_identity(char)
84
- @new_string << char unless char == "\n"
85
- end
86
-
87
- private
88
-
89
- def load_file_to_stream(file)
90
- sgf = ""
91
- File.open(file) { |f| sgf = f.read }
92
- clean_string(sgf)
93
- return StringIO.new(sgf, 'r')
94
- end
95
-
96
- def clean_string(sgf)
97
- sgf.gsub! "\\\\n\\\\r", ""
98
- sgf.gsub! "\\\\r\\\\n", ""
99
- sgf.gsub! "\\\\r", ""
100
- sgf.gsub! "\\\\n", ""
101
- end
102
-
103
- end
104
- end
105
-
106
-
107
-
108
-
1
+ require 'stringio'
2
+
3
+ module SGF
4
+
5
+ # This indents an SGF file to make it more readable. It outputs to the screen
6
+ # by default, but can be given a file as output.
7
+ # Usage: SgfParser::Indenter.new infile, outfile
8
+ class Indenter
9
+
10
+ def initialize file, out=$stdout
11
+ @stream = load_file_to_stream(file)
12
+ @new_string = ""
13
+ @indentation = 0
14
+ @out = (out == $stdout) ? $stdout : File.open(out, 'w')
15
+ parse
16
+ end
17
+
18
+ def next_character
19
+ !@stream.eof? && @stream.sysread(1)
20
+ end
21
+
22
+ def parse
23
+ while char = next_character
24
+ case char
25
+ when '(' then open_branch
26
+ when ')' then close_branch
27
+ when ';' then new_node
28
+ when '[' then add_property
29
+ else add_identity(char)
30
+ end
31
+ end
32
+
33
+ @out << @new_string
34
+ @out.close unless @out == $stdout
35
+ #if out == $stdout
36
+ # $stdout << @new_string
37
+ #else
38
+ # File.open(out, 'w') { |file| file << @new_string }
39
+ #end
40
+ end
41
+
42
+ def open_branch
43
+ next_line
44
+ @indentation += 2
45
+ @new_string << " " * @indentation
46
+ @new_string << "("
47
+ end
48
+
49
+ def close_branch
50
+ @new_string << ")"
51
+ next_line
52
+ @indentation -= 2
53
+ @indentation = 0 if @indentation < 0
54
+ @new_string << " " * @indentation
55
+ end
56
+
57
+ def new_node
58
+ next_line
59
+ @new_string << " " * @indentation
60
+ @new_string << ";"
61
+ end
62
+
63
+ def next_line
64
+ @new_string << "\n"
65
+ end
66
+
67
+ #TODO Fix it more. Add _ONE_ set of indentation if there are newlines,
68
+ #TODO Not one set for every newline.
69
+ def add_property
70
+ buffer = "["
71
+ while true
72
+ next_bit = @stream.sysread(1)
73
+ next_bit << @stream.sysread(1) if next_bit == "\\"
74
+ buffer << next_bit
75
+ buffer << " " * @indentation if next_bit == "\n"
76
+ break if next_bit == "]"
77
+ end
78
+ buffer << "\n"
79
+ buffer << " " * @indentation
80
+ @new_string << buffer
81
+ end
82
+
83
+ def add_identity(char)
84
+ @new_string << char unless char == "\n"
85
+ end
86
+
87
+ private
88
+
89
+ def load_file_to_stream(file)
90
+ sgf = ""
91
+ File.open(file) { |f| sgf = f.read }
92
+ clean_string(sgf)
93
+ return StringIO.new(sgf, 'r')
94
+ end
95
+
96
+ def clean_string(sgf)
97
+ sgf.gsub! "\\\\n\\\\r", ""
98
+ sgf.gsub! "\\\\r\\\\n", ""
99
+ sgf.gsub! "\\\\r", ""
100
+ sgf.gsub! "\\\\n", ""
101
+ end
102
+
103
+ end
104
+ end
105
+
106
+
107
+
108
+
@@ -1,58 +1,62 @@
1
- module SgfParser
2
-
3
- class Node
4
-
5
- attr_accessor :parent, :children, :properties
6
-
7
- # Creates a new node. Options which can be passed in are:
8
- # :parent => parent_node (nil by default)
9
- # : children => [list, of, children] (empty array by default)
10
- # :properties => {hash_of => properties} (empty hash by default)
11
- def initialize args={}
12
- @parent = args[:parent]
13
- @children = []
14
- add_children args[:children] if !args[:children].nil?
15
- @properties = Hash.new
16
- @properties.merge! args[:properties] if !args[:properties].nil?
17
- end
18
-
19
- def add_children *nodes
20
- nodes.flatten!
21
- raise "Non-node child given!" if nodes.find { |node| node.class != Node }
22
- # This node becomes the proud parent of one or more node!
23
- nodes.each { |node| node.parent = self }
24
- @children.concat nodes
25
- end
26
-
27
- def add_properties hash
28
- hash.each do |key, value|
29
- @properties[key] ||= ""
30
- @properties[key].concat value
31
- end
32
- end
33
-
34
- def each_child
35
- @children.each { |child| yield child }
36
- end
37
-
38
- def == other_node
39
- @properties == other_node.properties
40
- end
41
-
42
- def comments
43
- @properties["C"]
44
- end
45
-
46
- alias :comment :comments
47
-
48
- private
49
-
50
- def method_missing method_name, *args
51
- output = @properties[method_name.to_s.upcase]
52
- super(method_name, args) if output.nil?
53
- output
54
- end
55
-
56
- end
57
-
1
+ module SGF
2
+
3
+ class Node
4
+
5
+ attr_accessor :parent, :children, :properties
6
+
7
+ # Creates a new node. Arguments which can be passed in are:
8
+ # :parent => parent_node (nil by default)
9
+ # :children => [list, of, children] (empty array by default)
10
+ # :properties => {hash_of => properties} (empty hash by default)
11
+ def initialize args={}
12
+ @parent = args[:parent]
13
+ @children = []
14
+ add_children args[:children] if args[:children]
15
+ @properties = Hash.new
16
+ @properties.merge! args[:properties] if args[:properties]
17
+ end
18
+
19
+ def add_children *nodes
20
+ nodes.flatten!
21
+ raise "Non-node child given!" if nodes.any? { |node| node.class != Node }
22
+ nodes.each { |node| node.parent = self }
23
+ @children.concat nodes
24
+ end
25
+
26
+ def add_properties hash
27
+ hash.each do |key, value|
28
+ @properties[key] ||= ""
29
+ @properties[key].concat value
30
+ end
31
+ end
32
+
33
+ def each_child
34
+ @children.each { |child| yield child }
35
+ end
36
+
37
+ def == other_node
38
+ @properties == other_node.properties
39
+ end
40
+
41
+ def comments
42
+ @properties["C"]
43
+ end
44
+
45
+ alias :comment :comments
46
+
47
+ private
48
+
49
+ def method_missing method_name, *args
50
+ property = method_name.to_s.upcase
51
+ if property[/(.*?)=$/]
52
+ @properties[$1] = args[0]
53
+ else
54
+ output = @properties[property]
55
+ super(method_name, args) if output.nil?
56
+ output
57
+ end
58
+ end
59
+
60
+ end
61
+
58
62
  end