diff --git a/src/bindings/ruby/README.md b/src/bindings/ruby/README.md index 5ed26aebf351da13b7687480d784ce2e7d6843fe..1876fc7317823d0234c6157b9128c101e8d1b90c 100644 --- a/src/bindings/ruby/README.md +++ b/src/bindings/ruby/README.md @@ -47,7 +47,7 @@ Also possible: ```ruby parser = Hammer::ParserBuilder.new .token('Hello ') - .choice(Hammer::Parser::Token.new('Mom'), Hammer::Parser::Token.new('Dad')) + .choice(Hammer::Parser.token('Mom'), Hammer::Parser.token('Dad')) .token('!') .build ``` @@ -56,7 +56,7 @@ More like hammer in C: ```ruby h = Hammer::Parser -parser = h.sequence(h.token('Hello'), h.choice(h.token('Mom'), h.token('Dad')), h.token('!')) +parser = h.sequence(h.token('Hello '), h.choice(h.token('Mom'), h.token('Dad')), h.token('!')) ``` ### Parsing diff --git a/src/bindings/ruby/lib/hammer.rb b/src/bindings/ruby/lib/hammer.rb index 2699d96f982a5f1e3dccf078d0567963f3e24ce7..0f10ab182f82755f0488382d8e480967c336fff6 100644 --- a/src/bindings/ruby/lib/hammer.rb +++ b/src/bindings/ruby/lib/hammer.rb @@ -32,16 +32,6 @@ if parser p parser.parse 'blah' end -parser = Hammer::Parser::Sequence.new( - Hammer::Parser::Token.new('Hello '), - Hammer::Parser::Choice.new( - Hammer::Parser::Token.new('Mom'), - Hammer::Parser::Token.new('Dad') - ), - Hammer::Parser::Token.new('!') -) -p parser.parse 'Hello Mom!' - parser = Hammer::Parser.build { token 'Hello ' choice { @@ -54,12 +44,11 @@ p parser.parse 'Hello Mom!' parser = Hammer::ParserBuilder.new .token('Hello ') - .choice(Hammer::Parser::Token.new('Mom'), Hammer::Parser::Token.new('Dad')) + .choice(Hammer::Parser.token('Mom'), Hammer::Parser.token('Dad')) .token('!') .build p parser.parse 'Hello Mom!' -# not yet working -#h = Hammer::Parser -#parser = h.sequence(h.token('Hello'), h.choice(h.token('Mom'), h.token('Dad')), h.token('!')) -#p parser.parse 'Hello Mom!' +h = Hammer::Parser +parser = h.sequence(h.token('Hello '), h.choice(h.token('Mom'), h.token('Dad')), h.token('!')) +p parser.parse 'Hello Mom!' diff --git a/src/bindings/ruby/lib/hammer/parser.rb b/src/bindings/ruby/lib/hammer/parser.rb index a7b75e273a820963ce0132270b8ad049f67f2e4d..5d1e8e57c1a0c6704608b4a9336fb1e624d6b48f 100644 --- a/src/bindings/ruby/lib/hammer/parser.rb +++ b/src/bindings/ruby/lib/hammer/parser.rb @@ -14,95 +14,51 @@ module Hammer !result.null? end - class Token < Parser - def initialize(string) - @h_parser = Hammer::Internal.h_token(string, string.length) - end + def self.token(string) + h_parser = Hammer::Internal.h_token(string, string.length) + + parser = Hammer::Parser.new + parser.instance_variable_set :@h_parser, h_parser + return parser end - class Ch < Parser - def initialize(char) + def self.ch(char) # TODO: Really? Should probably accept Fixnum in appropriate range - # Also, char.ord gives unexptected results if you pass e.g. Japanese characters: '今'.ord == 20170; Hammer::Parser::Ch.new('今').parse(202.chr) == true + # Also, char.ord gives unexpected results if you pass e.g. Japanese characters: '今'.ord == 20170; Hammer::Parser::Ch.new('今').parse(202.chr) == true # Not really unexpected though, since 20170 & 255 == 202. # But probably it's better to use Ch for Fixnum in 0..255 only, and only Token for strings. raise ArgumentError, 'expecting a one-character String' unless char.is_a?(String) && char.length == 1 - @h_parser = Hammer::Internal.h_ch(char.ord) - end + h_parser = Hammer::Internal.h_ch(char.ord) + + parser = Hammer::Parser.new + parser.instance_variable_set :@h_parser, h_parser + return parser end - class Sequence < Parser - def initialize(*parsers) - #args = [] - #parsers.each { |p| args += [:pointer, p.h_parser] } + def self.sequence(*parsers) args = parsers.flat_map { |p| [:pointer, p.h_parser] } - @h_parser = Hammer::Internal.h_sequence(*args, :pointer, nil) - @sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though) + h_parser = Hammer::Internal.h_sequence(*args, :pointer, nil) + sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though) # TODO: Use (managed?) FFI struct instead of void pointers - end + + parser = Hammer::Parser.new + parser.instance_variable_set :@h_parser, h_parser + parser.instance_variable_set :@sub_parsers, sub_parsers + return parser end - class Choice < Parser - def initialize(*parsers) - #args = [] - #parsers.each { |p| args += [:pointer, p.h_parser] } + def self.choice(*parsers) args = parsers.flat_map { |p| [:pointer, p.h_parser] } - @h_parser = Hammer::Internal.h_choice(*args, :pointer, nil) - @sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though) + h_parser = Hammer::Internal.h_choice(*args, :pointer, nil) + sub_parsers = parsers # store them so they don't get garbage-collected (probably not needed, though) # TODO: Use (managed?) FFI struct instead of void pointers - end - end - # Define parsers that take some number of other parsers - # TODO: Maybe use -1 for variable number, and use this for Sequence and Choice too - # TODO: Refactor this code as a method? And call it like: define_parser :Int64, :h_int64, 0 - [ - [:Int64, :h_int64, 0], - [:Int32, :h_int32, 0], - [:Int16, :h_int16, 0], - [:Int8, :h_int8, 0], - [:UInt64, :h_uint64, 0], - [:UInt32, :h_uint32, 0], - [:UInt16, :h_uint16, 0], - [:UInt8, :h_uint8, 0], - [:Whitespace, :h_whitespace, 1], - [:Left, :h_left, 2], - [:Right, :h_right, 2], - [:Middle, :h_middle, 3], - [:End, :h_end_p, 0], - [:Nothing, :h_nothing_p, 0], - [:ButNot, :h_butnot, 2], - [:Difference, :h_difference, 2], - [:Xor, :h_xor, 2], - [:Many, :h_many, 1], - [:Many1, :h_many1, 1] - ].each do |class_name, h_function_name, parameter_count| - # Create new subclass of Hammer::Parser - klass = Class.new(Hammer::Parser) do - # Need to use define_method instead of def to be able to access h_function_name in the method's body - define_method :initialize do |*parsers| - # Checking parameter_count is not really needed, since the h_* methods will complain anyways - @h_parser = Hammer::Internal.send(h_function_name, *parsers.map(&:h_parser)) - # TODO: Do we need to store sub-parsers to prevent them from getting garbage-collected? - end - end - # Register class with name Hammer::Parser::ClassName - Hammer::Parser.const_set class_name, klass + parser = Hammer::Parser.new + parser.instance_variable_set :@h_parser, h_parser + parser.instance_variable_set :@sub_parsers, sub_parsers + return parser end - # TODO: - # Hammer::Parser::Token.new('...') is a bit too long. Find a shorter way to use the parsers. - # Maybe: - # class Hammer::Parser - # def self.token(*args) - # Hammer::Parser::Token.new(*args) - # end - # end - # Can create functions like that automatically. Usage: - # h = Hammer::Parser - # parser = h.sequence(h.token('blah'), h.token('other_token')) - # Looks almost like hammer in C! - # Defines a parser constructor with the given name. # Options: # hammer_function: name of the hammer function to call (default: 'h_'+name) diff --git a/src/bindings/ruby/lib/hammer/parser_builder.rb b/src/bindings/ruby/lib/hammer/parser_builder.rb index 2f36c844dea5ed1ebc36a89d9cee06ff56a598af..de8ce0d264a0c398396870db06d60c064177c2c4 100644 --- a/src/bindings/ruby/lib/hammer/parser_builder.rb +++ b/src/bindings/ruby/lib/hammer/parser_builder.rb @@ -25,7 +25,7 @@ module Hammer def build if @parsers.length > 1 - Hammer::Parser::Sequence.new(*@parsers) + Hammer::Parser.sequence(*@parsers) else @parsers.first end @@ -40,14 +40,12 @@ module Hammer def token(str) - #@h_parsers << Hammer::Internal.h_token(str, str.length) - @parsers << Hammer::Parser::Token.new(str) + @parsers << Hammer::Parser.token(str) return self end def ch(char) - #@h_parsers << Hammer::Internal.h_ch(char.ord) - @parsers << Hammer::Parser::Ch.new(char) + @parsers << Hammer::Parser.ch(char) return self end @@ -57,17 +55,13 @@ module Hammer @parsers += parsers @parsers << Docile.dsl_eval(ParserBuilder.new, &block).build if block_given? return self - #builder = Hammer::ParserBuilder.new - #builder.instance_eval &block - #@parsers << Hammer::Parser::Sequence.new(*builder.parsers) - ## TODO: Save original receiver and redirect missing methods! end def choice(*parsers, &block) if block_given? parsers += Docile.dsl_eval(ParserBuilder.new, &block).parsers end - @parsers << Hammer::Parser::Choice.new(*parsers) + @parsers << Hammer::Parser.choice(*parsers) return self end end