Module: Fiddle::CParser

Included in:
Importer
Defined in:
lib/fiddle/cparser.rb

Overview

A mixin that provides methods for parsing C struct and prototype signatures.

Example

require 'fiddle/import'

include Fiddle::CParser
  #=> Object

parse_ctype('int')
  #=> Fiddle::TYPE_INT

parse_struct_signature(['int i', 'char c'])
  #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]]

parse_signature('double sum(double, double)')
  #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]]

Instance Method Summary collapse

Instance Method Details

#parse_ctype(ty, tymap = nil) ⇒ Object

Given a String of C type ty, returns the corresponding Fiddle constant.

ty can also accept an Array of C type Strings, and will be returned in a corresponding Array.

If Hash tymap is provided, ty is expected to be the key, and the value will be the C type to be looked up.

Example:

include Fiddle::CParser
  #=> Object

parse_ctype('int')
  #=> Fiddle::TYPE_INT

parse_ctype('double diff')
  #=> Fiddle::TYPE_DOUBLE

parse_ctype('unsigned char byte')
  #=> -Fiddle::TYPE_CHAR

parse_ctype('const char* const argv[]')
  #=> -Fiddle::TYPE_VOIDP


121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
# File 'lib/fiddle/cparser.rb', line 121

def parse_ctype(ty, tymap=nil)
  tymap ||= {}
  case ty
  when Array
    return [parse_ctype(ty[0], tymap), ty[1]]
  when 'void'
    return TYPE_VOID
  when /^(?:(?:signed\s+)?long\s+long(?:\s+int\s+)?|int64_t)(?:\s+\w+)?$/
    if( defined?(TYPE_LONG_LONG) )
      return TYPE_LONG_LONG
    else
      raise(RuntimeError, "unsupported type: #{ty}")
    end
  when /^(?:unsigned\s+long\s+long(?:\s+int\s+)?|uint64_t)(?:\s+\w+)?$/
    if( defined?(TYPE_LONG_LONG) )
      return -TYPE_LONG_LONG
    else
      raise(RuntimeError, "unsupported type: #{ty}")
    end
  when /^(?:signed\s+)?long(?:\s+int\s+)?(?:\s+\w+)?$/
    return TYPE_LONG
  when /^unsigned\s+long(?:\s+int\s+)?(?:\s+\w+)?$/
    return -TYPE_LONG
  when /^(?:signed\s+)?int(?:\s+\w+)?$/
    return TYPE_INT
  when /^(?:unsigned\s+int|uint)(?:\s+\w+)?$/
    return -TYPE_INT
  when /^(?:signed\s+)?short(?:\s+int\s+)?(?:\s+\w+)?$/
    return TYPE_SHORT
  when /^unsigned\s+short(?:\s+int\s+)?(?:\s+\w+)?$/
    return -TYPE_SHORT
  when /^(?:signed\s+)?char(?:\s+\w+)?$/
    return TYPE_CHAR
  when /^unsigned\s+char(?:\s+\w+)?$/
    return  -TYPE_CHAR
  when /^float(?:\s+\w+)?$/
    return TYPE_FLOAT
  when /^double(?:\s+\w+)?$/
    return TYPE_DOUBLE
  when /^size_t(?:\s+\w+)?$/
    return TYPE_SIZE_T
  when /^ssize_t(?:\s+\w+)?$/
    return TYPE_SSIZE_T
  when /^ptrdiff_t(?:\s+\w+)?$/
    return TYPE_PTRDIFF_T
  when /^intptr_t(?:\s+\w+)?$/
    return TYPE_INTPTR_T
  when /^uintptr_t(?:\s+\w+)?$/
    return TYPE_UINTPTR_T
  when /\*/, /\[[\s\d]*\]/
    return TYPE_VOIDP
  else
    ty = ty.split(' ', 2)[0]
    if( tymap[ty] )
      return parse_ctype(tymap[ty], tymap)
    else
      raise(DLError, "unknown type: #{ty}")
    end
  end
end

#parse_signature(signature, tymap = nil) ⇒ Object

Parses a C prototype signature

If Hash tymap is provided, the return value and the arguments from the signature are expected to be keys, and the value will be the C type to be looked up.

Example:

include Fiddle::CParser
  #=> Object

parse_signature('double sum(double, double)')
  #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]]

parse_signature('void update(void (*cb)(int code))')
  #=> ["update", Fiddle::TYPE_VOID, [Fiddle::TYPE_VOIDP]]

parse_signature('char (*getbuffer(void))[80]')
  #=> ["getbuffer", Fiddle::TYPE_VOIDP, []]


82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/fiddle/cparser.rb', line 82

def parse_signature(signature, tymap=nil)
  tymap ||= {}
  case compact(signature)
  when /^(?:[\w\*\s]+)\(\*(\w+)\((.*?)\)\)(?:\[\w*\]|\(.*?\));?$/
    func, args = $1, $2
    return [func, TYPE_VOIDP, split_arguments(args).collect {|arg| parse_ctype(arg, tymap)}]
  when /^([\w\*\s]+[\*\s])(\w+)\((.*?)\);?$/
    ret, func, args = $1.strip, $2, $3
    return [func, parse_ctype(ret, tymap), split_arguments(args).collect {|arg| parse_ctype(arg, tymap)}]
  else
    raise(RuntimeError,"can't parse the function prototype: #{signature}")
  end
end

#parse_struct_signature(signature, tymap = nil) ⇒ Object

Parses a C struct's members

Example:

include Fiddle::CParser
  #=> Object

parse_struct_signature(['int i', 'char c'])
  #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]]

parse_struct_signature(['char buffer[80]'])
  #=> [[[Fiddle::TYPE_CHAR, 80]], ["buffer"]]


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/fiddle/cparser.rb', line 34

def parse_struct_signature(signature, tymap=nil)
  if signature.is_a?(String)
    signature = split_arguments(signature, /[,;]/)
  end
  mems = []
  tys  = []
  signature.each{|msig|
    msig = compact(msig)
    case msig
    when /^[\w\*\s]+[\*\s](\w+)$/
      mems.push($1)
      tys.push(parse_ctype(msig, tymap))
    when /^[\w\*\s]+\(\*(\w+)\)\(.*?\)$/
      mems.push($1)
      tys.push(parse_ctype(msig, tymap))
    when /^([\w\*\s]+[\*\s])(\w+)\[(\d+)\]$/
      mems.push($2)
      tys.push([parse_ctype($1.strip, tymap), $3.to_i])
    when /^([\w\*\s]+)\[(\d+)\](\w+)$/
      mems.push($3)
      tys.push([parse_ctype($1.strip, tymap), $2.to_i])
    else
      raise(RuntimeError,"can't parse the struct member: #{msig}")
    end
  }
  return tys, mems
end