diff --git a/src/bindings/ruby/lib/hammer.rb b/src/bindings/ruby/lib/hammer.rb index 2032b36afbaa54841822802295a9799753ad199e..54b2501c71bd21bcae02b44977c9ea7e70e625fb 100644 --- a/src/bindings/ruby/lib/hammer.rb +++ b/src/bindings/ruby/lib/hammer.rb @@ -12,7 +12,7 @@ require 'hammer/parser_builder' parser = Hammer::Parser.build do token 'blah' - ch 'a' + ch 'a'.ord choice { sequence { token 'abc' @@ -57,3 +57,17 @@ parser = h.token(s) p parser.parse 'BLAH' # => false s.upcase! p parser.parse 'BLAH' # => false + + +x = nil +parser = Hammer::Parser.build { + token 'abc' + x = indirect + end_p +} +x.bind(h.token('abd')) + +p parser.parse 'abcabdabd' +p parser.parse 'abcabd' +p parser.parse 'abdabd' +p parser.parse 'abd' diff --git a/src/bindings/ruby/lib/hammer/parser.rb b/src/bindings/ruby/lib/hammer/parser.rb index 12d047629308e5546bb1da9fa6cfc781d84a7349..8ae6ee8bf5586ab65c7def6a1e856259757828ec 100644 --- a/src/bindings/ruby/lib/hammer/parser.rb +++ b/src/bindings/ruby/lib/hammer/parser.rb @@ -3,9 +3,22 @@ module Hammer # Don't create new instances with Hammer::Parser.new, # use the constructor methods instead (i.e. Hammer::Parser.int64 etc.) - def initialize + # + # name: Name of the parser. Should be a symbol. + # h_parser: The pointer to the parser as returned by hammer. + # dont_gc: Pass additional data that's used by the parser and needs to be saved from the garbage collector. + def initialize(name, h_parser, dont_gc) + @name = name + @h_parser = h_parser + @dont_gc = dont_gc end + attr_reader :name + attr_reader :h_parser + + # Parse the given data. Returns true if successful, false otherwise. + # + # data: A string containing the data to parse. def parse(data) raise RuntimeError, '@h_parser is nil' if @h_parser.nil? raise ArgumentError, 'expecting a String' unless data.is_a? String # TODO: Not needed, FFI checks that. @@ -16,24 +29,24 @@ module Hammer !result.null? end + # Binds an indirect parser. + def bind(other_parser) + raise RuntimeError, 'can only bind indirect parsers' unless self.name == :indirect + Hammer::Internal.h_bind_indirect(self.h_parser, other_parser.h_parser) + end + def self.token(string) h_string = string.dup h_parser = Hammer::Internal.h_token(h_string, h_string.length) - parser = Hammer::Parser.new - parser.instance_variable_set :@h_parser, h_parser - # prevent string from getting garbage-collected - parser.instance_variable_set :@h_string, h_string - return parser + return Hammer::Parser.new(:token, h_parser, h_string) end def self.ch(num) - raise ArgumentError, 'expecting a Fixnum in 0..255', unless num.is_a?(Fixnum) and num.between?(0, 255) + raise ArgumentError, 'expecting a Fixnum in 0..255' unless num.is_a?(Fixnum) and num.between?(0, 255) h_parser = Hammer::Internal.h_ch(num) - parser = Hammer::Parser.new - parser.instance_variable_set :@h_parser, h_parser - return parser + return Hammer::Parser.new(:ch, h_parser, nil) end # Defines a parser constructor with the given name. @@ -41,7 +54,7 @@ module Hammer # hammer_function: name of the hammer function to call (default: 'h_'+name) # varargs: Whether the function is taking a variable number of arguments (default: false) def self.define_parser(name, options = {}) - hammer_function = options[:hammer_function] || ('h_' + name.to_s) + hammer_function = options[:hammer_function] || ('h_' + name.to_s).to_sym varargs = options[:varargs] || false # Define a new class method @@ -54,10 +67,7 @@ module Hammer end h_parser = Hammer::Internal.send hammer_function, *args - parser = Hammer::Parser.new - parser.instance_variable_set :@h_parser, h_parser - parser.instance_variable_set :@sub_parsers, parsers # store sub parsers to prevent them from being garbage-collected - return parser + return Hammer::Parser.new(name, h_parser, parsers) end end private_class_method :define_parser @@ -77,8 +87,8 @@ module Hammer define_parser :left define_parser :right define_parser :middle - define_parser :end - define_parser :nothing + define_parser :end_p + define_parser :nothing_p define_parser :butnot define_parser :difference define_parser :xor @@ -92,10 +102,7 @@ module Hammer define_parser :length_value define_parser :and define_parser :not - - # TODO: If indirect, add a bind method that calls h_bind_indirect define_parser :indirect - attr_reader :h_parser end end diff --git a/src/bindings/ruby/lib/hammer/parser_builder.rb b/src/bindings/ruby/lib/hammer/parser_builder.rb index d1618c51860ffd882294b55e3a6d9348eb865873..d610db9cec397c475fbcd939c90f61de74a2ab94 100644 --- a/src/bindings/ruby/lib/hammer/parser_builder.rb +++ b/src/bindings/ruby/lib/hammer/parser_builder.rb @@ -75,8 +75,8 @@ module Hammer define_parser :left define_parser :right define_parser :middle - define_parser :end - define_parser :nothing + define_parser :end_p + define_parser :nothing_p define_parser :butnot define_parser :difference define_parser :xor @@ -90,7 +90,25 @@ module Hammer define_parser :length_value define_parser :and define_parser :not - define_parser :indirect + + # At least indirect must return the parser instead of the builder, so it can be stored in a variable. + # Other possible solution: + # Make indirect take a name parameter, and use the name to bind it later. + # Example: + # p = Hammer::Parser.build { indirect(:the_name) } + # p.bind(:the_name, inner_parser) + # (store names and parsers in hash in the builder, + # when building merge hashes from sub builders and store everything in the resulting sequence or choice. + # make Parser#bind take and optional symbol. if it is given, the name is looked up in the table.) + # TODO: + # Think about this more. + # Do we need to be able to build parsers by chaining function calls? DSL should be sufficient. + # If yes, the parser methods in this class should not return "self", but the Hammer::Parser object they create. + def indirect + parser = Hammer::Parser.indirect + @parsers << parser + return parser + end end # class ParserBuilder