class BinData::DSLMixin::DSLParser
* params
is a hash containing any parameters
* name
is the (possible optional) name of the field
* type
is the under_scored name of a registered type
where:
type name, params
A DSLParser parses and accumulates field definitions of the form
def append_field(type, name, params)
def append_field(type, name, params) fields.add_field(type, name, params) rescue BinData::UnRegisteredTypeError => e raise TypeError, "unknown type '#{e.message}'" end
def dsl_params
def dsl_params abilities = parser_abilities[@parser_type] send(abilities.at(0), abilities.at(1)) end
def dsl_raise(exception, msg)
def dsl_raise(exception, msg) backtrace = caller backtrace.shift while %r{bindata/dsl.rb}.match?(backtrace.first) raise exception, "#{msg} in #{@the_class}", backtrace end
def endian(endian = nil)
def endian(endian = nil) if endian set_endian(endian) elsif @endian.nil? set_endian(parent_attribute(:endian)) end @endian end
def ensure_hints
def ensure_hints endian search_prefix end
def fields
def fields @fields ||= SanitizedFields.new(hints, parent_fields) end
def fields?
def fields? defined?(@fields) && !@fields.empty? end
def hide(*args)
def hide(*args) if option?(:hidden_fields) @hide ||= parent_attribute(:hide, []).dup hidden = args.collect(&:to_sym).compact @hide.concat(hidden) @hide end end
def hints
def hints { endian: endian, search_prefix: search_prefix } end
def initialize(the_class, parser_type)
def initialize(the_class, parser_type) 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 = nil end
def method_missing(*args, &block)
def method_missing(*args, &block) ensure_hints parse_and_append_field(*args, &block) end
def option?(opt)
def option?(opt) parser_abilities[@parser_type].at(2).include?(opt) end
def parent_attribute(attr, default = nil)
def parent_attribute(attr, default = nil) parent = @the_class.superclass parser = parent.respond_to?(:dsl_parser) ? parent.dsl_parser : nil if parser&.respond_to?(attr) parser.send(attr) else default end end
def parent_fields
def parent_fields parent_attribute(:fields) end
def parse_and_append_field(*args, &block)
def parse_and_append_field(*args, &block) parser = DSLFieldParser.new(hints, *args, &block) begin @validator.validate_field(parser.name) append_field(parser.type, parser.name, parser.params) rescue Exception => e dsl_raise e.class, e.message end end
def parser_abilities
def parser_abilities @abilities ||= { struct: [:to_struct_params, :struct, [:multiple_fields, :optional_fieldnames, :hidden_fields]], array: [:to_object_params, :type, [:multiple_fields, :optional_fieldnames]], buffer: [:to_object_params, :type, [:multiple_fields, :optional_fieldnames, :hidden_fields]], choice: [:to_choice_params, :choices, [:multiple_fields, :all_or_none_fieldnames, :fieldnames_are_values]], delayed_io: [:to_object_params, :type, [:multiple_fields, :optional_fieldnames, :hidden_fields]], primitive: [:to_struct_params, :struct, [:multiple_fields, :optional_fieldnames]], section: [:to_object_params, :type, [:multiple_fields, :optional_fieldnames]], skip: [:to_object_params, :until_valid, [:multiple_fields, :optional_fieldnames]] } end
def search_prefix(*args)
def search_prefix(*args) @search_prefix ||= parent_attribute(:search_prefix, []).dup prefix = args.collect(&:to_sym).compact unless prefix.empty? if fields? dsl_raise SyntaxError, "search_prefix must be called before defining fields" end @search_prefix = prefix.concat(@search_prefix) end @search_prefix end
def set_endian(endian)
def set_endian(endian) if endian if fields? dsl_raise SyntaxError, "endian must be called before defining fields" end if !valid_endian?(endian) dsl_raise ArgumentError, "unknown value for endian '#{endian}'" end if endian == :big_and_little DSLBigAndLittleEndianHandler.handle(@the_class) end @endian = endian end end
def to_choice_params(key)
def to_choice_params(key) if fields.empty? {} elsif fields.all_field_names_blank? { key => fields.collect(&:prototype) } else choices = {} fields.each { |f| choices[f.name] = f.prototype } { key => choices } end end
def to_object_params(key)
def to_object_params(key) case fields.length when 0 {} when 1 { key => fields[0].prototype } else { key => [:struct, to_struct_params] } end end
def to_struct_params(*_)
def to_struct_params(*_) result = { fields: fields } if !endian.nil? result[:endian] = endian end if !search_prefix.empty? result[:search_prefix] = search_prefix end if option?(:hidden_fields) && !hide.empty? result[:hide] = hide end result end
def valid_endian?(endian)
def valid_endian?(endian) [:big, :little, :big_and_little].include?(endian) end