rbind 0.0.16 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. data/lib/rbind.rb +1 -0
  2. data/lib/rbind/clang/clang.rb +3699 -0
  3. data/lib/rbind/clang/clang_types.rb +327 -0
  4. data/lib/rbind/clang_parser.rb +451 -0
  5. data/lib/rbind/core.rb +7 -4
  6. data/lib/rbind/core/rattribute.rb +17 -21
  7. data/lib/rbind/core/rbase.rb +98 -64
  8. data/lib/rbind/core/rcallback.rb +15 -0
  9. data/lib/rbind/core/rclass.rb +79 -16
  10. data/lib/rbind/core/rdata_type.rb +40 -39
  11. data/lib/rbind/core/renum.rb +18 -0
  12. data/lib/rbind/core/rnamespace.rb +189 -52
  13. data/lib/rbind/core/roperation.rb +52 -20
  14. data/lib/rbind/core/rparameter.rb +43 -8
  15. data/lib/rbind/core/rpointer.rb +70 -0
  16. data/lib/rbind/core/rreference.rb +54 -0
  17. data/lib/rbind/core/rtemplate_class.rb +49 -0
  18. data/lib/rbind/core/rtype_qualifier.rb +60 -0
  19. data/lib/rbind/default_parser.rb +48 -36
  20. data/lib/rbind/generator_c.rb +2 -2
  21. data/lib/rbind/generator_extern.rb +1 -3
  22. data/lib/rbind/generator_ruby.rb +201 -47
  23. data/lib/rbind/logger.rb +3 -0
  24. data/lib/rbind/rbind.rb +25 -9
  25. data/lib/rbind/templates/c/CMakeLists.txt +6 -6
  26. data/lib/rbind/templates/ruby/rbind.rb +1 -1
  27. data/lib/rbind/templates/ruby/rmethod.rb +4 -1
  28. data/lib/rbind/templates/ruby/rnamespace.rb +2 -1
  29. data/lib/rbind/templates/ruby/roverloaded_method.rb +3 -1
  30. data/lib/rbind/templates/ruby/roverloaded_method_call.rb +1 -0
  31. data/lib/rbind/templates/ruby/roverloaded_static_method.rb +3 -2
  32. data/lib/rbind/templates/ruby/rstatic_method.rb +4 -1
  33. data/lib/rbind/templates/ruby/rtype.rb +19 -16
  34. data/lib/rbind/templates/ruby/rtype_template.rb +7 -0
  35. data/lib/rbind/{core/rstring.rb → types/std_string.rb} +8 -8
  36. data/lib/rbind/types/std_vector.rb +100 -0
  37. data/rbind.gemspec +2 -2
  38. data/test/headers/cfunctions.h +7 -0
  39. data/test/headers/classes.hpp +29 -0
  40. data/test/headers/constants.hpp +14 -0
  41. data/test/headers/enums.hpp +22 -0
  42. data/test/headers/std_string.hpp +26 -0
  43. data/test/headers/std_vector.hpp +31 -0
  44. data/test/headers/structs.hpp +34 -0
  45. data/test/headers/templates.hpp +20 -0
  46. data/test/test_clang_parser.rb +146 -0
  47. data/test/test_generator_ruby.rb +0 -5
  48. data/test/test_roperation.rb +144 -0
  49. data/test/test_rparameter.rb +88 -0
  50. metadata +24 -7
  51. data/lib/rbind/core/.roperation.rb.swp +0 -0
  52. data/lib/rbind/core/rconst.rb +0 -35
  53. data/lib/rbind/core/rstruct.rb +0 -87
  54. data/lib/rbind/core/rvector.rb +0 -27
@@ -0,0 +1,327 @@
1
+ module Clang
2
+
3
+ class Clang
4
+ @@index = Hash.new
5
+ attr_reader :units
6
+
7
+ def initialize
8
+ ObjectSpace.define_finalizer(self, Clang.method(:finalize))
9
+ @index = Rbind::create_index(1, 1)
10
+ @@index[self.object_id] = @index
11
+ @units = []
12
+ end
13
+
14
+ def translation_unit(file,args)
15
+ raise ArgumentError,"File #{file} does not exist!" unless File.exist?(file)
16
+ @pargs = args.map do |a|
17
+ FFI::MemoryPointer.from_string(a)
18
+ end
19
+ @cargs = FFI::MemoryPointer.new(:pointer, @pargs.size)
20
+ @cargs.write_array_of_pointer(@pargs)
21
+ tu = Rbind::parse_translation_unit(@index,file,@cargs,@pargs.size,nil,0,1)
22
+ # auto release if Clang goes out of scope
23
+ # this cannot be encapsulate into the obj because
24
+ # each cursor can return a pointer to the unit
25
+ @units << TranslationUnitImplStruct.new(FFI::AutoPointer.new(tu.__obj_ptr__.pointer,TranslationUnitImplStruct.method(:release)))
26
+ tu
27
+ end
28
+
29
+ # dispose index
30
+ def self.finalize(id)
31
+ Rbind::dispose_index(@@index[id])
32
+ @@index.delete(id)
33
+ end
34
+ end
35
+
36
+ # A single translation unit, which resides in an index.
37
+ class TranslationUnitImplStruct < FFI::Struct
38
+ layout :dummy, :char
39
+
40
+ # do not call this for each instance !
41
+ # class Clang is taking care of this
42
+ def self.release(pointer)
43
+ Rbind::dispose_translation_unit(pointer) unless pointer.null?
44
+ rescue Exception => e
45
+ puts e
46
+ end
47
+ end
48
+
49
+ class TranslationUnitImpl
50
+ extend FFI::DataConverter
51
+ native_type FFI::Type::POINTER
52
+
53
+ def self.to_native(obj,context)
54
+ if obj.is_a? TranslationUnitImpl
55
+ obj.__obj_ptr__
56
+ else
57
+ raise TypeError, "expected kind of #{name}, was #{obj.class}"
58
+ end
59
+ end
60
+
61
+ def self.from_native(ptr,context)
62
+ TranslationUnitImpl.new(ptr)
63
+ end
64
+
65
+ attr_reader :__obj_ptr__
66
+ def initialize(ptr)
67
+ @__obj_ptr__ = if ptr.is_a? TranslationUnitImplStruct
68
+ ptr
69
+ else
70
+ TranslationUnitImplStruct.new(ptr)
71
+ end
72
+ end
73
+
74
+ def cursor
75
+ cu = Rbind::get_translation_unit_cursor(self)
76
+ cu.instance_variable_set(:@__translation_unit__,self)
77
+ cu
78
+ end
79
+
80
+ def spelling
81
+ Rbind::get_translation_unit_spelling(self).to_s
82
+ end
83
+ end
84
+
85
+ # extend Structs to support auto dispose
86
+ module Rbind
87
+ class Cursor < FFI::Struct
88
+ def null?
89
+ 1 == Rbind::cursor_is_null(self)
90
+ end
91
+
92
+ def location
93
+ line = FFI::MemoryPointer.new(:uint,1)
94
+ col = FFI::MemoryPointer.new(:uint,1)
95
+ location = Rbind::get_cursor_location(self)
96
+ fstr = String.new
97
+ Rbind::get_presumed_location(location,fstr,line,col)
98
+ result = [fstr.to_s,line.get_uint(0),col.get_uint(0)]
99
+ result
100
+ end
101
+
102
+ def location_int
103
+ Rbind::get_cursor_location(self)[:int_data]
104
+ end
105
+
106
+ def referenced
107
+ Rbind::get_cursor_referenced self
108
+ end
109
+
110
+ def file_name
111
+ location[0]
112
+ end
113
+
114
+ def line
115
+ location[1]
116
+ end
117
+
118
+ def column
119
+ location[2]
120
+ end
121
+
122
+ def private?
123
+ cxx_access_specifier == :x_private
124
+ end
125
+
126
+ def public?
127
+ cxx_access_specifier == :x_public
128
+ end
129
+
130
+ def protected?
131
+ cxx_access_specifier == :x_protected
132
+ end
133
+
134
+ def cxx_access_specifier
135
+ Rbind::get_cxx_access_specifier self
136
+ end
137
+
138
+ def extent
139
+ Rbind::get_cursor_extent self
140
+ end
141
+
142
+ def semantic_parent
143
+ Rbind::get_cursor_semantic_parent self
144
+ end
145
+
146
+ def namespace
147
+ namespace = []
148
+ cursor = semantic_parent
149
+ while !cursor.translation_unit?
150
+ namespace << cursor.spelling
151
+ cursor = cursor.semantic_parent
152
+ end
153
+ namespace.reverse.join("::")
154
+ end
155
+
156
+ def translation_unit?
157
+ kind == :translation_unit
158
+ end
159
+
160
+ def expression
161
+ num = FFI::MemoryPointer.new(:uint,1)
162
+ tokens = FFI::MemoryPointer.new(:pointer,1)
163
+ tu = translation_unit
164
+ Rbind::tokenize(tu,extent,tokens,num)
165
+ ptr = FFI::Pointer.new(Token,tokens.read_pointer)
166
+ result = 0.upto(num.read_uint-1).map do |i|
167
+ Rbind::get_token_spelling(tu,ptr[i])
168
+ end
169
+ Rbind::dispose_tokens(tu,ptr,num.get_uint(0))
170
+ result
171
+ end
172
+
173
+ def result_type
174
+ Rbind::get_cursor_result_type self
175
+ end
176
+
177
+ def kind_spelling
178
+ Rbind::get_cursor_kind_spelling(self).to_s
179
+ end
180
+
181
+ def spelling
182
+ Rbind::get_cursor_spelling(self).to_s
183
+ end
184
+
185
+ def display_name
186
+ Rbind::get_cursor_display_name(self).to_s
187
+ end
188
+
189
+ def kind
190
+ Rbind::get_cursor_kind(self)
191
+ end
192
+
193
+ def translation_unit
194
+ Rbind::cursor_get_translation_unit(self)
195
+ end
196
+
197
+ def type
198
+ Rbind::get_cursor_type self
199
+ end
200
+
201
+ def virtul_base?
202
+ 1 == Rbind::is_virtual_base(self)
203
+ end
204
+
205
+ def specialized_template
206
+ Rbind::get_specialized_cursor_template self
207
+ end
208
+
209
+ def template_kind
210
+ Rbind::get_template_cursor_kind self
211
+ end
212
+
213
+ def complete?
214
+ !incomplete?
215
+ end
216
+
217
+ def incomplete?
218
+ definition.null?
219
+ end
220
+
221
+ def definition
222
+ Rbind::get_cursor_definition self
223
+ end
224
+
225
+ def static?
226
+ 1 == Rbind::cxx_method_is_static(self)
227
+ end
228
+
229
+ def virtual?
230
+ 1 == Rbind::cxx_method_is_virtual(self)
231
+ end
232
+
233
+ def visit_children(recurse=false,*reg_filters,&block)
234
+ if reg_filters.empty?
235
+ reg_filters << Regexp.new("#{File.dirname(translation_unit.spelling)}")
236
+ end
237
+ p = proc do |cur,parent,data|
238
+ # puts "#{cur.kind} #{cur.spelling} #{cur.template_kind} #{cur.specialized_template.kind} #{cur.location}"
239
+ if(reg_filters.find{|reg| cur.file_name =~ reg})
240
+ block.call(cur,parent)
241
+ if recurse
242
+ :recurse
243
+ else
244
+ :continue
245
+ end
246
+ else
247
+ :continue
248
+ end
249
+ end
250
+ Rbind::visit_children(self,p,FFI::Pointer.new(0))
251
+ end
252
+ end
253
+
254
+ class String < FFI::Struct
255
+ @@pointer = Hash.new
256
+
257
+ def self.finalize(id)
258
+ Rbind::dispose_string(@@pointer[id])
259
+ @@pointer.delete(id)
260
+ rescue Exception => e
261
+ puts e
262
+ end
263
+
264
+ def initialize(*args)
265
+ super
266
+ # we cannot use auto pointer because string is returned as value
267
+ ObjectSpace.define_finalizer(self, String.method(:finalize))
268
+ @@pointer[self.object_id] = pointer
269
+ end
270
+
271
+ def to_s
272
+ Rbind.get_c_string self
273
+ end
274
+ end
275
+
276
+ class Token < FFI::Struct
277
+ def self.release(pointer)
278
+ end
279
+
280
+ def spelling(translation_unit)
281
+ Rbind::get_token_spelling(translation_unit,self).to_s
282
+ end
283
+ end
284
+
285
+ class Type < FFI::Struct
286
+ def null?
287
+ kind == :invalid
288
+ end
289
+
290
+ def declaration
291
+ Rbind::get_type_declaration self
292
+ end
293
+
294
+ def const_qualified?
295
+ 1 == Rbind::is_const_qualified_type(self)
296
+ end
297
+
298
+ def canonical_type
299
+ Rbind::get_canonical_type self
300
+ end
301
+
302
+ def result_type
303
+ Rbind::get_result_type self
304
+ end
305
+
306
+ def pointee_type
307
+ Rbind::get_pointee_type self
308
+ end
309
+
310
+ def pod?
311
+ 1 == Rbind::is_pod_type(self)
312
+ end
313
+
314
+ def array_size
315
+ Rbind::get_array_size self
316
+ end
317
+
318
+ def array_element_type
319
+ Rbind::get_array_element_type self
320
+ end
321
+
322
+ def kind
323
+ self[:kind]
324
+ end
325
+ end
326
+ end
327
+ end
@@ -0,0 +1,451 @@
1
+
2
+ require 'rbind/clang/clang'
3
+ require 'rbind'
4
+ require 'pp'
5
+ require 'hooks'
6
+
7
+ module Rbind
8
+ class ClangParser < RNamespace
9
+ class ClangParserError < RuntimeError
10
+ def initialize(message,cursor)
11
+ @cursor = cursor
12
+ super(message)
13
+ end
14
+
15
+ def context(before = 10)
16
+ file,row,cloumn = @cursor.location
17
+ f = File.open(file)
18
+ lines = f.readlines[[0,row-before].max..row-1]
19
+ f.close
20
+ lines
21
+ end
22
+
23
+ def to_s
24
+ location = @cursor.location
25
+ con = context
26
+ row = location[1] - con.size
27
+ con = con.map do |line|
28
+ row += 1
29
+ "#{row}:\t> #{line}"
30
+ end
31
+ pos_width = @cursor.location_int-@cursor.extent[:begin_int_data]
32
+ pos_start = [location[2]-pos_width,0].max
33
+ con << " \t " + " "*pos_start + "."*pos_width
34
+ "#{super}\n\n#{"#"*5}\nParsed File: #{location.join(":")}\n#{con.join()}\n#{"#"*5}\n\n"
35
+ rescue Exception => e
36
+ pp e
37
+ end
38
+ end
39
+
40
+ include Hooks
41
+ extend ::Rbind::Logger
42
+
43
+ def initialize(root=nil)
44
+ super(nil,root)
45
+ add_default_types if !root
46
+ @clang = Clang::Clang.new
47
+ end
48
+
49
+ def parse(file_path,args = ["-xc++","-fno-rtti"])
50
+ tu = @clang.translation_unit(file_path,args)
51
+ process(tu.cursor)
52
+ self
53
+ end
54
+
55
+ def normalize_accessor(accessor)
56
+ case accessor
57
+ when :x_public
58
+ :public
59
+ when :x_private
60
+ :private
61
+ when :x_protected
62
+ :protected
63
+ else
64
+ raise "Cannot normalize accessor #{accessor}"
65
+ end
66
+ end
67
+
68
+ #returns the real type and the pointer level
69
+ # char** would be returned as [char,2]
70
+ def pointee_type(clang_type)
71
+ # count pointer level
72
+ level = 0
73
+ while(clang_type.kind == :pointer)
74
+ clang_type = clang_type.pointee_type
75
+ level += 1
76
+ end
77
+ [clang_type,level]
78
+ end
79
+
80
+ # if rbind_type is given only pointer/ref or qualifier are applied
81
+ def to_rbind_type(parent,cursor,rbind_type=nil,type_getter = :type,canonical = true)
82
+ clang_type = cursor.send(type_getter)
83
+ return nil if clang_type.null?
84
+ clang_type = clang_type.canonical_type if canonical
85
+ clang_type,level = pointee_type(clang_type)
86
+
87
+ t = if rbind_type
88
+ rbind_type
89
+ else
90
+ name = clang_type.declaration.spelling
91
+ name = if name.empty?
92
+ if clang_type.kind != :unexposed
93
+ if clang_type.kind == :l_value_reference
94
+ clang_type.pointee_type.kind
95
+ else
96
+ clang_type.kind.to_s
97
+ end
98
+ else
99
+ # fall back to cursor spelling
100
+ cursor.spelling
101
+ end
102
+ else
103
+ namespace = clang_type.declaration.namespace
104
+ if namespace.empty?
105
+ name
106
+ else
107
+ "#{namespace}::#{name}"
108
+ end
109
+
110
+ end
111
+ t = parent.type(name,!canonical)
112
+ end
113
+ # try again without canonical
114
+ return to_rbind_type(parent,cursor,rbind_type,type_getter,false) if !t
115
+
116
+ # add pointer level
117
+ 1.upto(level) do
118
+ t = t.to_ptr
119
+ end
120
+ t = if clang_type.kind == :l_value_reference
121
+ if clang_type.pointee_type.const_qualified?
122
+ t.to_ref.to_const
123
+ else
124
+ t.to_ref
125
+ end
126
+ else
127
+ if clang_type.const_qualified?
128
+ t.to_const
129
+ else
130
+ t
131
+ end
132
+ end
133
+ end
134
+
135
+ # entry call to parse a file
136
+ def process(cursor,parent = self)
137
+ access = :public
138
+ last_obj = nil
139
+ cursor.visit_children(false) do |cu,cu_parent|
140
+ # puts "----->#{cu.kind} #{cu.spelling} #{cu.type.kind} #{cu.specialized_template.kind}"
141
+ last_obj = case cu.kind
142
+ when :namespace
143
+ process_namespace(cu,parent)
144
+ when :enum_decl
145
+ process_enum(cu,parent) if access == :public
146
+ when :union_decl
147
+ # puts "got union declaration #{cu.spelling}"
148
+ when :struct_decl
149
+ if access == :public
150
+ process_class(cu,parent)
151
+ end
152
+ when :class_decl
153
+ if access == :public
154
+ access = :private
155
+ klass = process_class(cu,parent)
156
+ access = :public
157
+ klass
158
+ end
159
+ when :function_decl
160
+ process_function(cu,parent) if access == :public
161
+ when :macro_expansion # CV_WRAP ...
162
+ # puts "got macro #{cu.spelling} #{cu.location}"
163
+ when :function_template
164
+ # puts "got template fuction #{cu.spelling} #{cu.location}"
165
+ when :class_template
166
+ process_class_template(cu,parent)
167
+ when :template_type_parameter
168
+ # if !cu.spelling.empty?
169
+ # parent.add_type(RTemplateParameter.new(cu.spelling))
170
+ # else
171
+ # ClangParser.log.info "no template parameter name"
172
+ # end
173
+ when :x_access_specifier
174
+ access = normalize_accessor(cu.cxx_access_specifier)
175
+ when :x_base_specifier
176
+ local_access = normalize_accessor(cu.cxx_access_specifier)
177
+ p = parent.type(RBase.normalize(cu.spelling),false)
178
+ ClangParser.log.info "auto add parent class #{cu.spelling}" unless p
179
+ p ||= parent.add_type(RClass.new(RBase.normalize(cu.spelling)))
180
+ parent.add_parent p,local_access
181
+ when :field_decl
182
+ process_field(cu,parent) if cu.public? || access == :public
183
+ when :constructor
184
+ if access == :public
185
+ f = process_function(cu,parent)
186
+ f.return_type = nil if f
187
+ f
188
+ end
189
+ when :x_method
190
+ process_function(cu,parent) if access == :public
191
+ when :typedef_decl
192
+ # rename object if parent has no name
193
+ if last_obj && last_obj.name =~ /no_name/
194
+ ClangParser.log.info "rename #{last_obj.name} to #{cu.spelling}"
195
+ last_obj.rename(cu.spelling)
196
+ last_obj
197
+ end
198
+ when :var_decl
199
+ process_variable(cu,parent) if access == :public
200
+ else
201
+ #puts "skip: #{cu.spelling}"
202
+ end
203
+ raise ClangParserError.new("jjj",cu) if last_obj.is_a? Fixnum
204
+ end
205
+ end
206
+
207
+ def process_namespace(cursor,parent)
208
+ name = cursor.spelling
209
+ ClangParser.log.info "processing namespace #{parent}::#{name}"
210
+ ns = parent.add_namespace(name)
211
+ process(cursor,ns)
212
+ ns
213
+ end
214
+
215
+ def process_enum(cursor,parent)
216
+ name = cursor.spelling
217
+ name = if name.empty?
218
+ n = 0.upto(10000) do |i|
219
+ n = "no_name_enum_#{i}"
220
+ break n if !parent.type(n,false,false)
221
+ end
222
+ raise "Cannot find unique enum name" unless n
223
+ n
224
+ else
225
+ name
226
+ end
227
+ ClangParser.log.info "processing enum #{parent}::#{name}"
228
+ enum = REnum.new(name)
229
+ cursor.visit_children(false) do |cu,_|
230
+ case cu.kind
231
+ when :enum_constant_decl
232
+ # for now there is no api to access these values from libclang
233
+ expression = cu.expression
234
+ expression.pop
235
+ val = if expression.join(" ") =~ /=(.*)/
236
+ $1.gsub(" ","")
237
+ end
238
+ enum.add_value(cu.spelling,val)
239
+ end
240
+ end
241
+ parent.add_type(enum)
242
+ enum
243
+ end
244
+
245
+ def process_variable(cursor,parent)
246
+ name = cursor.spelling
247
+ ClangParser.log.info "processing variable #{parent}::#{name}"
248
+ var = process_parameter(cursor,parent)
249
+ if var.type.const?
250
+ parent.add_const(var)
251
+ end
252
+ var
253
+ end
254
+
255
+ def process_field(cursor,parent)
256
+ name = cursor.spelling
257
+ ClangParser.log.info "processing field #{parent}::#{name}"
258
+ var = process_parameter(cursor,parent)
259
+ # TODO check for read write access
260
+ a = RAttribute.new(var.name,var.type).writeable!
261
+ parent.add_attribute a
262
+ a
263
+ end
264
+
265
+ def process_class_template(cursor,parent)
266
+ class_name = cursor.spelling
267
+ ClangParser.log.info "processing class template #{parent}::#{class_name}"
268
+
269
+ klass = parent.type(class_name,false)
270
+ klass = if(!klass)
271
+ klass = RTemplateClass.new(class_name)
272
+ parent.add_type(klass)
273
+ klass
274
+ else
275
+ ClangParser.log.info " reopening existing class template #{klass}"
276
+ klass
277
+ end
278
+ process(cursor,klass)
279
+ klass
280
+ end
281
+
282
+ def process_class(cursor,parent)
283
+ class_name = cursor.spelling
284
+ class_name = if class_name.empty?
285
+ "no_name_class"
286
+ else
287
+ class_name
288
+ end
289
+ if cursor.incomplete?
290
+ ClangParser.log.info "skipping incomplete class #{parent}::#{class_name}"
291
+ return
292
+ else
293
+ ClangParser.log.info "processing class #{parent}::#{class_name}"
294
+ end
295
+
296
+ klass = parent.type(class_name,false)
297
+ klass = if(!klass)
298
+ klass = RClass.new(class_name)
299
+ parent.add_type(klass)
300
+ klass
301
+ else
302
+ if klass.empty?
303
+ ClangParser.log.info " reopening existing class #{klass}"
304
+ klass
305
+ elsif klass.template?
306
+ ClangParser.log.info " skipping template #{name}"
307
+ nil
308
+ else
309
+ ClangParser.log.warn " skipping non empty clas #{name}"
310
+ #raise "Cannot reopening existing class #{klass} which is non-empty!"
311
+ nil
312
+ end
313
+ end
314
+ #klass.extern_package_name = nil
315
+ process(cursor,klass) if klass
316
+ klass
317
+ end
318
+
319
+ def process_function(cursor,parent)
320
+ name = cursor.spelling
321
+ args = []
322
+
323
+ cursor = if(cursor.specialized_template.kind == :function_template)
324
+ cursor.specialized_template
325
+ else
326
+ cursor
327
+ end
328
+ cursor.visit_children() do |cu,_|
329
+ case cu.kind
330
+ when :parm_decl
331
+ p = process_parameter(cu,parent)
332
+ args << p
333
+ end
334
+ end
335
+
336
+ # some default values are not parsed by clang
337
+ # try to parse them from Tokens
338
+ # and rename parameters with unknown name
339
+ # to prevent name clashes
340
+ expression = cursor.expression.join()
341
+ args.each_with_index do |arg,idx|
342
+ if(!arg.default_value && (expression =~ /#{arg.name}=(\w*)/))
343
+ arg.default_value = $1
344
+ end
345
+ arg.name = if(arg.name == "no_name_arg")
346
+ arg.name + idx.to_s
347
+ else
348
+ arg.name
349
+ end
350
+ end
351
+
352
+ result_type = if !cursor.result_type.null?
353
+ process_parameter(cursor,parent,:result_type).type
354
+ end
355
+ op = ::Rbind::ROperation.new(name,result_type,*args)
356
+ op = if cursor.static?
357
+ op.to_static
358
+ else
359
+ op
360
+ end
361
+ ClangParser.log.info "add function #{op.signature}"
362
+ parent.add_operation(op)
363
+ op
364
+ rescue RuntimeError => e
365
+ ClangParser.log.info "skipping instance method #{parent.full_name}::#{name}: #{e}"
366
+ nil
367
+ end
368
+
369
+ # type_getter is also used for :result_type
370
+ def process_parameter(cursor,parent,type_getter = :type)
371
+ para_name = cursor.spelling
372
+ para_name = if para_name.empty?
373
+ "no_name_arg"
374
+ else
375
+ para_name
376
+ end
377
+ default_value = nil
378
+ type_cursor = nil
379
+ template_name = ""
380
+ name_space = []
381
+
382
+ cursor.visit_children do |cu,_|
383
+ case cu.kind
384
+ when :integer_literal
385
+ exp = cu.expression
386
+ exp.pop
387
+ default_value = exp.join("")
388
+ when :floating_literal
389
+ exp = cu.expression
390
+ exp.pop
391
+ default_value = exp.join("")
392
+ when :call_expr
393
+ exp = cu.expression
394
+ exp.shift
395
+ exp.pop
396
+ default_value = exp.join("")
397
+ when :gnu_null_expr
398
+ default_value = 0
399
+ when :unexposed_expr
400
+ exp = cu.expression
401
+ exp.pop
402
+ default_value = exp.join("")
403
+ when :template_ref
404
+ name_space << cu.spelling
405
+ if !template_name.empty?
406
+ template_name += "<#{name_space.join("::")}"
407
+ else
408
+ template_name = name_space.join("::")
409
+ end
410
+ name_space.clear
411
+ when :namespace_ref
412
+ name_space << cu.spelling
413
+ when :type_ref
414
+ type_cursor = cu
415
+ end
416
+ end
417
+ type = if template_name.empty?
418
+ type = if type_cursor
419
+ to_rbind_type(parent,type_cursor)
420
+ end
421
+ # just upgrade type to pointer / ref if type != nil
422
+ to_rbind_type(parent,cursor,type,type_getter)
423
+ else
424
+ # parameter is a template type
425
+ # TODO find better way to get inner type
426
+ # we could use type_cursor here if given but this is
427
+ # not the case for basic types and somehow the type
428
+ # qualifier are not provided
429
+ expression = cursor.expression.join(" ")
430
+ inner_types = if expression =~ /<([ \w\*&,:]*)>/
431
+ $1
432
+ else
433
+ raise RuntimeError,"Cannot parse template type parameter."
434
+ end
435
+
436
+ inner_types = inner_types.split(",").map do |inner_type|
437
+ parent.type(inner_type)
438
+ end
439
+
440
+ templates = template_name.split("<")
441
+ templates << inner_types.map(&:full_name).join(",")
442
+
443
+ t = parent.type(templates.join("<")+">"*(templates.size-1),true)
444
+ to_rbind_type(parent,cursor,t,type_getter)
445
+ end
446
+ RParameter.new(para_name,type,default_value)
447
+ rescue RuntimeError => e
448
+ raise ClangParserError.new(e.to_s,cursor)
449
+ end
450
+ end
451
+ end