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.
- data/lib/rbind.rb +1 -0
- data/lib/rbind/clang/clang.rb +3699 -0
- data/lib/rbind/clang/clang_types.rb +327 -0
- data/lib/rbind/clang_parser.rb +451 -0
- data/lib/rbind/core.rb +7 -4
- data/lib/rbind/core/rattribute.rb +17 -21
- data/lib/rbind/core/rbase.rb +98 -64
- data/lib/rbind/core/rcallback.rb +15 -0
- data/lib/rbind/core/rclass.rb +79 -16
- data/lib/rbind/core/rdata_type.rb +40 -39
- data/lib/rbind/core/renum.rb +18 -0
- data/lib/rbind/core/rnamespace.rb +189 -52
- data/lib/rbind/core/roperation.rb +52 -20
- data/lib/rbind/core/rparameter.rb +43 -8
- data/lib/rbind/core/rpointer.rb +70 -0
- data/lib/rbind/core/rreference.rb +54 -0
- data/lib/rbind/core/rtemplate_class.rb +49 -0
- data/lib/rbind/core/rtype_qualifier.rb +60 -0
- data/lib/rbind/default_parser.rb +48 -36
- data/lib/rbind/generator_c.rb +2 -2
- data/lib/rbind/generator_extern.rb +1 -3
- data/lib/rbind/generator_ruby.rb +201 -47
- data/lib/rbind/logger.rb +3 -0
- data/lib/rbind/rbind.rb +25 -9
- data/lib/rbind/templates/c/CMakeLists.txt +6 -6
- data/lib/rbind/templates/ruby/rbind.rb +1 -1
- data/lib/rbind/templates/ruby/rmethod.rb +4 -1
- data/lib/rbind/templates/ruby/rnamespace.rb +2 -1
- data/lib/rbind/templates/ruby/roverloaded_method.rb +3 -1
- data/lib/rbind/templates/ruby/roverloaded_method_call.rb +1 -0
- data/lib/rbind/templates/ruby/roverloaded_static_method.rb +3 -2
- data/lib/rbind/templates/ruby/rstatic_method.rb +4 -1
- data/lib/rbind/templates/ruby/rtype.rb +19 -16
- data/lib/rbind/templates/ruby/rtype_template.rb +7 -0
- data/lib/rbind/{core/rstring.rb → types/std_string.rb} +8 -8
- data/lib/rbind/types/std_vector.rb +100 -0
- data/rbind.gemspec +2 -2
- data/test/headers/cfunctions.h +7 -0
- data/test/headers/classes.hpp +29 -0
- data/test/headers/constants.hpp +14 -0
- data/test/headers/enums.hpp +22 -0
- data/test/headers/std_string.hpp +26 -0
- data/test/headers/std_vector.hpp +31 -0
- data/test/headers/structs.hpp +34 -0
- data/test/headers/templates.hpp +20 -0
- data/test/test_clang_parser.rb +146 -0
- data/test/test_generator_ruby.rb +0 -5
- data/test/test_roperation.rb +144 -0
- data/test/test_rparameter.rb +88 -0
- metadata +24 -7
- data/lib/rbind/core/.roperation.rb.swp +0 -0
- data/lib/rbind/core/rconst.rb +0 -35
- data/lib/rbind/core/rstruct.rb +0 -87
- 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
|