remogatto-ffi-swig-generator 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ module FFI
2
+ module Generator
3
+ # :stopdoc:
4
+ VERSION = '0.2.1'
5
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
+ # :startdoc:
8
+
9
+ # Returns the version string for the library.
10
+ #
11
+ def self.version
12
+ VERSION
13
+ end
14
+
15
+ # Returns the library path for the module. If any arguments are given,
16
+ # they will be joined to the end of the libray path using
17
+ # <tt>File.join</tt>.
18
+ #
19
+ def self.libpath( *args )
20
+ args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
21
+ end
22
+
23
+ # Returns the lpath for the module. If any arguments are given,
24
+ # they will be joined to the end of the path using
25
+ # <tt>File.join</tt>.
26
+ #
27
+ def self.path( *args )
28
+ args.empty? ? PATH : ::File.join(PATH, args.flatten)
29
+ end
30
+
31
+ # Utility method used to require all files ending in .rb that lie in the
32
+ # directory below this file that has the same name as the filename passed
33
+ # in. Optionally, a specific _directory_ name can be passed in such that
34
+ # the _filename_ does not have to be equivalent to the directory.
35
+ #
36
+ def self.require_all_libs_relative_to( fname, dir = nil )
37
+ dir ||= ::File.basename(fname, '.*')
38
+ search_me = ::File.expand_path(
39
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
40
+
41
+ Dir.glob(search_me).sort.each {|rb| require rb}
42
+ end
43
+ end
44
+ end
45
+
46
+ FFI::Generator.require_all_libs_relative_to(__FILE__, 'generator')
47
+
48
+ # EOF
@@ -0,0 +1,69 @@
1
+ require 'getoptlong'
2
+
3
+ module FFI
4
+ module Generator
5
+ class Application
6
+ OPTIONS = [
7
+ [ "--help", "-u", GetoptLong::NO_ARGUMENT,
8
+ "Display help information." ],
9
+ [ "--version", "-v", GetoptLong::NO_ARGUMENT,
10
+ "Display the version number and quit." ],
11
+ ]
12
+
13
+ USAGE_PREAMBLE = <<-EOU
14
+ Usage: ffi-gen [options] <input_file> <output_file>
15
+
16
+ <input_file> is the xml parse tree generated by swig -xml command.
17
+ <output_file> is the ruby-ffi wrapper file.
18
+
19
+ EOU
20
+ class << self
21
+ def run
22
+ process_args
23
+ if ARGV.size == 2
24
+ File.open(ARGV[1], 'w') do |file|
25
+ file << FFI::Generator::Parser.generate(Nokogiri::XML(File.open(ARGV[0])))
26
+ end
27
+ else
28
+ help
29
+ raise "Invalid number of arguments!"
30
+ end
31
+ end
32
+ private
33
+ def do_option(option, value = nil)
34
+ case option
35
+ when '--help'
36
+ help
37
+ exit
38
+ when '--version'
39
+ puts "ffi-swig-generator, version #{Generator::VERSION}\n"
40
+ exit
41
+ end
42
+ end
43
+ def command_line_options
44
+ OPTIONS.collect { |lst| lst[0..-2] }
45
+ end
46
+ def process_args
47
+ opts = GetoptLong.new(*command_line_options)
48
+ opts.each { |opt, value| do_option(opt, value) }
49
+ end
50
+ def help
51
+ puts
52
+ puts USAGE_PREAMBLE
53
+ puts "Recognized options are:"
54
+ puts
55
+ OPTIONS.sort.each do |long, short, mode, desc|
56
+ if mode == GetoptLong::REQUIRED_ARGUMENT
57
+ if desc =~ /\b([A-Z]{2,})\b/
58
+ long = long + "=#{$1}"
59
+ end
60
+ end
61
+ printf " %-20s (%s)\n", long, short
62
+ printf " %s\n", desc
63
+ puts
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,344 @@
1
+ require 'rubygems'
2
+ require 'nokogiri'
3
+
4
+ module FFI
5
+ module Generator
6
+ @typedefs = {}
7
+ TYPES = {
8
+ 'char' => ':char',
9
+ 'double' => ':double',
10
+ 'float' => ':float',
11
+ 'unsigned long' => ':ulong',
12
+ 'unsigned char' => ':uchar',
13
+ 'signed char' => ':char',
14
+ 'unsigned char' => ':uchar',
15
+ 'short' => ':short',
16
+ 'signed short' => ':short',
17
+ 'signed short int' => ':short',
18
+ 'unsigned short' => ':ushort',
19
+ 'unsigned short int' => ':ushort',
20
+ 'int' => ':int',
21
+ 'signed int' => ':int',
22
+ 'unsigned int' => ':uint',
23
+ 'long' => ':long',
24
+ 'long int' => ':long',
25
+ 'signed long' => ':long',
26
+ 'signed long int' => ':long',
27
+ 'unsigned long' => ':ulong',
28
+ 'unsigned long int' => ':ulong',
29
+ 'long unsigned int' => ':ulong',
30
+ 'long long' => ':long_long',
31
+ 'long long int' => ':long_long',
32
+ 'signed long long' => ':long_long',
33
+ 'signed long long int' => ':long_long',
34
+ 'unsigned long long' => ':ulong_long',
35
+ 'unsigned long long int' => ':ulong_long',
36
+ 'void' => ':void'
37
+ }
38
+ class << self
39
+ attr_reader :typedefs
40
+ def add_type(ctype, rtype)
41
+ @typedefs[ctype] = rtype
42
+ end
43
+ end
44
+ class Node
45
+ attr_reader :symname
46
+ def initialize(params = { })
47
+ params = { :indent => 0 }.merge(params)
48
+ @node, @indent = params[:node], params[:indent]
49
+ @indent_str = ' ' * @indent
50
+ @symname = get_attr('name')
51
+ end
52
+ def get_attr(name)
53
+ if @node
54
+ attr = (@node / "./attributelist/attribute[@name='#{name}']").first
55
+ attr['value'] if attr
56
+ end
57
+ end
58
+ end
59
+ class Type < Node
60
+ def initialize(params = { })
61
+ super
62
+ @statement = params[:statement] || get_statement
63
+ end
64
+ def to_s
65
+ get_type
66
+ end
67
+ private
68
+ def get_statement
69
+ get_attr('decl').to_s + get_attr('type').to_s if @node
70
+ end
71
+ def is_native?
72
+ Generator::TYPES.has_key?(@statement)
73
+ end
74
+ def is_pointer?
75
+ @statement[/^p\./] and not is_callback?
76
+ end
77
+ def is_enum?
78
+ @statement[/^enum/]
79
+ end
80
+ def is_array?
81
+ @statement and @statement[/\w+\(\d+\)/]
82
+ end
83
+ def is_struct?
84
+ @statement[/^struct/]
85
+ end
86
+ def is_union?
87
+ @statement[/^union/]
88
+ end
89
+ def is_constant?
90
+ @statement[/^q\(const\)/]
91
+ end
92
+ def is_callback?
93
+ @statement[/^p.f\(/]
94
+ end
95
+ def native
96
+ if is_native?
97
+ @statement = Generator::TYPES[@statement]
98
+ get_type
99
+ end
100
+ end
101
+ def constant
102
+ if is_constant?
103
+ @statement = @statement.scan(/^q\(const\)\.(.+)/).flatten[0]
104
+ get_type
105
+ end
106
+ end
107
+ def pointer
108
+ if is_pointer?
109
+ if @statement[/char/] and @statement.scan(/p\./).size == 1
110
+ @statement = ':string'
111
+ # @decl.gsub!(/p\./, '')
112
+ get_type
113
+ else
114
+ return ':pointer'
115
+ end
116
+ end
117
+ end
118
+ def array
119
+ if is_array?
120
+ num = @statement.scan(/\w+\((\d+)\)/).flatten[0]
121
+ @statement.gsub!(/\w+\(\d+\)\./, '')
122
+ "[#{get_type}, #{num}]"
123
+ end
124
+ end
125
+ def struct
126
+ if is_struct?
127
+ @statement = Structure.camelcase(@statement.scan(/^struct\s(\w+)/).flatten[0])
128
+ get_type
129
+ end
130
+ end
131
+ def union
132
+ if is_union?
133
+ @statement = Union.camelcase(@statement.scan(/^union\s(\w+)/).flatten[0])
134
+ get_type
135
+ end
136
+ end
137
+ def enum
138
+ if is_enum?
139
+ @statement = Generator::TYPES['int']
140
+ get_type
141
+ end
142
+ end
143
+ def callback
144
+ Callback.new(:node => @node).to_s if is_callback?
145
+ end
146
+ def typedef
147
+ if Generator.typedefs.has_key?(@statement)
148
+ @statement = Generator.typedefs[@statement]
149
+ get_type
150
+ end
151
+ end
152
+ def get_type
153
+ constant || pointer || enum || typedef || native || struct || union || array || callback || "#{@statement}"
154
+ end
155
+ end
156
+ class Typedef < Type
157
+ attr_reader :symname, :statement
158
+ def initialize(params = { })
159
+ super
160
+ @symname = get_attr('name')
161
+ # @type = is_pointer? ? ':pointer' : get_attr('type')
162
+ # p @statement
163
+ end
164
+ end
165
+ class Constant < Node
166
+ def initialize(params = { })
167
+ super
168
+ @name, @value = get_attr('sym_name'), get_attr('value')
169
+ end
170
+ def to_s
171
+ @indent_str + "#{@name} = #{@value}"
172
+ end
173
+ end
174
+ class Enum < Node
175
+ def initialize(params = { })
176
+ super
177
+ eval_items
178
+ end
179
+ def to_s
180
+ @items.sort { |i1, i2| i1[1] <=> i2[1] }.inject("") do |result, item|
181
+ result << assignment_str(item[0], item[1]) << "\n"
182
+ end
183
+ end
184
+ private
185
+ def assignment_str(name, value)
186
+ @indent_str + "#{name} = #{value}"
187
+ end
188
+ def eval_expr(expr)
189
+ if expr.include?('+')
190
+ (@items[expr[/\w+/]].to_i + 1).to_s
191
+ else
192
+ 0.to_s
193
+ end
194
+ end
195
+ def eval_items
196
+ @items = {}
197
+ get_items.each do |i|
198
+ node = Node.new(:node => i)
199
+ @items[node.get_attr('name')] = node.get_attr('enumvalueex') ? eval_expr(node.get_attr('enumvalueex')) : node.get_attr('enumvalue')
200
+ end
201
+ @items
202
+ end
203
+ def get_items
204
+ @node / './enumitem'
205
+ end
206
+ end
207
+ class Structure < Node
208
+ def self.camelcase(name)
209
+ name.gsub(/^\w|\_\w/).each {|c| c.upcase }.delete('_')
210
+ end
211
+ def initialize(params = { })
212
+ super
213
+ @name = self.class.camelcase(@symname)
214
+ end
215
+ def to_s
216
+ fields_str = fields.inject("") do |str, f|
217
+ str << @indent_str + ' ' * 9 << f.join(', ') << ",\n"
218
+ end
219
+ code = klass_string + @indent_str + " layout(\n" + fields_str.chomp.chomp(',') + "\n" + @indent_str + " )\n" + @indent_str + "end\n"
220
+ end
221
+ private
222
+ def klass_string
223
+ @indent_str + "class #{@name} < FFI::Struct\n"
224
+ end
225
+ def fields
226
+ (@node / 'cdecl').inject([]) do |array, field|
227
+ array << [":#{Node.new(:node => field).symname}", "#{Type.new(:node => field)}"]
228
+ end
229
+ end
230
+ end
231
+ class Union < Structure
232
+ private
233
+ def klass_string
234
+ @indent_str + "class #{@name} < FFI::Union\n"
235
+ end
236
+ end
237
+ class Function < Type
238
+ class Argument < Type
239
+ def to_s
240
+ get_attr('type') == 'void' ? nil : super
241
+ end
242
+ end
243
+ def initialize(params = { })
244
+ super
245
+ @type = get_attr('type')
246
+ end
247
+ def to_s
248
+ params = get_params(@node).inject([]) do |array, node|
249
+ array << Argument.new(:node => node).to_s
250
+ end.collect { |p| "#{p}" }
251
+ @indent_str + "attach_function :#{@symname}, [ #{params.join(', ')} ], #{get_rvalue}"
252
+ end
253
+ private
254
+ def get_params(node)
255
+ parmlist = node / './attributelist/parmlist/parm'
256
+ end
257
+ def get_rvalue
258
+ Type.new(:node => @node, :statement => @statement.scan(/^f\(.*\)\.(.+)/).flatten[0]).to_s
259
+ end
260
+ end
261
+ class Callback < Type
262
+ def to_s
263
+ params = get_params.inject([]) do |array, type|
264
+ array << (type == 'void' ? '' : Type.new(:statement => type).to_s)
265
+ end
266
+ @indent_str + "callback(:#{@symname}, [ #{params.join(', ')} ], #{get_rtype})"
267
+ end
268
+ private
269
+ def get_params
270
+ @statement.scan(/p.f\((.*)\)/).flatten[0].split(',')
271
+ end
272
+ def get_rtype
273
+ Type.new(:statement => @statement.scan(/\)\.(\w+)/).flatten[0]).to_s
274
+ end
275
+ end
276
+ class Parser
277
+ @indent = 2
278
+ class << self
279
+ def get_verbatim(node)
280
+ node.xpath("./attributelist/attribute[@name='code']").first['value']
281
+ end
282
+ def is_insert_runtime?(node)
283
+ section = node.xpath("./attributelist/attribute[@name='section']")
284
+ section.first['value'] == 'runtime' if section.first
285
+ end
286
+ def is_constant?(node)
287
+ node.name == 'constant'
288
+ end
289
+ def is_enum?(node)
290
+ node.name == 'enum'
291
+ end
292
+ def is_function_decl?(node)
293
+ node.name == 'cdecl' and (node / "./attributelist/attribute[@name='kind']").first['value'] == 'function'
294
+ end
295
+ def is_struct?(node)
296
+ node.name == 'class' and (node / "./attributelist/attribute[@name='kind']").first['value'] == 'struct'
297
+ end
298
+ def is_union?(node)
299
+ node.name == 'class' and (node / "./attributelist/attribute[@name='kind']").first['value'] == 'union'
300
+ end
301
+ def is_typedef?(node)
302
+ node.name == 'cdecl' and (node / "./attributelist/attribute[@name='kind']").first['value'] == 'typedef'
303
+ end
304
+ def is_callback?(node)
305
+ (node / "./attributelist/attribute[@name='decl']").first['value'] =~ /^p\.f\(/
306
+ end
307
+ def generate(node)
308
+ result = ""
309
+ node.traverse do |node|
310
+ if is_constant?(node)
311
+ result << Constant.new(:node => node, :indent => @indent).to_s << "\n"
312
+ elsif is_typedef?(node)
313
+ typedef = Typedef.new(:node => node)
314
+ Generator.add_type(typedef.symname, typedef.statement)
315
+ if is_callback?(node)
316
+ cb = Callback.new(:node => node, :indent => @indent).to_s << "\n"
317
+ Generator.add_type(typedef.symname, ":#{typedef.symname}")
318
+ result << cb.to_s
319
+ end
320
+ elsif is_enum?(node)
321
+ e = Enum.new(:node => node, :indent => @indent)
322
+ Generator.add_type(e.symname, Generator::TYPES['int'])
323
+ result << e.to_s << "\n"
324
+ elsif is_struct?(node)
325
+ s = Structure.new(:node => node, :indent => @indent)
326
+ Generator.add_type(s.symname, "struct #{s.symname}")
327
+ result << s.to_s
328
+ elsif is_union?(node)
329
+ s = Union.new(:node => node, :indent => @indent)
330
+ Generator.add_type(s.symname, "union #{s.symname}")
331
+ result << s.to_s
332
+ elsif is_function_decl?(node)
333
+ result << Function.new(:node => node, :indent => @indent).to_s << "\n"
334
+ elsif node.name == 'insert' and not is_insert_runtime?(node)
335
+ result << get_verbatim(node)
336
+ end
337
+ end
338
+ result
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
344
+