summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-01-03 00:54:37 +0000
committerhsbt <hsbt@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-01-03 00:54:37 +0000
commit3afd0f9a914e4a7c733523ee657e238861dd8b38 (patch)
treebc52bbfab87ceeda97a5a967bf00d147a43aeeec
parent4690241f012af796315013189ee5b98a3cf88f05 (diff)
* ext/fiddle/lib/fiddle/cparser.rb: Support for Fiddle::CParser
to handle rich signatures including parameter names and function pointer types. Patch by @theryan [fix GH-590] * test/fiddle/test_cparser.rb: ditto. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@49110 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog7
-rw-r--r--ext/fiddle/lib/fiddle/cparser.rb156
-rw-r--r--test/fiddle/test_cparser.rb161
3 files changed, 253 insertions, 71 deletions
diff --git a/ChangeLog b/ChangeLog
index c7cee89d1a..5fd0fddff2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sat Jan 3 09:54:32 2015 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
+
+ * ext/fiddle/lib/fiddle/cparser.rb: Support for Fiddle::CParser
+ to handle rich signatures including parameter names and function
+ pointer types. Patch by @theryan [fix GH-590]
+ * test/fiddle/test_cparser.rb: ditto.
+
Sat Jan 3 09:01:43 2015 SHIBATA Hiroshi <shibata.hiroshi@gmail.com>
* NEWS: added compatibility entry of r49101.
diff --git a/ext/fiddle/lib/fiddle/cparser.rb b/ext/fiddle/lib/fiddle/cparser.rb
index 43fb184a12..18c9908e91 100644
--- a/ext/fiddle/lib/fiddle/cparser.rb
+++ b/ext/fiddle/lib/fiddle/cparser.rb
@@ -7,8 +7,14 @@ module Fiddle
# include Fiddle::CParser
# #=> Object
#
- # parse_ctype('int increment(int)')
- # #=> ["increment", Fiddle::TYPE_INT, [Fiddle::TYPE_INT]]
+ # 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]]
#
module CParser
# Parses a C struct's members
@@ -21,37 +27,33 @@ module Fiddle
# 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"]]
+ #
def parse_struct_signature(signature, tymap=nil)
- if( signature.is_a?(String) )
- signature = signature.split(/\s*,\s*/)
+ if signature.is_a?(String)
+ signature = split_arguments(signature, /[,;]/)
end
mems = []
tys = []
signature.each{|msig|
- tks = msig.split(/\s+(\*)?/)
- ty = tks[0..-2].join(" ")
- member = tks[-1]
-
- case ty
- when /\[(\d+)\]/
- n = $1.to_i
- ty.gsub!(/\s*\[\d+\]/,"")
- ty = [ty, n]
- when /\[\]/
- ty.gsub!(/\s*\[\]/, "*")
- end
-
- case member
- when /\[(\d+)\]/
- ty = [ty, $1.to_i]
- member.gsub!(/\s*\[\d+\]/,"")
- when /\[\]/
- ty = ty + "*"
- member.gsub!(/\s*\[\]/, "")
+ 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
-
- mems.push(member)
- tys.push(parse_ctype(ty,tymap))
}
return tys, mems
end
@@ -70,22 +72,21 @@ module Fiddle
# 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, []]
+ #
def parse_signature(signature, tymap=nil)
tymap ||= {}
- signature = signature.gsub(/\s+/, " ").strip
- case signature
- when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/
- ret = $1
- (args = $2).strip!
- ret = ret.split(/\s+/)
- args = args.split(/\s*,\s*/)
- func = ret.pop
- if( func =~ /^\*/ )
- func.gsub!(/^\*+/,"")
- ret.push("*")
- end
- ret = ret.join(" ")
- return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, 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
@@ -107,62 +108,65 @@ module Fiddle
# parse_ctype('int')
# #=> Fiddle::TYPE_INT
#
- # parse_ctype('double')
+ # parse_ctype('double diff')
# #=> Fiddle::TYPE_DOUBLE
#
- # parse_ctype('unsigned char')
+ # parse_ctype('unsigned char byte')
# #=> -Fiddle::TYPE_CHAR
#
+ # parse_ctype('const char* const argv[]')
+ # #=> -Fiddle::TYPE_VOIDP
+ #
def parse_ctype(ty, tymap=nil)
tymap ||= {}
case ty
when Array
return [parse_ctype(ty[0], tymap), ty[1]]
- when "void"
+ when 'void'
return TYPE_VOID
- when "char"
- return TYPE_CHAR
- when "unsigned char"
- return -TYPE_CHAR
- when "short"
- return TYPE_SHORT
- when "unsigned short"
- return -TYPE_SHORT
- when "int"
- return TYPE_INT
- when "unsigned int", 'uint'
- return -TYPE_INT
- when "long"
- return TYPE_LONG
- when "unsigned long"
- return -TYPE_LONG
- when "long long"
+ 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 long long"
+ 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 "float"
+ 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"
+ when /^double(?:\s+\w+)?$/
return TYPE_DOUBLE
- when "size_t"
+ when /^size_t(?:\s+\w+)?$/
return TYPE_SIZE_T
- when "ssize_t"
+ when /^ssize_t(?:\s+\w+)?$/
return TYPE_SSIZE_T
- when "ptrdiff_t"
+ when /^ptrdiff_t(?:\s+\w+)?$/
return TYPE_PTRDIFF_T
- when "intptr_t"
+ when /^intptr_t(?:\s+\w+)?$/
return TYPE_INTPTR_T
- when "uintptr_t"
+ when /^uintptr_t(?:\s+\w+)?$/
return TYPE_UINTPTR_T
- when /\*/, /\[\s*\]/
+ when /\*/, /\[[\s\d]*\]/
return TYPE_VOIDP
else
if( tymap[ty] )
@@ -172,5 +176,17 @@ module Fiddle
end
end
end
+
+ private
+
+ def split_arguments(arguments, sep=',')
+ return [] if arguments.strip == 'void'
+ arguments.scan(/([\w\*\s]+\(\*\w*\)\(.*?\)|[\w\*\s\[\]]+)(?:#{sep}\s*|$)/).collect {|m| m[0]}
+ end
+
+ def compact(signature)
+ signature.gsub(/\s+/, ' ').gsub(/\s*([\(\)\[\]\*,;])\s*/, '\1').strip
+ end
+
end
end
diff --git a/test/fiddle/test_cparser.rb b/test/fiddle/test_cparser.rb
index 666d8c89d6..fb62a15bf9 100644
--- a/test/fiddle/test_cparser.rb
+++ b/test/fiddle/test_cparser.rb
@@ -8,8 +8,35 @@ module Fiddle
class TestCParser < TestCase
include CParser
- def test_uint_ctype
+ def test_char_ctype
+ assert_equal(TYPE_CHAR, parse_ctype('char'))
+ assert_equal(TYPE_CHAR, parse_ctype('signed char'))
+ assert_equal(-TYPE_CHAR, parse_ctype('unsigned char'))
+ end
+
+ def test_short_ctype
+ assert_equal(TYPE_SHORT, parse_ctype('short'))
+ assert_equal(TYPE_SHORT, parse_ctype('short int'))
+ assert_equal(TYPE_SHORT, parse_ctype('signed short'))
+ assert_equal(TYPE_SHORT, parse_ctype('signed short int'))
+ assert_equal(-TYPE_SHORT, parse_ctype('unsigned short'))
+ assert_equal(-TYPE_SHORT, parse_ctype('unsigned short int'))
+ end
+
+ def test_int_ctype
+ assert_equal(TYPE_INT, parse_ctype('int'))
+ assert_equal(TYPE_INT, parse_ctype('signed int'))
assert_equal(-TYPE_INT, parse_ctype('uint'))
+ assert_equal(-TYPE_INT, parse_ctype('unsigned int'))
+ end
+
+ def test_long_ctype
+ assert_equal(TYPE_LONG, parse_ctype('long'))
+ assert_equal(TYPE_LONG, parse_ctype('long int'))
+ assert_equal(TYPE_LONG, parse_ctype('signed long'))
+ assert_equal(TYPE_LONG, parse_ctype('signed long int'))
+ assert_equal(-TYPE_LONG, parse_ctype('unsigned long'))
+ assert_equal(-TYPE_LONG, parse_ctype('unsigned long int'))
end
def test_size_t_ctype
@@ -31,5 +58,137 @@ module Fiddle
def test_uintptr_t_ctype
assert_equal(TYPE_UINTPTR_T, parse_ctype("uintptr_t"))
end
+
+ def test_struct_basic
+ assert_equal [[TYPE_INT, TYPE_CHAR], ['i', 'c']], parse_struct_signature(['int i', 'char c'])
+ end
+
+ def test_struct_array
+ assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature(['char buffer[80]', 'int[5] x'])
+ end
+
+ def test_struct_array_str
+ assert_equal [[[TYPE_CHAR,80],[TYPE_INT,5]], ['buffer','x']], parse_struct_signature('char buffer[80], int[5] x')
+ end
+
+ def test_struct_function_pointer
+ assert_equal [[TYPE_VOIDP], ['cb']], parse_struct_signature(['void (*cb)(const char*)'])
+ end
+
+ def test_struct_function_pointer_str
+ assert_equal [[TYPE_VOIDP,TYPE_VOIDP], ['cb', 'data']], parse_struct_signature('void (*cb)(const char*), const char* data')
+ end
+
+ def test_struct_string
+ assert_equal [[TYPE_INT,TYPE_VOIDP,TYPE_VOIDP], ['x', 'cb', 'name']], parse_struct_signature('int x; void (*cb)(); const char* name')
+ end
+
+ def test_signature_basic
+ func, ret, args = parse_signature('void func()')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [], args
+ end
+
+ def test_signature_semi
+ func, ret, args = parse_signature('void func();')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [], args
+ end
+
+ def test_signature_void_arg
+ func, ret, args = parse_signature('void func(void)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [], args
+ end
+
+ def test_signature_type_args
+ types = [
+ 'char', 'unsigned char',
+ 'short', 'unsigned short',
+ 'int', 'unsigned int',
+ 'long', 'unsigned long',
+ 'long long', 'unsigned long long',
+ 'float', 'double',
+ 'const char*', 'void*',
+ ]
+ func, ret, args = parse_signature("void func(#{types.join(',')})")
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [
+ TYPE_CHAR, -TYPE_CHAR,
+ TYPE_SHORT, -TYPE_SHORT,
+ TYPE_INT, -TYPE_INT,
+ TYPE_LONG, -TYPE_LONG,
+ TYPE_LONG_LONG, -TYPE_LONG_LONG,
+ TYPE_FLOAT, TYPE_DOUBLE,
+ TYPE_VOIDP, TYPE_VOIDP,
+ ], args
+ end
+
+ def test_signature_single_variable
+ func, ret, args = parse_signature('void func(int x)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [TYPE_INT], args
+ end
+
+ def test_signature_multiple_variables
+ func, ret, args = parse_signature('void func(int x, const char* s)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [TYPE_INT, TYPE_VOIDP], args
+ end
+
+ def test_signature_array_variable
+ func, ret, args = parse_signature('void func(int x[], int y[40])')
+ assert_equal 'func', func
+ assert_equal TYPE_VOID, ret
+ assert_equal [TYPE_VOIDP, TYPE_VOIDP], args
+ end
+
+ def test_signature_function_pointer
+ func, ret, args = parse_signature('int func(int (*sum)(int x, int y), int x, int y)')
+ assert_equal 'func', func
+ assert_equal TYPE_INT, ret
+ assert_equal [TYPE_VOIDP, TYPE_INT, TYPE_INT], args
+ end
+
+ def test_signature_return_pointer
+ func, ret, args = parse_signature('void* malloc(size_t)')
+ assert_equal 'malloc', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [TYPE_SIZE_T], args
+ end
+
+ def test_signature_return_array
+ func, ret, args = parse_signature('int (*func())[32]')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [], args
+ end
+
+ def test_signature_return_array_with_args
+ func, ret, args = parse_signature('int (*func(const char* s))[]')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [TYPE_VOIDP], args
+ end
+
+ def test_signature_return_function_pointer
+ func, ret, args = parse_signature('int (*func())(int x, int y)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [], args
+ end
+
+ def test_signature_return_function_pointer_with_args
+ func, ret, args = parse_signature('int (*func(int z))(int x, int y)')
+ assert_equal 'func', func
+ assert_equal TYPE_VOIDP, ret
+ assert_equal [TYPE_INT], args
+ end
end
end if defined?(Fiddle)