dub 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,12 @@
1
+ == 0.6.0 2010-03-11
2
+
3
+ * 4 enhancements
4
+ * added support for custom tostring methods for classes
5
+ * fixed parsing of templated return types
6
+ * static class methods are now registered in the namespace as Klass_method
7
+ * removing functions and methods with class pointer arguments (could use lists later on)
8
+ * better parsing of complex types (including nested template arguments)
9
+
1
10
  == 0.5.1 2010-03-05
2
11
 
3
12
  * 2 minor enhancements
data/dub.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{dub}
8
- s.version = "0.5.1"
8
+ s.version = "0.6.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Gaspard Bucher"]
12
- s.date = %q{2010-03-05}
12
+ s.date = %q{2010-03-11}
13
13
  s.description = %q{This is a tool to ease the creation of scripting language bindings for a C++ library.
14
14
  It is currently developed to crete the OpenCV bindings for Lua in Rubyk (http://rubyk.org). The generator uses the xml output from Doxygen to avoid parsing C++ code by itself.}
15
15
  s.email = %q{gaspard@teti.ch}
@@ -148,6 +148,7 @@ Gem::Specification.new do |s|
148
148
  "test/fixtures/app/xml/namespacedub.xml",
149
149
  "test/fixtures/classcv_1_1_mat.xml",
150
150
  "test/fixtures/classcv_1_1_point__.xml",
151
+ "test/fixtures/classcv_1_1_scalar__.xml",
151
152
  "test/fixtures/classcv_1_1_size__.xml",
152
153
  "test/fixtures/group___magic_type.xml",
153
154
  "test/fixtures/namespacecv.xml",
data/lib/dub/argument.rb CHANGED
@@ -4,8 +4,8 @@ module Dub
4
4
  class Argument
5
5
  include Dub::EntitiesUnescape
6
6
  attr_reader :name, :default, :function, :xml
7
- attr_accessor :type
8
-
7
+ attr_accessor :type, :is_list, :is_list_count
8
+ TYPE_REGEXP = %r{^\s*(\w+\s+|)(const\s+|)([\w\:]+|\.\.\.)(\s*<(.+)>|)(\s*\*+|\s*&|)$}
9
9
  NUMBER_TYPES = [
10
10
  'float',
11
11
  'double',
@@ -14,6 +14,7 @@ module Dub
14
14
  'uint',
15
15
  'int',
16
16
  'size_t',
17
+ 'time_t',
17
18
  'unsigned int',
18
19
  'uint',
19
20
  'bool',
@@ -141,6 +142,18 @@ module Dub
141
142
  @type == '...'
142
143
  end
143
144
 
145
+ def complex?
146
+ @is_complex
147
+ end
148
+
149
+ def is_list?
150
+ @is_list
151
+ end
152
+
153
+ def is_list_count?
154
+ @is_list_count
155
+ end
156
+
144
157
  def create_type
145
158
  resolve_type if @template_params
146
159
  (is_const? ? 'const ' : '') +
@@ -199,39 +212,32 @@ module Dub
199
212
  end
200
213
 
201
214
  type = type.innerHTML
215
+ type = unescape(type).strip
202
216
 
203
- # Strip CV_EXPORT .....
204
- if type =~ /^([^ ]+)\s+([a-zA-Z_]+.*)$/
205
- if $1 == 'const'
206
- @const = true
207
- end
208
- type = $2
209
- end
217
+ if type =~ TYPE_REGEXP
218
+ res = $~.to_a
210
219
 
211
- # Strip const .....
212
- if type =~ /^const\s+(.+)$/
213
- type = $1.strip
214
- @const = true
215
- end
220
+ if res[1].strip == 'const'
221
+ res[2] = res[1]
222
+ res[1] = ""
223
+ end
216
224
 
217
- # Strip ..... &
218
- if type =~ /^(.+)&amp;$/
219
- type = $1.strip
220
- @ref = true
221
- end
225
+ @const = res[2].strip == 'const'
226
+ @type = res[3]
222
227
 
223
- # Strip ..... *
224
- if type =~ /^(.+)(\*+)\s*$/
225
- type = $1.strip
226
- @pointer = $2
227
- end
228
+ if res[6] == ''
229
+ @pointer = nil
230
+ @res = nil
231
+ elsif res[6] =~ /^\s*(\*+)\s*$/
232
+ @pointer = $1
233
+ else
234
+ @ref = res[6].strip
235
+ end
228
236
 
229
- # Strip .....< ... >
230
- if type =~ /^(.+)\s*&lt;\s*(.+)\s*&gt;/
231
- type = $1.strip
232
- @template_params = $2.split(',').map(&:strip)
237
+ @template_params = res[5] ? res[5].split(',').map(&:strip) : nil
238
+ else
239
+ # ERROR
233
240
  end
234
- @type = type
235
241
  end
236
242
 
237
243
  # Replace something like AUTO_STEP by cv::Mat::AUTO_STEP
@@ -245,21 +251,23 @@ module Dub
245
251
  end
246
252
 
247
253
  def resolve_type
254
+ params = @template_params
255
+ @template_params = nil
248
256
  if container = @function.parent
249
257
  if container.kind_of?(Klass)
250
258
  container = container.parent
251
259
  end
252
260
  if container && tclass = container.template_class(@type)
253
- if instanciation = tclass.instanciations[@template_params]
261
+ if instanciation = tclass.instanciations[params]
254
262
  @type = instanciation.name
255
- else
256
- Dub.logger.warn "Could not resolve templated type #{@type}<#{@template_params.join(', ')}>"
263
+ return
257
264
  end
258
- else
259
- Dub.logger.warn "Could not find class for type #{@type}<#{@template_params.join(', ')}>"
260
265
  end
261
266
  end
262
- @template_params = nil
267
+
268
+ @type = "#{@type}< #{params.join(', ')} >"
269
+ Dub.logger.warn "Could not resolve templated type #{@type}"
270
+ @is_complex = true
263
271
  end
264
272
  end
265
273
  end # Namespace
data/lib/dub/function.rb CHANGED
@@ -11,13 +11,14 @@ module Dub
11
11
  @parent, @name = parent, name
12
12
  @xml, @prefix, @overloaded_index = xml, prefix, overloaded_index
13
13
  parse_xml
14
+ parse_template_params
14
15
  end
15
-
16
+
16
17
  def set_as_constructor
17
18
  @return_value = Argument.new(self, (Hpricot::XML("<type>#{name} *</type>")/''))
18
19
  @is_constructor = true
19
20
  end
20
-
21
+
21
22
  def bind(generator)
22
23
  @gen = generator.function_generator
23
24
  end
@@ -42,6 +43,10 @@ module Dub
42
43
  @is_constructor
43
44
  end
44
45
 
46
+ def static?
47
+ @is_static
48
+ end
49
+
45
50
  alias gen generator
46
51
 
47
52
  def name=(n)
@@ -51,6 +56,14 @@ module Dub
51
56
  end
52
57
  end
53
58
 
59
+ def call_name
60
+ if klass
61
+ static? ? "#{klass.name}::#{name}" : name
62
+ else
63
+ name
64
+ end
65
+ end
66
+
54
67
  def source
55
68
  loc = (@xml/'location').first.attributes
56
69
  "#{loc['file'].split('/')[-3..-1].join('/')}:#{loc['line']}"
@@ -70,10 +83,33 @@ module Dub
70
83
  @has_array_arguments = !@arguments.detect {|a| a.array_suffix }.nil?
71
84
  end
72
85
 
86
+ def has_class_pointer_arguments?
87
+ return @has_class_pointer_arguments if defined?(@has_class_pointer_arguments)
88
+ @has_class_pointer_arguments = !@arguments.detect {|a| !a.is_native? && a.is_pointer? }.nil?
89
+ end
90
+
91
+ def has_complex_arguments?
92
+ return @has_complex_arguments if defined?(@has_complex_arguments)
93
+ @has_complex_arguments = !(@arguments + [@return_value]).compact.detect {|a| a.complex? }.nil?
94
+ end
95
+
73
96
  def vararg?
74
97
  @arguments.last && @arguments.last.vararg?
75
98
  end
76
99
 
100
+ def arg_is_list(list_position, count_position)
101
+ @arguments[list_position ].is_list = true
102
+ @arguments[count_position].is_list_count = true
103
+ end
104
+
105
+ def template?
106
+ !@template_params.nil?
107
+ end
108
+
109
+ def template_params
110
+ @template_params
111
+ end
112
+
77
113
  def inspect
78
114
  "#<Function #{@prefix}_#{@name}(#{@arguments.inspect[1..-2]})>"
79
115
  end
@@ -100,12 +136,24 @@ module Dub
100
136
  end
101
137
 
102
138
  raw_type = (@xml/'/type').innerHTML
139
+
103
140
  if raw_type.strip == ''
104
141
  # no return type
105
142
  else
106
143
  arg = Argument.new(self, (@xml/'/type'))
107
144
  @return_value = arg unless arg.create_type =~ /void\s*$/
108
145
  end
146
+
147
+ @is_static = @xml[:static] == 'yes'
148
+ end
149
+
150
+ def parse_template_params
151
+ template_params = (@xml/'/templateparamlist/param')
152
+ if !template_params.empty?
153
+ @template_params = template_params.map do |param|
154
+ (param/'/type').innerHTML.gsub(/^\s*(typename|class)\s+/,'')
155
+ end
156
+ end
109
157
  end
110
158
  end
111
159
  end # Namespace
@@ -35,10 +35,6 @@ module Dub
35
35
  generator.group(self)
36
36
  end
37
37
 
38
- def name
39
- first.name
40
- end
41
-
42
38
  def method_name(overloaded_index = nil)
43
39
  first.method_name(overloaded_index)
44
40
  end
@@ -63,12 +59,14 @@ module Dub
63
59
  nil
64
60
  end
65
61
 
66
- def prefix
67
- first.prefix
68
- end
69
-
70
62
  def <=>(other)
71
63
  name <=> other.name
72
64
  end
65
+
66
+ private
67
+ def method_missing(method, *args)
68
+ first.send(method, *args)
69
+ end
70
+
73
71
  end
74
72
  end
data/lib/dub/group.rb CHANGED
@@ -6,5 +6,19 @@ module Dub
6
6
  super
7
7
  parse_defines
8
8
  end
9
+
10
+ def arg_is_list(argument_pos, count_pos)
11
+ each do |f|
12
+ f.arg_is_list(argument_pos, count_pos)
13
+ end
14
+ end
15
+
16
+ def members
17
+ if self.generator
18
+ @gen_members ||= self.generator.members_list(super, @ignores)
19
+ else
20
+ super
21
+ end
22
+ end
9
23
  end
10
24
  end
data/lib/dub/klass.rb CHANGED
@@ -8,7 +8,7 @@ module Dub
8
8
  class Klass
9
9
  include MemberExtraction
10
10
  attr_reader :name, :xml, :prefix, :constructor, :alias_names, :enums, :parent, :instanciations
11
- attr_accessor :header
11
+ attr_accessor :header, :string_format, :string_args
12
12
 
13
13
  def initialize(parent, name, xml, prefix = '')
14
14
  @parent, @name, @xml, @prefix = parent, name, xml, prefix
@@ -63,7 +63,7 @@ module Dub
63
63
  end
64
64
 
65
65
  def <=>(other)
66
- name <=> other.name
66
+ (name || "") <=> (other.name || "")
67
67
  end
68
68
 
69
69
  def [](name)
@@ -23,7 +23,11 @@ static int <%= @class.destructor_name %>(lua_State *L) {
23
23
 
24
24
  static int <%= @class.tostring_name %>(lua_State *L) {
25
25
  <%= @class.name %> **userdata = (<%= @class.name %>**)luaL_checkudata(L, 1, <%= @class.id_name.inspect %>);
26
- lua_pushfstring(L, "<%= @class.id_name %>: %p", *userdata);
26
+ <% if @class.string_format %>
27
+ lua_pushfstring(L, "<<%= @class.id_name %>: %p <%= @class.string_format %>>", *userdata, <%= @class.string_args %>);
28
+ <% else %>
29
+ lua_pushfstring(L, "<<%= @class.id_name %>: %p>", *userdata);
30
+ <% end %>
27
31
  return 1;
28
32
  }
29
33
 
@@ -56,7 +60,7 @@ static const struct lua_constants_Reg <%= @class.name %>_namespace_constants[] =
56
60
 
57
61
  void luaopen_<%= @class.lib_name %>(lua_State *L) {
58
62
  // Create the metatable which will contain all the member methods
59
- luaL_newmetatable(L, <%= @class.id_name.inspect %>); // "dub.Matrix"
63
+ luaL_newmetatable(L, <%= @class.id_name.inspect %>);
60
64
 
61
65
  // metatable.__index = metatable (find methods in the table itself)
62
66
  lua_pushvalue(L, -1);
@@ -65,7 +69,7 @@ void luaopen_<%= @class.lib_name %>(lua_State *L) {
65
69
  // register member methods
66
70
  luaL_register(L, NULL, <%= @class.name %>_member_methods);
67
71
 
68
- // register class methods in a global table like "dub"
72
+ // register class methods in a global namespace table
69
73
  luaL_register(L, <%= @class.prefix.inspect %>, <%= @class.name %>_namespace_methods);
70
74
 
71
75
  <% if @class.has_constants? %>
@@ -20,8 +20,9 @@ module Dub
20
20
 
21
21
  def method_registration(klass = @class)
22
22
  member_methods = (klass.members || []).map do |method|
23
+ next if method.static?
23
24
  "{%-20s, #{method.method_name(0)}}" % method.name.inspect
24
- end
25
+ end.compact
25
26
 
26
27
  member_methods << "{%-20s, #{klass.tostring_name}}" % "__tostring".inspect
27
28
  member_methods << "{%-20s, #{klass.destructor_name}}" % "__gc".inspect
@@ -29,10 +30,17 @@ module Dub
29
30
  member_methods.join(",\n")
30
31
  end
31
32
 
32
- def namespace_methods_registration
33
- @class.names.map do |name|
34
- "{%-20s, #{@class.constructor.method_name(0)}}" % name.inspect
35
- end.join(",\n")
33
+ def namespace_methods_registration(klass = @class)
34
+ global_methods = klass.names.map do |name|
35
+ "{%-20s, #{klass.constructor.method_name(0)}}" % name.inspect
36
+ end
37
+
38
+ (klass.members || []).map do |method|
39
+ next unless method.static?
40
+ global_methods << "{%-20s, #{method.method_name(0)}}" % "#{klass.name}_#{method.name}".inspect
41
+ end
42
+
43
+ global_methods.join(",\n")
36
44
  end
37
45
 
38
46
  def constants_registration(klass = @class)
@@ -61,10 +69,10 @@ module Dub
61
69
  def ignore_member?(member)
62
70
  if member.name =~ /^~/ || # do not build constructor
63
71
  member.name =~ /^operator/ || # no conversion operators
64
- member.original_signature =~ />/ || # no complex types in signature
72
+ member.has_complex_arguments? || # no complex arguments or return values
65
73
  member.has_array_arguments? ||
66
74
  member.vararg? ||
67
- member.original_signature =~ /void\s+\*/
75
+ member.original_signature =~ /void\s+\*/ # used to detect return value and parameters
68
76
  true # ignore
69
77
  elsif return_value = member.return_value
70
78
  return_value.type =~ />$/ || # no complex return types
@@ -4,9 +4,10 @@ require 'erb'
4
4
  module Dub
5
5
  module Lua
6
6
  class FunctionGen < Dub::Generator
7
- FLOAT_TYPES = [
7
+ NUMBER_TYPES = [
8
8
  'float',
9
9
  'double',
10
+ 'time_t',
10
11
  ]
11
12
 
12
13
  INT_TYPES = [
@@ -29,7 +30,11 @@ module Dub
29
30
  # Produce bindings for a group of overloaded functions
30
31
  def group(group)
31
32
  @group = group
32
- @group_template.result(binding)
33
+ if @group.members
34
+ @group_template.result(binding)
35
+ else
36
+ ''
37
+ end
33
38
  end
34
39
 
35
40
  def function(function)
@@ -45,6 +50,17 @@ module Dub
45
50
  Dub::Lua.namespace_generator
46
51
  end
47
52
 
53
+ def chooser_body(group = @group)
54
+ decision_tree = Argument.decision_tree(group.members)
55
+ res = []
56
+ res << "int type__ = lua_type(L, 1);"
57
+ if flatten_hash(decision_tree).include?(nil)
58
+ res << "int top__ = lua_gettop(L);"
59
+ end
60
+ res << switch(decision_tree)
61
+ res.join("\n")
62
+ end
63
+
48
64
  # Create a switch to choose the correct method from argument types (overloaded functions)
49
65
  def switch(hash_or_function, depth = 1)
50
66
  if hash_or_function.kind_of?(Function)
@@ -62,7 +78,7 @@ module Dub
62
78
  when :string
63
79
  res << "#{else_prefix}if (type__ == LUA_TSTRING) {"
64
80
  when nil
65
- res << "#{else_prefix}if (type__ == LUA_TNONE) {"
81
+ res << "#{else_prefix}if (top__ < #{depth}) {"
66
82
  else
67
83
  res << "#{else_prefix}if (type__ == LUA_TUSERDATA && is_userdata(L, #{depth}, \"#{type}\")) {"
68
84
  end
@@ -89,7 +105,7 @@ module Dub
89
105
  def body(func)
90
106
  res = []
91
107
 
92
- if func.member_method? && !func.constructor?
108
+ if func.member_method? && !func.constructor? && !func.static?
93
109
  klass = func.parent
94
110
  res << "#{klass.name} *self__ = *((#{klass.name}**)luaL_checkudata(L, 1, #{klass.id_name.inspect}));"
95
111
  res << "lua_remove(L, 1);"
@@ -132,16 +148,17 @@ module Dub
132
148
  "return #{method_name(func)}(L);"
133
149
  end
134
150
 
135
- def call_string(func, upto_arg)
151
+ def call_string(func, upto_arg = nil)
152
+ upto_arg ||= func.arguments.count
136
153
  if upto_arg == 0
137
- call_string = "#{func.name}();"
154
+ call_string = "#{func.call_name}();"
138
155
  else
139
- call_string = "#{func.name}(#{func.arguments[0..(upto_arg-1)].map{|arg| arg.in_call_type}.join(', ')});"
156
+ call_string = "#{func.call_name}(#{func.arguments[0..(upto_arg-1)].map{|arg| arg.in_call_type}.join(', ')});"
140
157
  end
141
158
 
142
159
  if func.constructor?
143
160
  call_string = "new #{call_string}"
144
- elsif func.member_method?
161
+ elsif func.member_method? && !func.static?
145
162
  call_string = "self__->#{call_string}"
146
163
  end
147
164
 
@@ -198,7 +215,7 @@ module Dub
198
215
  "#{type_def} = ptr_#{arg.name}(L, #{stack_pos});"
199
216
  end
200
217
  else
201
- if FLOAT_TYPES.include?(arg.type)
218
+ if NUMBER_TYPES.include?(arg.type)
202
219
  "#{type_def} = luaL_checknumber(L, #{stack_pos});"
203
220
  elsif BOOL_TYPES.include?(arg.type)
204
221
  "#{type_def} = lua_toboolean(L, #{stack_pos});"
@@ -214,12 +231,15 @@ module Dub
214
231
  end
215
232
 
216
233
  def flatten_hash(hash)
234
+ list = []
217
235
  hash.each do |k, v|
218
236
  if v.kind_of?(Hash)
219
- hash[k] = flatten_hash(v)
237
+ list << [k, flatten_hash(v)]
238
+ else
239
+ list << [k, v]
220
240
  end
221
241
  end
222
- hash.to_a.flatten
242
+ list.flatten
223
243
  end
224
244
 
225
245
  end # FunctionGen
@@ -5,6 +5,5 @@
5
5
 
6
6
  /** Overloaded function chooser for <%= @group.name %>(...) */
7
7
  <%= signature(@group.first, 0) %> {
8
- int type__ = lua_type(L, 1);
9
- <%= indent(switch(Argument.decision_tree(@group.members)), 2) %>
8
+ <%= indent(chooser_body, 2) %>
10
9
  }
@@ -50,7 +50,7 @@ module Dub
50
50
  end
51
51
 
52
52
  def members_list(all_members, ignore_list = [])
53
- list = all_members.map do |member_or_group|
53
+ list = (all_members || []).map do |member_or_group|
54
54
  if ignore_list.include?(member_or_group.name)
55
55
  nil
56
56
  elsif member_or_group.kind_of?(Array)
@@ -72,7 +72,9 @@ module Dub
72
72
  member.original_signature =~ />/ || # no complex types in signature
73
73
  member.has_array_arguments? ||
74
74
  member.vararg? ||
75
- member.original_signature =~ /void\s+\*/
75
+ member.original_signature =~ /void\s+\*/ ||
76
+ member.has_class_pointer_arguments?
77
+
76
78
  true # ignore
77
79
  elsif return_value = member.return_value
78
80
  return_value.type =~ />$/ || # no complex return types
@@ -17,7 +17,7 @@ module Dub
17
17
  @t_members_hash = {}
18
18
  # TODO: template functions
19
19
  (@xml/'memberdef').each do |member|
20
- Dub.logger.info "Parsing #{member.name}"
20
+ Dub.logger.info "Parsing #{(member/'name').innerHTML}"
21
21
  name = (member/"name").innerHTML
22
22
  if (member/'templateparamlist').first
23
23
  insert_member(member, name, @t_members_hash)
@@ -38,7 +38,7 @@ module Dub
38
38
  end
39
39
 
40
40
  def member(name)
41
- get_member(name.to_s)
41
+ get_member(name.to_s, @members_hash)
42
42
  end
43
43
 
44
44
  def members
@@ -52,9 +52,13 @@ module Dub
52
52
  end
53
53
  end
54
54
 
55
+ def template_method(name)
56
+ get_member(name.to_s, @t_members_hash)
57
+ end
58
+
55
59
  # Lazy construction of members
56
- def get_member(name)
57
- if member_or_group = @members_hash[name]
60
+ def get_member(name, source = @members_hash)
61
+ if member_or_group = source[name]
58
62
  if member_or_group.kind_of?(Array)
59
63
  if member_or_group.first.kind_of?(Hpricot::Elem)
60
64
  list = Dub::FunctionGroup.new(self)
@@ -65,7 +69,7 @@ module Dub
65
69
  member_or_group = nil if member_or_group == []
66
70
  end
67
71
  elsif member_or_group.kind_of?(Hpricot::Elem)
68
- @members_hash[name] = member_or_group = make_member(name, member_or_group)
72
+ source[name] = member_or_group = make_member(name, member_or_group)
69
73
  end
70
74
  end
71
75
  member_or_group
data/lib/dub/namespace.rb CHANGED
@@ -197,7 +197,8 @@ module Dub
197
197
  old_name = (ref_class/'/compoundname').first.innerHTML.gsub(/^.*::/,'')
198
198
 
199
199
  # replace class name
200
- class_def = ref_class.to_s.gsub(/#{old_name}&lt;.*?&gt;::/,"#{new_name}::")
200
+ # <type><ref refid="classcv_1_1_scalar__" kindref="compound">Scalar_</ref>&lt; _Tp &gt;</type>
201
+ class_def = ref_class.to_s.gsub(/<ref .*>#{old_name}<\/ref>&lt;.*?&gt;/, new_name)
201
202
  class_def = class_def.gsub(/#{old_name}/, new_name)
202
203
 
203
204
  # replace template types
@@ -220,7 +221,7 @@ module Dub
220
221
  class_xml = (Hpricot::XML(class_def)/'compounddef').first
221
222
 
222
223
  (class_xml/'*[@prot=private]').remove
223
- (class_xml/'templateparamlist').remove
224
+ (class_xml/'/templateparamlist').remove
224
225
  (class_xml/'').append("<originaltemplate>#{old_name}</originaltemplate>")
225
226
  (ref_class/'').append("<instanciation><name>#{new_name}</name><param>#{instanciations_params.join('</param><param>')}</param></instanciation>")
226
227
 
data/lib/dub/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Dub
2
- VERSION = '0.5.1'
2
+ VERSION = '0.6.0'
3
3
  end