1module Fiddle
2  # A mixin that provides methods for parsing C struct and prototype signatures.
3  #
4  # == Example
5  #   require 'fiddle/import'
6  #
7  #   include Fiddle::CParser
8  #     #=> Object
9  #
10  #   parse_ctype('int increment(int)')
11  #     #=> ["increment", Fiddle::TYPE_INT, [Fiddle::TYPE_INT]]
12  #
13  module CParser
14    # Parses a C struct's members
15    #
16    # Example:
17    #
18    #   include Fiddle::CParser
19    #     #=> Object
20    #
21    #   parse_struct_signature(['int i', 'char c'])
22    #     #=> [[Fiddle::TYPE_INT, Fiddle::TYPE_CHAR], ["i", "c"]]
23    #
24    def parse_struct_signature(signature, tymap=nil)
25      if( signature.is_a?(String) )
26        signature = signature.split(/\s*,\s*/)
27      end
28      mems = []
29      tys  = []
30      signature.each{|msig|
31        tks = msig.split(/\s+(\*)?/)
32        ty = tks[0..-2].join(" ")
33        member = tks[-1]
34
35        case ty
36        when /\[(\d+)\]/
37          n = $1.to_i
38          ty.gsub!(/\s*\[\d+\]/,"")
39          ty = [ty, n]
40        when /\[\]/
41          ty.gsub!(/\s*\[\]/, "*")
42        end
43
44        case member
45        when /\[(\d+)\]/
46          ty = [ty, $1.to_i]
47          member.gsub!(/\s*\[\d+\]/,"")
48        when /\[\]/
49          ty = ty + "*"
50          member.gsub!(/\s*\[\]/, "")
51        end
52
53        mems.push(member)
54        tys.push(parse_ctype(ty,tymap))
55      }
56      return tys, mems
57    end
58
59    # Parses a C prototype signature
60    #
61    # If Hash +tymap+ is provided, the return value and the arguments from the
62    # +signature+ are expected to be keys, and the value will be the C type to
63    # be looked up.
64    #
65    # Example:
66    #
67    #   include Fiddle::CParser
68    #     #=> Object
69    #
70    #   parse_signature('double sum(double, double)')
71    #     #=> ["sum", Fiddle::TYPE_DOUBLE, [Fiddle::TYPE_DOUBLE, Fiddle::TYPE_DOUBLE]]
72    #
73    def parse_signature(signature, tymap=nil)
74      tymap ||= {}
75      signature = signature.gsub(/\s+/, " ").strip
76      case signature
77      when /^([\w@\*\s]+)\(([\w\*\s\,\[\]]*)\)$/
78        ret = $1
79        (args = $2).strip!
80        ret = ret.split(/\s+/)
81        args = args.split(/\s*,\s*/)
82        func = ret.pop
83        if( func =~ /^\*/ )
84          func.gsub!(/^\*+/,"")
85          ret.push("*")
86        end
87        ret  = ret.join(" ")
88        return [func, parse_ctype(ret, tymap), args.collect{|arg| parse_ctype(arg, tymap)}]
89      else
90        raise(RuntimeError,"can't parse the function prototype: #{signature}")
91      end
92    end
93
94    # Given a String of C type +ty+, returns the corresponding Fiddle constant.
95    #
96    # +ty+ can also accept an Array of C type Strings, and will be returned in
97    # a corresponding Array.
98    #
99    # If Hash +tymap+ is provided, +ty+ is expected to be the key, and the
100    # value will be the C type to be looked up.
101    #
102    # Example:
103    #
104    #   include Fiddle::CParser
105    #     #=> Object
106    #
107    #   parse_ctype('int')
108    #     #=> Fiddle::TYPE_INT
109    #
110    #   parse_ctype('double')
111    #     #=> Fiddle::TYPE_DOUBLE
112    #
113    #   parse_ctype('unsigned char')
114    #     #=> -Fiddle::TYPE_CHAR
115    #
116    def parse_ctype(ty, tymap=nil)
117      tymap ||= {}
118      case ty
119      when Array
120        return [parse_ctype(ty[0], tymap), ty[1]]
121      when "void"
122        return TYPE_VOID
123      when "char"
124        return TYPE_CHAR
125      when "unsigned char"
126        return  -TYPE_CHAR
127      when "short"
128        return TYPE_SHORT
129      when "unsigned short"
130        return -TYPE_SHORT
131      when "int"
132        return TYPE_INT
133      when "unsigned int", 'uint'
134        return -TYPE_INT
135      when "long"
136        return TYPE_LONG
137      when "unsigned long"
138        return -TYPE_LONG
139      when "long long"
140        if( defined?(TYPE_LONG_LONG) )
141          return TYPE_LONG_LONG
142        else
143          raise(RuntimeError, "unsupported type: #{ty}")
144        end
145      when "unsigned long long"
146        if( defined?(TYPE_LONG_LONG) )
147          return -TYPE_LONG_LONG
148        else
149          raise(RuntimeError, "unsupported type: #{ty}")
150        end
151      when "float"
152        return TYPE_FLOAT
153      when "double"
154        return TYPE_DOUBLE
155      when "size_t"
156        return TYPE_SIZE_T
157      when "ssize_t"
158        return TYPE_SSIZE_T
159      when "ptrdiff_t"
160        return TYPE_PTRDIFF_T
161      when "intptr_t"
162        return TYPE_INTPTR_T
163      when "uintptr_t"
164        return TYPE_UINTPTR_T
165      when /\*/, /\[\s*\]/
166        return TYPE_VOIDP
167      else
168        if( tymap[ty] )
169          return parse_ctype(tymap[ty], tymap)
170        else
171          raise(DLError, "unknown type: #{ty}")
172        end
173      end
174    end
175  end
176end
177