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