Skip to content

Commit

Permalink
Improved the usefulness/accuracy of type mappings
Browse files Browse the repository at this point in the history
- char * -> CString
- void * -> Ptr Word8

- unsigned char  -> Word8
- unsigned short -> CUShort
- unsigned int   -> CUInt
- unsigned long  -> CULong

- size_t     -> CSize
- time_t     -> CTime
- git_time_t -> CTime

- fixed size arrays -> [a]
  • Loading branch information
jacobstanley committed Mar 17, 2011
1 parent 54c0748 commit 156812c
Showing 1 changed file with 57 additions and 19 deletions.
76 changes: 57 additions & 19 deletions create_bindings.rb
Expand Up @@ -2,19 +2,44 @@

require 'fileutils'

def ffi_type(type)
def ffi_type(ptr, type)
# special cases
case type
when "void"
return "Ptr Word8" if ptr == 1
return "Ptr (Ptr Word8)" if ptr == 2
when "char"
return "CString" if ptr == 1
return "Ptr CString" if ptr == 2
end

# standard cases
types = {
"int" => "CInt",
"char" => "CChar",
"unsigned" => "CInt",
"void" => "()",
"size_t" => "CInt",
"time_t" => "CInt"
"void" => "()",
"char" => "CChar",
"uchar" => "Word8",
"short" => "CShort",
"ushort" => "CUShort",
"int" => "CInt",
"uint" => "CUInt",
"long" => "CLong",
"ulong" => "CULong",
"size_t" => "CSize",
"time_t" => "CTime",

# git_time_t is typedef'd to time_t on every
# platform that GHC builds on because GHC uses
# MinGW when building on Windows.
"git_time_t" => "CTime"
}
if types[type]
return types[type]
end
return "<#{type}>"

ffi = types[type] || "<#{type}>"
return case ptr
when 0 then ffi
when 1 then "Ptr #{ffi}"
when 2 then "Ptr (Ptr #{ffi})"
else raise "only two pointer indirections supported"
end
end

def ffi_return(arg)
Expand All @@ -26,15 +51,19 @@ def ffi_return(arg)

def ffi_argument(arg)
arg.gsub!(/const/, '')
arg.gsub!(/unsigned int/, 'unsigned')
arg.gsub!(/unsigned char/, 'uchar')
arg.gsub!(/unsigned short/, 'ushort')
arg.gsub!(/unsigned int/, 'uint')
arg.gsub!(/unsigned long/, 'ulong')
arg.gsub!(/unsigned/, 'uint')
if (m = arg.match(/^ *([^ ]+) *\*\*/))
return "Ptr (Ptr #{ffi_type(m[1])})"
return ffi_type 2, m[1]
end
if (m = arg.match(/^ *([^ ]+) *\*/))
return "Ptr #{ffi_type(m[1])}"
return ffi_type 1, m[1]
end
if (m = arg.match(/^ *([^ ]+)/))
return ffi_type(m[1])
return ffi_type 0, m[1]
end
raise "unkown type: #{arg}"
end
Expand Down Expand Up @@ -113,8 +142,14 @@ def parse_struct_fields(body)
body.scan(/^ *[^ ]+ [^;]+;/){|field|
f = field.match(/^ *([^;]+);/)
raise "could not parse struct field '#{field}'" if not f
type = f[1].strip
fieldname = field.match(/([^* ]+);/)
yield(f[1].strip, fieldname[1])
arrayname = fieldname[1].match(/([^\[]+)\[/)
if arrayname
yield('array_field', type, arrayname[1])
else
yield('field', type, fieldname[1])
end
}
end

Expand All @@ -128,8 +163,8 @@ def transform_structs(string)
})
else
structlist = ["#starttype #{p[:name]}"]
parse_struct_fields(p[:body]) {|type, field|
structlist.push("#field #{field} , #{ffi_argument(type)}")
parse_struct_fields(p[:body]) {|field, type, name|
structlist.push("##{field} #{name} , #{ffi_argument(type)}")
}
structlist.push("#stoptype")
structs.push({ :transformed => structlist.join("\n"),
Expand All @@ -144,7 +179,10 @@ def transform_typedefs(contents)
types = {}
contents.scan(/typedef ([^\s]+) ([^\s]+_t);/) {|m|
if types[m[1]].nil?
defs.push({:transformed => "#integral_t #{m[1]}"})
# git_time_t is handled explicitly in ffi_argument
if m[1] != "git_time_t"
defs.push({:transformed => "#integral_t #{m[1]}"})
end
types[m[1]] = true
end
}
Expand Down

0 comments on commit 156812c

Please sign in to comment.