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.
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