Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Moving sgf tools to github

  • Loading branch information...
commit 7051ce31ac316f2a2cf2274a1971ea161f9f534f 0 parents
@ps2 authored
4 CHANGES
@@ -0,0 +1,4 @@
+== Version 0.0.1 (sgf_parser) - 15-Dec-2007
+
+* Initial revision
+
5 README
@@ -0,0 +1,5 @@
+= SGF Tools - Ruby library to parse SGF files
+
+For more information about the SGF format, see http://www.red-bean.com/sgf/
+
+
31 Rakefile
@@ -0,0 +1,31 @@
+# Available options:
+#
+# rake test - Runs all test cases.
+# rake package - Runs test cases and builds packages for distribution.
+# rake rdoc - Builds API documentation in doc dir.
+
+require 'rake'
+require 'rspec/core/rake_task'
+require 'rake/gempackagetask'
+
+task :default => :spec
+
+RSpec::Core::RakeTask.new do |rspec|
+ #rspec.ruby_opts="-w"
+end
+
+load(File.join(File.dirname(__FILE__), "sgftools.gemspec"))
+Rake::GemPackageTask.new(SPEC) do |package|
+ # do nothing: I just need a gem but this block is required
+end
+
+file "lib/sgf_tools/parser.rb" => "lib/sgf_tools/parser.y" do
+ sh "racc -o lib/sgf_tools/parser.rb lib/sgf_tools/parser.y"
+end
+
+file "lib/sgf_tools/tokenizer.rb" => "lib/sgf_tools/tokenizer.rex" do
+ sh "rex -o lib/sgf_tools/tokenizer.rb lib/sgf_tools/tokenizer.rex"
+end
+
+task :build => ["lib/sgf_tools/parser.rb", "lib/sgf_tools/tokenizer.rb"]
+
6 lib/sgf_tools.rb
@@ -0,0 +1,6 @@
+module SgfTools
+ VERSION = "0.0.1"
+end
+
+require 'sgf_tools/parser'
+
261 lib/sgf_tools/parser.rb
@@ -0,0 +1,261 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by Racc 1.4.6
+# from Racc grammer file "".
+#
+
+require 'racc/parser.rb'
+
+require 'sgf_tools/tokenizer'
+
+module SgfTools
+ class Node
+ attr_accessor :properties
+ attr_accessor :branches
+
+ def initialize(prop, branches)
+ @properties = prop
+ @branches = branches
+ end
+
+ def inspect
+ str = ""
+ str << @properties.inspect
+ if !@branches.nil? && @branches.size > 0
+ str << " (#{@branches.size} variation(s))"
+ end
+ str
+ end
+
+ def [](key)
+ @properties[key]
+ end
+ end
+
+class Parser < Racc::Parser
+
+module_eval(<<'...end parser.y/module_eval...', 'parser.y', 58)
+
+ def parse_file(filename)
+ @tokenizer = SgfTokenizer.new
+ @tokenizer.load_file(filename)
+ do_parse
+ end
+
+ def parse_string(str)
+ @tokenizer = SgfTokenizer.new
+ @tokenizer.scan_evaluate(str)
+ do_parse
+ end
+
+ def next_token
+ @tokenizer.next_token
+ end
+
+...end parser.y/module_eval...
+##### State transition tables begin ###
+
+racc_action_table = [
+ 4, 15, 3, 3, 11, 8, 3, 16, 8, 9,
+ 3, 15, 19, 20 ]
+
+racc_action_check = [
+ 1, 8, 1, 6, 6, 6, 10, 10, 3, 4,
+ 0, 13, 15, 18 ]
+
+racc_action_pointer = [
+ 8, 0, nil, 4, 9, nil, 1, nil, -4, nil,
+ 4, nil, nil, 6, nil, 6, nil, nil, 7, nil,
+ nil ]
+
+racc_action_default = [
+ -14, -14, -1, -14, -14, -2, -14, -5, -7, 21,
+ -14, -3, -6, -8, -9, -14, -4, -10, -11, -12,
+ -13 ]
+
+racc_goto_table = [
+ 5, 1, 7, 14, 13, 12, 6, 10, 17, 5,
+ 18 ]
+
+racc_goto_check = [
+ 2, 1, 4, 6, 5, 4, 3, 1, 6, 2,
+ 7 ]
+
+racc_goto_pointer = [
+ nil, 1, -1, 3, -1, -4, -5, -5 ]
+
+racc_goto_default = [
+ nil, nil, 2, nil, nil, nil, nil, nil ]
+
+racc_reduce_table = [
+ 0, 0, :racc_error,
+ 1, 8, :_reduce_1,
+ 2, 8, :_reduce_2,
+ 3, 9, :_reduce_3,
+ 4, 9, :_reduce_4,
+ 1, 10, :_reduce_5,
+ 2, 10, :_reduce_6,
+ 1, 11, :_reduce_7,
+ 2, 11, :_reduce_8,
+ 1, 12, :_reduce_9,
+ 2, 12, :_reduce_10,
+ 2, 13, :_reduce_11,
+ 1, 14, :_reduce_12,
+ 2, 14, :_reduce_13 ]
+
+racc_reduce_n = 14
+
+racc_shift_n = 21
+
+racc_token_table = {
+ false => 0,
+ :error => 1,
+ "(" => 2,
+ ")" => 3,
+ ";" => 4,
+ :prop_ident => 5,
+ :prop_value => 6 }
+
+racc_nt_base = 7
+
+racc_use_result_var = true
+
+Racc_arg = [
+ racc_action_table,
+ racc_action_check,
+ racc_action_default,
+ racc_action_pointer,
+ racc_goto_table,
+ racc_goto_check,
+ racc_goto_default,
+ racc_goto_pointer,
+ racc_nt_base,
+ racc_reduce_table,
+ racc_token_table,
+ racc_shift_n,
+ racc_reduce_n,
+ racc_use_result_var ]
+
+Racc_token_to_s_table = [
+ "$end",
+ "error",
+ "\"(\"",
+ "\")\"",
+ "\";\"",
+ "prop_ident",
+ "prop_value",
+ "$start",
+ "collection",
+ "game_tree",
+ "sequence",
+ "node",
+ "properties",
+ "property",
+ "prop_list" ]
+
+Racc_debug_parser = false
+
+##### State transition tables end #####
+
+# reduce 0 omitted
+
+module_eval(<<'.,.,', 'parser.y', 3)
+ def _reduce_1(val, _values, result)
+ result = val
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 4)
+ def _reduce_2(val, _values, result)
+ result.push val[1]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 6)
+ def _reduce_3(val, _values, result)
+ result = val[1]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 9)
+ def _reduce_4(val, _values, result)
+ val[2][0].first.branches = val[2][1..-1]
+ result = val[1] + val[2][0]
+
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 13)
+ def _reduce_5(val, _values, result)
+ result = val
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 14)
+ def _reduce_6(val, _values, result)
+ result.push val[1]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 16)
+ def _reduce_7(val, _values, result)
+ result = {}
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 17)
+ def _reduce_8(val, _values, result)
+ result = Node.new(val[1], nil)
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 19)
+ def _reduce_9(val, _values, result)
+ result = val[0]
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 20)
+ def _reduce_10(val, _values, result)
+ result.merge!(val[1])
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 22)
+ def _reduce_11(val, _values, result)
+ result = {val[0] => val[1]}
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 24)
+ def _reduce_12(val, _values, result)
+ result = val
+ result
+ end
+.,.,
+
+module_eval(<<'.,.,', 'parser.y', 25)
+ def _reduce_13(val, _values, result)
+ result.push val[1]
+ result
+ end
+.,.,
+
+def _reduce_none(val, _values, result)
+ val[0]
+end
+
+end # class Parser
+
+end
76 lib/sgf_tools/parser.y
@@ -0,0 +1,76 @@
+
+ class Parser
+rule
+ collection: game_tree { result = val }
+ | collection game_tree { result.push val[1] }
+
+ game_tree: '(' sequence ')' { result = val[1] }
+ | '(' sequence collection ')'
+ {
+ val[2][0].first.branches = val[2][1..-1]
+ result = val[1] + val[2][0]
+ }
+
+ sequence: node { result = val }
+ | sequence node { result.push val[1] }
+
+ node: ';' { result = {} }
+ | ';' properties { result = Node.new(val[1], nil) }
+
+ properties: property { result = val[0] }
+ | properties property { result.merge!(val[1]) }
+
+ property: prop_ident prop_list { result = {val[0] => val[1]} }
+
+ prop_list: prop_value { result = val }
+ | prop_list prop_value { result.push val[1] }
+
+ end
+
+---- header
+require 'sgf_tools/tokenizer'
+
+module SgfTools
+ class Node
+ attr_accessor :properties
+ attr_accessor :branches
+
+ def initialize(prop, branches)
+ @properties = prop
+ @branches = branches
+ end
+
+ def inspect
+ str = ""
+ str << @properties.inspect
+ if !@branches.nil? && @branches.size > 0
+ str << " (#{@branches.size} variation(s))"
+ end
+ str
+ end
+
+ def [](key)
+ @properties[key]
+ end
+ end
+
+---- inner
+
+ def parse_file(filename)
+ @tokenizer = SgfTokenizer.new
+ @tokenizer.load_file(filename)
+ do_parse
+ end
+
+ def parse_string(str)
+ @tokenizer = SgfTokenizer.new
+ @tokenizer.scan_evaluate(str)
+ do_parse
+ end
+
+ def next_token
+ @tokenizer.next_token
+ end
+
+---- footer
+end
87 lib/sgf_tools/tokenizer.rb
@@ -0,0 +1,87 @@
+#
+# DO NOT MODIFY!!!!
+# This file is automatically generated by rex 1.0.0
+# from lexical definition file "lib/sgf_parser/tokenizer.rex".
+#
+
+require 'racc/parser'
+#
+# sgf.rex
+# lexical definition for sgf
+#
+# usage
+# rex sgf.rex
+#
+
+class SgfTokenizer < Racc::Parser
+ require 'strscan'
+
+ class ScanError < StandardError ; end
+
+ attr_reader :lineno
+ attr_reader :filename
+
+ def scan_setup ; end
+
+ def action &block
+ yield
+ end
+
+ def scan_str( str )
+ scan_evaluate str
+ do_parse
+ end
+
+ def load_file( filename )
+ @filename = filename
+ open(filename, "r") do |f|
+ scan_evaluate f.read
+ end
+ end
+
+ def scan_file( filename )
+ load_file filename
+ do_parse
+ end
+
+ def next_token
+ @rex_tokens.shift
+ end
+
+ def scan_evaluate( str )
+ scan_setup
+ @rex_tokens = []
+ @lineno = 1
+ ss = StringScanner.new(str)
+ state = nil
+ until ss.eos?
+ text = ss.peek(1)
+ @lineno += 1 if text == "\n"
+ case state
+ when nil
+ case
+ when (text = ss.scan(/[A-Z]+/))
+ @rex_tokens.push action { [:prop_ident, text] }
+
+ when (text = ss.scan(/\[[^\]]*\]/))
+ @rex_tokens.push action { [:prop_value, text[1..-2]] }
+
+ when (text = ss.scan(/\s+/))
+ ;
+
+ when (text = ss.scan(/./))
+ @rex_tokens.push action { [text, text] }
+
+ else
+ text = ss.string[ss.pos .. -1]
+ raise ScanError, "can not match: '" + text + "'"
+ end # if
+
+ else
+ raise ScanError, "undefined state: '" + state.to_s + "'"
+ end # case state
+ end # until ss
+ end # def scan_evaluate
+
+end # class
+
31 lib/sgf_tools/tokenizer.rex
@@ -0,0 +1,31 @@
+#
+# sgf.rex
+# lexical definition for sgf
+#
+# usage
+# rex sgf.rex
+#
+
+class SgfTokenizer
+
+macro
+ BLANK \s+
+
+rule
+
+# [:state] pattern [actions]
+
+# PropIdent
+ [A-Z]+ { [:prop_ident, text] }
+
+# PropValue
+ \[[^\]]*\] { [:prop_value, text[1..-2]] }
+
+# skip
+ {BLANK} # no action
+
+# identifier
+ . { [text, text] }
+
+end
+
2  lib/sgftools.rb
@@ -0,0 +1,2 @@
+
+require 'sgf_tools'
31 sgftools.gemspec
@@ -0,0 +1,31 @@
+DIR = File.dirname(__FILE__)
+LIB = File.join(DIR, *%w[lib sgf_tools.rb])
+VERSION = open(LIB) { |lib|
+ lib.each { |line|
+ if v = line[/^\s*VERSION\s*=\s*(['"])(\d\.\d\.\d)\1/, 2]
+ break v
+ end
+ }
+}
+
+SPEC = Gem::Specification.new do |s|
+ s.name = "sgfparser"
+ s.version = VERSION
+ s.platform = Gem::Platform::RUBY
+ s.authors = ["Pete Schwamb"]
+ s.email = ["pete@schwamb.net"]
+ s.homepage = "http://github.com/ps2/sgfparser"
+ s.summary = "A ruby library for working with SGF files"
+ s.description = <<-END_DESCRIPTION.gsub(/\s+/, " ").strip
+ This library reads sgf files, described at http://www.red-bean.com/sgf/
+ END_DESCRIPTION
+
+ s.required_rubygems_version = "~> 1.9.2"
+ s.required_rubygems_version = "~> 1.3.6"
+
+ s.add_development_dependency "rspec"
+
+ s.files = `git ls-files`.split("\n")
+ s.test_files = `git ls-files -- spec/*_spec.rb`.split("\n")
+ s.require_paths = %w[lib]
+end
21 spec/parser_spec.rb
@@ -0,0 +1,21 @@
+require 'sgftools'
+
+describe SgfTools::Parser do
+
+ it "should parse sgf with single game with branches" do
+ @simple_sgf = <<-END_OF_SGF
+(;FF[4]C[root](;C[a];C[b](;C[c])
+(;C[d];C[e]))
+(;C[f](;C[g];C[h];C[i])
+(;C[j])))
+END_OF_SGF
+ p = SgfTools::Parser.new
+ game = p.parse_string(@simple_sgf).first
+ game.size.should == 4
+ game[0].branches.should == nil
+ game[1].branches.size.should == 1
+ game[2].branches.should == nil
+ game[1].branches[0].size.should == 4
+ game[1].branches[0][1].branches.size.should == 1
+ end
+end
26 test/test_sgf_parse.rb
@@ -0,0 +1,26 @@
+
+require 'test/unit'
+require 'rubygems'
+require 'sgf_parser'
+
+class SgfParserTest < Test::Unit::TestCase
+
+ def test_parse_simple
+ p = SgfParser.new
+ simple_sgf = <<END_OF_SGF
+(;FF[4]C[root](;C[a];C[b](;C[c])
+(;C[d];C[e]))
+(;C[f](;C[g];C[h];C[i])
+(;C[j])))
+END_OF_SGF
+ game = p.parse_string(simple_sgf).first
+ assert_equal(4, game.size)
+ assert_equal(nil, game[0].branches)
+ assert_equal(1, game[1].branches.size)
+ assert_equal(nil, game[2].branches)
+ assert_equal(4, game[1].branches[0].size)
+ assert_equal(1, game[1].branches[0][1].branches.size)
+ end
+
+end
+
Please sign in to comment.
Something went wrong with that request. Please try again.