lib/bindata/dsl.rb in bindata-2.1.0 vs lib/bindata/dsl.rb in bindata-2.2.0
- old
+ new
@@ -65,26 +65,41 @@
raise "unknown parser type #{parser_type}" unless parser_abilities[parser_type]
@the_class = the_class
@parser_type = parser_type
@validator = DSLFieldValidator.new(the_class, self)
- @endian_handler = DSLBigAndLittleEndianHandler.new(the_class)
@endian = nil
end
attr_reader :parser_type
def endian(endian = nil)
if endian
set_endian(endian)
elsif @endian.nil?
- pendian = parent_attribute(:endian, nil)
- set_endian(pendian) if pendian
+ set_endian(parent_attribute(:endian))
end
@endian
end
+ def search_prefix(*args)
+ unless defined? @search_prefix
+ @search_prefix = parent_attribute(:search_prefix, []).dup
+ end
+
+ prefix = args.collect { |name| name.to_sym }.compact
+ if prefix.size > 0
+ if has_fields?
+ dsl_raise SyntaxError, "search_prefix must be called before defining fields"
+ end
+
+ @search_prefix = prefix.concat(@search_prefix)
+ end
+
+ @search_prefix
+ end
+
def hide(*args)
if option?(:hidden_fields)
hidden = args.collect { |name| name.to_sym }
unless defined? @hide
@@ -96,12 +111,12 @@
end
end
def fields
unless defined? @fields
- fields = @endian_handler.ancestor_fields || parent_attribute(:fields)
- @fields = SanitizedFields.new(endian)
+ fields = parent_fields
+ @fields = SanitizedFields.new(hints)
@fields.copy_fields(fields) if fields
end
@fields
end
@@ -109,69 +124,74 @@
def dsl_params
send(parser_abilities[@parser_type].at(0))
end
def method_missing(*args, &block)
- if endian == :big_and_little
- @endian_handler.forward_field_definition(*args, &block)
- else
- parse_and_append_field(*args, &block)
- end
+ ensure_hints
+ parse_and_append_field(*args, &block)
end
#-------------
private
def parser_abilities
@abilities ||= {
:struct => [:to_struct_params, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
:array => [:to_array_params, [:multiple_fields, :optional_fieldnames]],
- :buffer => [:to_array_params, [:multiple_fields, :optional_fieldnames]],
+ :buffer => [:to_array_params, [:multiple_fields, :optional_fieldnames, :hidden_fields]],
:choice => [:to_choice_params, [:multiple_fields, :all_or_none_fieldnames, :fieldnames_are_values]],
:primitive => [:to_struct_params, [:multiple_fields, :optional_fieldnames]]
}
end
def option?(opt)
parser_abilities[@parser_type].at(1).include?(opt)
end
+ def ensure_hints
+ endian
+ search_prefix
+ end
+
+ def hints
+ {
+ :endian => endian,
+ :search_prefix => search_prefix,
+ }
+ end
+
def set_endian(endian)
- if has_fields?
- dsl_raise SyntaxError, "endian must not be called after defining fields"
- end
- if not valid_endian?(endian)
- dsl_raise ArgumentError, "unknown value for endian '#{endian}'"
- end
+ if endian
+ if has_fields?
+ dsl_raise SyntaxError, "endian must be called before defining fields"
+ end
+ if not valid_endian?(endian)
+ dsl_raise ArgumentError, "unknown value for endian '#{endian}'"
+ end
- if endian == :big_and_little
- @endian_handler.prepare_subclasses
- end
+ if endian == :big_and_little
+ DSLBigAndLittleEndianHandler.handle(@the_class)
+ end
- @endian = endian
+ @endian = endian
+ end
end
- def has_fields?
- @fields && @fields.length > 0
- end
-
def valid_endian?(endian)
[:big, :little, :big_and_little].include?(endian)
end
- def parent_attribute(attr, default = nil)
- parent = @the_class.superclass
- parser = parent.respond_to?(:dsl_parser) ? parent.dsl_parser : nil
- if parser and parser.respond_to?(attr)
- parser.send(attr)
- else
- default
- end
+ def parent_fields
+ parent_attribute(:fields)
end
+ def has_fields?
+ @fields && @fields.length > 0
+ end
+
def parse_and_append_field(*args, &block)
- parser = DSLFieldParser.new(endian, *args, &block)
+ parser = DSLFieldParser.new(hints, *args, &block)
begin
@validator.validate_field(parser.name)
append_field(parser.type, parser.name, parser.params)
rescue Exception => err
dsl_raise err.class, err.message
@@ -182,10 +202,20 @@
fields.add_field(type, name, params)
rescue BinData::UnRegisteredTypeError => err
raise TypeError, "unknown type '#{err.message}'"
end
+ def parent_attribute(attr, default = nil)
+ parent = @the_class.superclass
+ parser = parent.respond_to?(:dsl_parser) ? parent.dsl_parser : nil
+ if parser and parser.respond_to?(attr)
+ parser.send(attr)
+ else
+ default
+ end
+ end
+
def dsl_raise(exception, message)
backtrace = caller
backtrace.shift while %r{bindata/dsl.rb} =~ backtrace.first
raise exception, message + " in #{@the_class}", backtrace
@@ -217,10 +247,13 @@
def to_struct_params
result = {:fields => fields}
if not endian.nil?
result[:endian] = endian
end
+ if not search_prefix.empty?
+ result[:search_prefix] = search_prefix
+ end
if option?(:hidden_fields) and not hide.empty?
result[:hide] = hide
end
result
@@ -229,91 +262,100 @@
# Handles the :big_and_little endian option.
# This option creates two subclasses, each handling
# :big or :little endian.
class DSLBigAndLittleEndianHandler
- def initialize(the_class)
- @the_class = the_class
- end
+ class << self
+ def handle(bnl_class)
+ make_class_abstract(bnl_class)
+ create_subclasses_with_endian(bnl_class)
+ override_new_in_class(bnl_class)
+ delegate_field_creation(bnl_class)
+ fixup_subclass_hierarchy(bnl_class)
+ end
- def prepare_subclasses
- create_subclasses_with_endian
- make_class_abstract
- override_new_in_class
- end
+ def make_class_abstract(bnl_class)
+ bnl_class.send(:unregister_self)
+ end
- def forward_field_definition(*args, &block)
- class_with_endian(@the_class, :big).send(*args, &block)
- class_with_endian(@the_class, :little).send(*args, &block)
- end
-
- def ancestor_fields
- if subclass_of_big_and_little_endian?
- pparent = @the_class.superclass.superclass
- ancestor_with_endian = class_with_endian(pparent, @the_class.endian)
- obj_attribute(ancestor_with_endian, :fields)
- else
- nil
+ def create_subclasses_with_endian(bnl_class)
+ instance_eval "class ::#{bnl_class}Be < ::#{bnl_class}; endian :big; end"
+ instance_eval "class ::#{bnl_class}Le < ::#{bnl_class}; endian :little; end"
end
- end
- #-------------
- private
+ def override_new_in_class(bnl_class)
+ endian_classes = {
+ :big => class_with_endian(bnl_class, :big),
+ :little => class_with_endian(bnl_class, :little),
+ }
+ bnl_class.define_singleton_method(:new) do |*args|
+ if self == bnl_class
+ value, options, parent = arg_processor.separate_args(self, args)
+ delegate = endian_classes[options[:endian]]
+ return delegate.new(*args) if delegate
+ end
- def create_subclasses_with_endian
- instance_eval "class ::#{@the_class}Be < ::#{@the_class}; endian :big; end"
- instance_eval "class ::#{@the_class}Le < ::#{@the_class}; endian :little; end"
- end
+ super(*args)
+ end
+ end
- def make_class_abstract
- @the_class.send(:unregister_self)
- end
+ def delegate_field_creation(bnl_class)
+ endian_classes = {
+ :big => class_with_endian(bnl_class, :big),
+ :little => class_with_endian(bnl_class, :little),
+ }
- def override_new_in_class
- saved_class = @the_class
- endian_classes = {
- :big => class_with_endian(saved_class, :big),
- :little => class_with_endian(saved_class, :little),
- }
- @the_class.define_singleton_method(:new) do |*args|
- if self == saved_class
- value, options, parent = arg_processor.separate_args(self, args)
- delegate = endian_classes[options[:endian]]
- return delegate.new(*args) if delegate
+ parser = bnl_class.dsl_parser
+ parser.define_singleton_method(:parse_and_append_field) do |*args, &block|
+ endian_classes[:big].send(*args, &block)
+ endian_classes[:little].send(*args, &block)
end
-
- super(*args)
end
- end
- def subclass_of_big_and_little_endian?
- parent = @the_class.superclass
- pparent = parent.superclass
+ def fixup_subclass_hierarchy(bnl_class)
+ parent = bnl_class.superclass
+ if obj_attribute(parent, :endian) == :big_and_little
+ be_subclass = class_with_endian(bnl_class, :big)
+ be_parent = class_with_endian(parent, :big)
+ be_fields = obj_attribute(be_parent, :fields)
- obj_attribute(parent, :endian) == :big_and_little and
- obj_attribute(pparent, :endian) == :big_and_little and
- [:big, :little].include?(@the_class.endian)
- end
+ le_subclass = class_with_endian(bnl_class, :little)
+ le_parent = class_with_endian(parent, :little)
+ le_fields = obj_attribute(le_parent, :fields)
- def class_with_endian(class_name, endian)
- RegisteredClasses.lookup(class_name, endian)
- end
+ be_subclass.dsl_parser.define_singleton_method(:parent_fields) do
+ be_fields
+ end
+ le_subclass.dsl_parser.define_singleton_method(:parent_fields) do
+ le_fields
+ end
+ end
+ end
- def obj_attribute(obj, attr, default = nil)
- parser = obj.respond_to?(:dsl_parser) ? obj.dsl_parser : nil
- if parser and parser.respond_to?(attr)
- parser.send(attr)
- else
- default
+ def class_with_endian(class_name, endian)
+ hints = {
+ :endian => endian,
+ :search_prefix => class_name.dsl_parser.search_prefix,
+ }
+ RegisteredClasses.lookup(class_name, hints)
end
+
+ def obj_attribute(obj, attr, default = nil)
+ parser = obj.respond_to?(:dsl_parser) ? obj.dsl_parser : nil
+ if parser and parser.respond_to?(attr)
+ parser.send(attr)
+ else
+ default
+ end
+ end
end
end
# Extracts the details from a field declaration.
class DSLFieldParser
- def initialize(endian, symbol, *args, &block)
- @endian = endian
+ def initialize(hints, symbol, *args, &block)
+ @hints = hints
@type = symbol
@name = name_from_field_declaration(args)
@params = params_from_field_declaration(args, &block)
end
@@ -353,10 +395,11 @@
:struct => BinData::Struct
}
if bindata_classes.include?(@type)
parser = DSLParser.new(bindata_classes[@type], @type)
- parser.endian(@endian)
+ parser.endian(@hints[:endian])
+ parser.search_prefix(*@hints[:search_prefix])
parser.instance_eval(&block)
parser.dsl_params
else
{}