Class: Hammer::Parser
- Inherits:
-
Object
- Object
- Hammer::Parser
- Defined in:
- lib/hammer/parser.rb,
lib/hammer/parser_builder.rb
Constant Summary collapse
- @@saved_objects =
Hammer::Internal::DynamicVariable.new nil, "Hammer parse-time pins"
Instance Attribute Summary collapse
-
#dont_gc ⇒ Object
readonly
Returns the value of attribute dont_gc.
-
#h_parser ⇒ Object
readonly
Returns the value of attribute h_parser.
-
#name ⇒ Object
readonly
dont_gc is required to build a fuzzer from the declaration of Hammer::Parser object.
Class Method Summary collapse
-
.action(parser, action = nil, &block) ⇒ Object
Can pass the action either as a Proc in second parameter, or as block.
-
.attr_bool(parser, predicate = nil, &block) ⇒ Object
Can pass the predicate either as a Proc in second parameter, or as block.
- .build(&block) ⇒ Object
- .build_choice(&block) ⇒ Object
- .ch(ch) ⇒ Object
- .ch_parser_wrapper(parser) ⇒ Object
- .ch_range(ch1, ch2) ⇒ Object
- .in(charset) ⇒ Object
- .int_range(parser, i1, i2) ⇒ Object
- .not_in(charset) ⇒ Object
- .repeat_n(parser, count) ⇒ Object
- .token(string) ⇒ Object
Instance Method Summary collapse
-
#bind(other_parser) ⇒ Object
Binds an indirect parser.
-
#initialize(name, h_parser, dont_gc = []) ⇒ Parser
constructor
Don’t create new instances with Hammer::Parser.new, use the constructor methods instead (i.e. Hammer::Parser.int64 etc.).
-
#parse(data) ⇒ Object
Parse the given data.
Constructor Details
#initialize(name, h_parser, dont_gc = []) ⇒ Parser
Don’t create new instances with Hammer::Parser.new, use the constructor methods instead (i.e. Hammer::Parser.int64 etc.)
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 (at least as long this object lives).
14 15 16 17 18 19 20 |
# File 'lib/hammer/parser.rb', line 14 def initialize(name, h_parser, dont_gc=[]) @name = name @h_parser = h_parser # Always store as array, so we can easily add stuff later on dont_gc = [dont_gc] unless dont_gc.is_a? Array @dont_gc = dont_gc.dup end |
Instance Attribute Details
#dont_gc ⇒ Object (readonly)
Returns the value of attribute dont_gc.
25 26 27 |
# File 'lib/hammer/parser.rb', line 25 def dont_gc @dont_gc end |
#h_parser ⇒ Object (readonly)
Returns the value of attribute h_parser.
24 25 26 |
# File 'lib/hammer/parser.rb', line 24 def h_parser @h_parser end |
#name ⇒ Object (readonly)
dont_gc is required to build a fuzzer from the declaration of Hammer::Parser object.
23 24 25 |
# File 'lib/hammer/parser.rb', line 23 def name @name end |
Class Method Details
.action(parser, action = nil, &block) ⇒ Object
Can pass the action either as a Proc in second parameter, or as block.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/hammer/parser.rb', line 59 def self.action(parser, action=nil, &block) action = block if action.nil? raise ArgumentError, 'no action' if action.nil? real_action = Proc.new {|hpr| ret = action.call(hpr.ast) # Pin the result @@saved_objects.value << ret hpt = hpr.arena_alloc(Hammer::Internal::HParsedToken) unless hpr.ast.nil? hpt[:index] = hpr[:ast][:index] hpt[:bit_offset] = hpr[:ast][:bit_offset] end hpt[:token_type] = :"com.upstandinghackers.hammer.ruby.object" hpt[:data][:uint] = ret.object_id hpt } h_parser = Hammer::Internal.h_action(parser.h_parser, real_action) return Hammer::Parser.new(:action, h_parser, [parser, action, real_action]) end |
.attr_bool(parser, predicate = nil, &block) ⇒ Object
Can pass the predicate either as a Proc in second parameter, or as block.
82 83 84 85 86 87 88 89 90 |
# File 'lib/hammer/parser.rb', line 82 def self.attr_bool(parser, predicate=nil, &block) predicate = block if predicate.nil? raise ArgumentError, 'no predicate' if predicate.nil? real_pred = Proc.new {|hpr| predicate.call hpr.ast} h_parser = Hammer::Internal.h_attr_bool(parser.h_parser, real_pred) return Hammer::Parser.new(:attr_bool, h_parser, [parser, predicate, real_pred]) end |
.build(&block) ⇒ Object
9 10 11 |
# File 'lib/hammer/parser_builder.rb', line 9 def self.build(&block) ParserBuilder.new.sequence(&block).build end |
.build_choice(&block) ⇒ Object
13 14 15 |
# File 'lib/hammer/parser_builder.rb', line 13 def self.build_choice(&block) ParserBuilder.new.choice(&block).build end |
.ch(ch) ⇒ Object
130 131 132 133 134 135 |
# File 'lib/hammer/parser.rb', line 130 def self.ch(ch) num = marshal_ch_arg(ch) h_parser = Hammer::Internal.h_ch(num) return ch_parser_wrapper(Hammer::Parser.new(:ch, h_parser, nil)) end |
.ch_parser_wrapper(parser) ⇒ Object
126 127 128 |
# File 'lib/hammer/parser.rb', line 126 def self.ch_parser_wrapper(parser) return Hammer::Parser.action(parser) {|x| x.data.chr} end |
.ch_range(ch1, ch2) ⇒ Object
137 138 139 140 141 142 |
# File 'lib/hammer/parser.rb', line 137 def self.ch_range(ch1, ch2) ch1 = marshal_ch_arg(ch1) ch2 = marshal_ch_arg(ch2) h_parser = Hammer::Internal.h_ch_range(ch1, ch2) return ch_parser_wrapper(Hammer::Parser.new(:ch_range, h_parser, nil)) end |
.in(charset) ⇒ Object
149 150 151 152 153 154 |
# File 'lib/hammer/parser.rb', line 149 def self.in(charset) raise ArgumentError, "Expected a String" unless charset.is_a?(String) ibuf = FFI::MemoryPointer.from_string(charset) h_parser = Hammer::Internal.h_in(ibuf, charset.bytesize) return ch_parser_wrapper(Hammer::Parser.new(:in, h_parser, nil)) end |
.int_range(parser, i1, i2) ⇒ Object
144 145 146 147 |
# File 'lib/hammer/parser.rb', line 144 def self.int_range(parser, i1, i2) h_parser = Hammer::Internal.h_int_range(parser.h_parser, i1, i2) return Hammer::Parser.new(:int_range, h_parser, [parser]) end |
.not_in(charset) ⇒ Object
161 162 163 164 165 166 |
# File 'lib/hammer/parser.rb', line 161 def self.not_in(charset) raise ArgumentError, "Expected a String" unless charset.is_a?(String) ibuf = FFI::MemoryPointer.from_string(charset) h_parser = Hammer::Internal.h_not_in(ibuf, charset.bytesize) return ch_parser_wrapper(Hammer::Parser.new(:not_in, h_parser, nil)) end |
.repeat_n(parser, count) ⇒ Object
156 157 158 159 |
# File 'lib/hammer/parser.rb', line 156 def self.repeat_n(parser, count) h_parser = Hammer::Internal.h_repeat_n(parser.h_parser, count) return Hammer::Parser.new(:repeat_n, h_parser, nil) end |
.token(string) ⇒ Object
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/hammer/parser.rb', line 92 def self.token(string) # Need to copy string to a memory buffer (not just string.dup) # * Original string might be modified, this must not affect existing tokens # * We need a constant memory address (Ruby string might be moved around by the Ruby VM) buffer = FFI::MemoryPointer.from_string(string) h_parser = Hammer::Internal.h_token(buffer, buffer.size-1) # buffer.size includes the null byte at the end encoding = string.encoding wrapping_action = Proc.new {|hpr| hstr = hpr.arena_alloc(Hammer::Internal::HString) hstr[:content] = hpr[:ast][:data][:bytes] hstr[:encoding] = encoding.object_id hpt = hpr.arena_alloc(Hammer::Internal::HParsedToken) hpt[:token_type] = :"com.upstandinghackers.hammer.ruby.encodedStr" hpt[:data][:user] = hstr.to_ptr hpt[:bit_offset] = hpr[:ast][:bit_offset] hpt[:index] = hpr[:ast][:index] hpt } wrapped_parser = Hammer::Internal.h_action(h_parser, wrapping_action) return Hammer::Parser.new(:token, wrapped_parser, [buffer, string, encoding, wrapping_action, h_parser]) end |
Instance Method Details
#bind(other_parser) ⇒ Object
Binds an indirect parser.
52 53 54 55 56 |
# File 'lib/hammer/parser.rb', line 52 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) @dont_gc << other_parser end |
#parse(data) ⇒ Object
Parse the given data. Returns the parse result if successful, nil otherwise.
data: A string containing the data to parse.
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/hammer/parser.rb', line 30 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. ibuf = FFI::MemoryPointer.from_string(data) @@saved_objects.with([]) do result = Hammer::Internal.h_parse(@h_parser, ibuf, data.bytesize) # Don't include the trailing null if result.null? return nil else # NOTE: # The parse result *must* hold a reference to the parser that created it! # Otherwise, the parser might get garbage-collected while the result is still valid. # Any pointers to token strings will then be invalid. result.instance_variable_set :@parser, self result.instance_variable_set :@pins, @@saved_objects.value return result end end end |