[前][次][番号順一覧][スレッド一覧]

ruby-changes:37029

From: hsbt <ko1@a...>
Date: Sat, 3 Jan 2015 09:54:44 +0900 (JST)
Subject: [ruby-changes:37029] hsbt:r49110 (trunk): * ext/fiddle/lib/fiddle/cparser.rb: Support for Fiddle::CParser

hsbt	2015-01-03 09:54:37 +0900 (Sat, 03 Jan 2015)

  New Revision: 49110

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=49110

  Log:
    * 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.

  Modified files:
    trunk/ChangeLog
    trunk/ext/fiddle/lib/fiddle/cparser.rb
    trunk/test/fiddle/test_cparser.rb
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 49109)
+++ ChangeLog	(revision 49110)
@@ -1,3 +1,10 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Sat Jan  3 09:54:32 2015  SHIBATA Hiroshi  <shibata.hiroshi@g...>
+
+	* 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@g...>
 
 	* NEWS: added compatibility entry of r49101.
Index: ext/fiddle/lib/fiddle/cparser.rb
===================================================================
--- ext/fiddle/lib/fiddle/cparser.rb	(revision 49109)
+++ ext/fiddle/lib/fiddle/cparser.rb	(revision 49110)
@@ -7,8 +7,14 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/cparser.rb#L7
   #   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 https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/cparser.rb#L27
     #   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 https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/cparser.rb#L72
     #   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 https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/cparser.rb#L108
     #   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 https://github.com/ruby/ruby/blob/trunk/ext/fiddle/lib/fiddle/cparser.rb#L176
         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
Index: test/fiddle/test_cparser.rb
===================================================================
--- test/fiddle/test_cparser.rb	(revision 49109)
+++ test/fiddle/test_cparser.rb	(revision 49110)
@@ -8,8 +8,35 @@ module Fiddle https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_cparser.rb#L8
   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 https://github.com/ruby/ruby/blob/trunk/test/fiddle/test_cparser.rb#L58
     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)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]