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 all_or_none_names_failed?(name)

def all_or_none_names_failed?(name)
  if option?(:all_or_none_fieldnames) and not fields.empty?
    all_names_blank = fields.all_field_names_blank?
    no_names_blank = fields.no_field_names_blank?
    (name != nil and all_names_blank) or (name == nil and no_names_blank)
  else
    false
  end
end

def append_field(type, name, params)

def append_field(type, name, params)
  ensure_valid_field(name)
  fields.add_field(type, name, params)
rescue ArgumentError => err
  dsl_raise ArgumentError, err.message
rescue UnRegisteredTypeError => err
  dsl_raise TypeError, "unknown type '#{err.message}'"
end

def dsl_params

def dsl_params
  case @parser_type
  when :struct
    to_struct_params
  when :array
    to_array_params
  when :choice
    to_choice_params
  when :primitive
    to_struct_params
  when :wrapper
    raise "Wrapper is deprecated"
  else
    raise "unknown parser type #{@parser_type}"
  end
end

def dsl_raise(exception, message)

def dsl_raise(exception, message)
  backtrace = caller
  backtrace.shift while %r{bindata/dsl.rb} =~ backtrace.first
  raise exception, message + " in #{@the_class}", backtrace
end

def duplicate_name?(name)

def duplicate_name?(name)
  fields.has_field_name?(name)
end

def endian(endian = nil)

def endian(endian = nil)
  if endian.nil?
    @endian
  elsif endian == :big or endian == :little
    @endian = endian
  else
    dsl_raise ArgumentError, "unknown value for endian '#{endian}'"
  end
end

def ensure_valid_field(field_name)

def ensure_valid_field(field_name)
  if too_many_fields?
    dsl_raise SyntaxError, "attempting to wrap more than one type"
  end
  if must_not_have_a_name_failed?(field_name)
    dsl_raise SyntaxError, "field must not have a name"
  end
  if all_or_none_names_failed?(field_name)
    dsl_raise SyntaxError, "fields must either all have names, or none must have names"
  end
  if must_have_a_name_failed?(field_name)
    dsl_raise SyntaxError, "field must have a name"
  end
  ensure_valid_name(field_name)
end

def ensure_valid_name(name)

def ensure_valid_name(name)
  if name and not option?(:fieldnames_are_values)
    if malformed_name?(name)
      dsl_raise NameError.new("", name), "field '#{name}' is an illegal fieldname"
    end
    if duplicate_name?(name)
      dsl_raise SyntaxError, "duplicate field '#{name}'"
    end
    if name_shadows_method?(name)
      dsl_raise NameError.new("", name), "field '#{name}' shadows an existing method"
    end
    if name_is_reserved?(name)
      dsl_raise NameError.new("", name), "field '#{name}' is a reserved name"
    end
  end
end

def fields

def fields
  unless defined? @fields
    fields = parent_attribute(:fields, nil)
    klass = option?(:sanitize_fields) ? SanitizedFields : UnSanitizedFields
    @fields = klass.new(endian)
    @fields.copy_fields(fields) if fields
  end
  @fields
end

def hide(*args)

def hide(*args)
  if option?(:hidden_fields)
    hidden = args.collect do |name|
               unless Symbol === name
                 warn "Hidden field '#{name}' should be provided as a symbol.  Using strings is deprecated"
               end
               name.to_sym
             end
    unless defined? @hide
      @hide = parent_attribute(:hide, []).dup
    end
    @hide.concat(hidden.compact)
    @hide
  end
end

def initialize(the_class, parser_type)

def initialize(the_class, parser_type)
  @the_class   = the_class
  @parser_type = parser_type
  @endian      = parent_attribute(:endian, nil)
end

def malformed_name?(name)

def malformed_name?(name)
  /^[a-z_]\w*$/ !~ name.to_s
end

def method_missing(symbol, *args, &block) #:nodoc:

:nodoc:
def method_missing(symbol, *args, &block) #:nodoc:
  type   = symbol
  name   = name_from_field_declaration(args)
  params = params_from_field_declaration(type, args, &block)
  append_field(type, name, params)
end

def must_have_a_name_failed?(name)

def must_have_a_name_failed?(name)
  option?(:mandatory_fieldnames) and name.nil?
end

def must_not_have_a_name_failed?(name)

def must_not_have_a_name_failed?(name)
  option?(:no_fieldnames) and name != nil
end

def name_from_field_declaration(args)

def name_from_field_declaration(args)
  name, params = args
  if name == "" or name.is_a?(Hash)
    nil
  else
    name
  end
end

def name_is_reserved?(name)

def name_is_reserved?(name)
  BinData::Struct::RESERVED.include?(name.to_sym)
end

def name_shadows_method?(name)

def name_shadows_method?(name)
  @the_class.method_defined?(name)
end

def option?(opt)

def option?(opt)
  options.include?(opt)
end

def options

def options
  case @parser_type
  when :struct
    [:multiple_fields, :optional_fieldnames, :sanitize_fields, :hidden_fields]
  when :array
    [:multiple_fields, :optional_fieldnames, :sanitize_fields]
  when :choice
    [:multiple_fields, :all_or_none_fieldnames, :sanitize_fields, :fieldnames_are_values]
  when :primitive
    [:multiple_fields, :optional_fieldnames, :sanitize_fields]
  when :wrapper
    [:only_one_field, :no_fieldnames]
  else
    raise "unknown parser type #{parser_type}"
  end
end

def params_from_args(args)

def params_from_args(args)
  name, params = args
  params = name if name.is_a?(Hash)
  params || {}
end

def params_from_block(type, &block)

def params_from_block(type, &block)
  bindata_classes = {
    :array  => BinData::Array,
    :choice => BinData::Choice,
    :struct => BinData::Struct
  }
  if bindata_classes.include?(type)
    parser = DSLParser.new(bindata_classes[type], type)
    parser.endian(endian)
    parser.instance_eval(&block)
    parser.dsl_params
  else
    {}
  end
end

def params_from_field_declaration(type, args, &block)

def params_from_field_declaration(type, args, &block)
  params = params_from_args(args)
  if block_given?
    params.merge(params_from_block(type, &block))
  else
    params
  end
end

def parent_attribute(attr, default = nil)

def parent_attribute(attr, default = nil)
  parent = @the_class.superclass.respond_to?(:dsl_parser) ? @the_class.superclass.dsl_parser : nil
  if parent and parent.respond_to?(attr)
    parent.send(attr)
  else
    default
  end
end

def to_array_params

def to_array_params
  case fields.length
  when 0
    {}
  when 1
    {:type => fields[0].prototype}
  else
    {:type => [:struct, to_struct_params]}
  end
end

def to_choice_params

def to_choice_params
  if fields.length == 0
    {}
  elsif fields.all_field_names_blank?
    {:choices => fields.collect { |f| f.prototype }}
  else
    choices = {}
    fields.each { |f| choices[f.name] = f.prototype }
    {:choices => choices}
  end
end

def to_struct_params

def to_struct_params
  result = {:fields => fields}
  if not endian.nil?
    result[:endian] = endian
  end
  if option?(:hidden_fields) and not hide.empty?
    result[:hide] = hide
  end
  result
end

def too_many_fields?

def too_many_fields?
  option?(:only_one_field) and not fields.empty?
end