ffi-swig-generator 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. data/.hg/branch.cache +2 -0
  2. data/.hg/dirstate +0 -0
  3. data/.hg/store/00changelog.i +0 -0
  4. data/.hg/store/00manifest.i +0 -0
  5. data/.hg/store/data/.hgignore.i +0 -0
  6. data/.hg/store/data/.hgtags.i +0 -0
  7. data/.hg/store/data/_history.txt.i +0 -0
  8. data/.hg/store/data/_r_e_a_d_m_e.rdoc.i +0 -0
  9. data/.hg/store/data/_rakefile.i +0 -0
  10. data/.hg/store/data/cucumber.yml.i +0 -0
  11. data/.hg/store/data/examples/_rakefile.i +0 -0
  12. data/.hg/store/data/features/generate.feature.i +0 -0
  13. data/.hg/store/data/features/step__definitions/generate.rb.i +0 -0
  14. data/.hg/store/data/features/support/env.rb.i +0 -0
  15. data/.hg/store/data/features/support/results.rb.i +0 -0
  16. data/.hg/store/data/features/support/templates.rb.i +0 -0
  17. data/.hg/store/data/lib/ffi-swig-generator.rb.i +0 -0
  18. data/.hg/store/data/lib/generator/application.rb.i +0 -0
  19. data/.hg/store/data/lib/generator/constant.rb.i +0 -0
  20. data/.hg/store/data/lib/generator/enum.rb.i +0 -0
  21. data/.hg/store/data/lib/generator/function.rb.i +0 -0
  22. data/.hg/store/data/lib/generator/generator.rb.i +0 -0
  23. data/.hg/store/data/lib/generator/generatortask.rb.i +0 -0
  24. data/.hg/store/data/lib/generator/logger.rb.i +0 -0
  25. data/.hg/store/data/lib/generator/node.rb.i +0 -0
  26. data/.hg/store/data/lib/generator/parser.rb.i +0 -0
  27. data/.hg/store/data/lib/generator/struct.rb.i +0 -0
  28. data/.hg/store/data/lib/generator/type.rb.i +0 -0
  29. data/.hg/store/data/lib/generator/types.rb.i +0 -0
  30. data/.hg/store/data/spec/generator/constant__spec.rb.i +0 -0
  31. data/.hg/store/data/spec/generator/enum__spec.rb.i +0 -0
  32. data/.hg/store/data/spec/generator/function__spec.rb.i +0 -0
  33. data/.hg/store/data/spec/generator/generator__spec.rb.i +0 -0
  34. data/.hg/store/data/spec/generator/node__spec.rb.i +0 -0
  35. data/.hg/store/data/spec/generator/parser__spec.rb.i +0 -0
  36. data/.hg/store/data/spec/generator/struct__spec.rb.i +0 -0
  37. data/.hg/store/data/spec/generator/swig/constants.i.i +0 -0
  38. data/.hg/store/data/spec/generator/swig/functions.i.i +0 -0
  39. data/.hg/store/data/spec/generator/swig/testlib.i.i +0 -0
  40. data/.hg/store/data/spec/generator/swig/typedefs.i.i +0 -0
  41. data/.hg/store/data/spec/generator/swig/types.i.i +0 -0
  42. data/.hg/store/data/spec/generator/type__spec.rb.i +0 -0
  43. data/.hg/store/data/spec/spec__helper.rb.i +0 -0
  44. data/.hg/store/data/tasks/cucumber.rake.i +0 -0
  45. data/.hg/store/undo +0 -0
  46. data/.hg/undo.dirstate +0 -0
  47. data/.hgignore +3 -0
  48. data/.hgtags +1 -0
  49. data/History.txt +22 -2
  50. data/README.rdoc +26 -22
  51. data/Rakefile +2 -3
  52. data/cucumber.yml +1 -0
  53. data/examples/Rakefile +5 -3
  54. data/examples/generated/libc_wrap.rb +18 -0
  55. data/examples/generated/libc_wrap.xml +597 -0
  56. data/examples/generated/wiiuse_wrap.rb +322 -0
  57. data/examples/generated/wiiuse_wrap.xml +9025 -0
  58. data/features/generate.feature +45 -0
  59. data/features/step_definitions/generate.rb +32 -0
  60. data/features/support/env.rb +4 -0
  61. data/features/support/templates.rb +381 -0
  62. data/lib/ffi-swig-generator.rb +1 -1
  63. data/lib/generator/application.rb +1 -1
  64. data/lib/generator/constant.rb +24 -0
  65. data/lib/generator/enum.rb +38 -0
  66. data/lib/generator/function.rb +71 -0
  67. data/lib/generator/generatortask.rb +21 -9
  68. data/lib/generator/logger.rb +29 -0
  69. data/lib/generator/node.rb +19 -0
  70. data/lib/generator/parser.rb +168 -0
  71. data/lib/generator/struct.rb +76 -0
  72. data/lib/generator/type.rb +128 -0
  73. data/lib/generator/types.rb +36 -0
  74. data/spec/generator/constant_spec.rb +17 -0
  75. data/spec/generator/enum_spec.rb +29 -0
  76. data/spec/generator/function_spec.rb +66 -0
  77. data/spec/generator/parser_spec.rb +250 -0
  78. data/spec/generator/struct_spec.rb +77 -0
  79. data/spec/generator/swig/constants.i +5 -0
  80. data/spec/generator/swig/functions.i +8 -0
  81. data/spec/generator/swig/testlib.i +42 -0
  82. data/spec/generator/swig/typedefs.i +1 -0
  83. data/spec/generator/swig/types.i +1 -0
  84. data/spec/generator/type_spec.rb +38 -0
  85. data/spec/spec_helper.rb +6 -0
  86. data/tasks/cucumber.rake +8 -0
  87. metadata +58 -18
  88. data/lib/generator/generator.rb +0 -344
  89. data/spec/generator/generator_spec.rb +0 -248
@@ -1,7 +1,7 @@
1
1
  module FFI
2
2
  module Generator
3
3
  # :stopdoc:
4
- VERSION = '0.2.1'
4
+ VERSION = '0.3.0'
5
5
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
6
6
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
7
7
  # :startdoc:
@@ -22,7 +22,7 @@ EOU
22
22
  process_args
23
23
  if ARGV.size == 2
24
24
  File.open(ARGV[1], 'w') do |file|
25
- file << FFI::Generator::Parser.generate(Nokogiri::XML(File.open(ARGV[0])))
25
+ file << FFI::Generator::Parser.new.generate(Nokogiri::XML(File.open(ARGV[0])))
26
26
  end
27
27
  else
28
28
  help
@@ -0,0 +1,24 @@
1
+ module FFI
2
+ module Generator
3
+ require libpath('generator/node')
4
+ class Constant < Node
5
+ def initialize(params = { })
6
+ super
7
+ @name, @value = get_attr('sym_name'), get_attr('value')
8
+ end
9
+ def to_s
10
+ @indent_str + "#{@name} = #{sanitize!(@value)}"
11
+ end
12
+ private
13
+ def sanitize!(value)
14
+ if @value.match(/\d+U$/) or @value.match(/\d+L$/)
15
+ result = value.chop
16
+ elsif @value.match(/\d+UL$/)
17
+ result = @value.chop.chop
18
+ else
19
+ result = @value
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,38 @@
1
+ module FFI
2
+ module Generator
3
+ require libpath('generator/node')
4
+ class Enum < Node
5
+ def initialize(params = { })
6
+ super
7
+ eval_items
8
+ end
9
+ def to_s
10
+ @items.sort { |i1, i2| i1[1] <=> i2[1] }.inject("") do |result, item|
11
+ result << assignment_str(item[0], item[1]) << "\n"
12
+ end
13
+ end
14
+ private
15
+ def assignment_str(name, value)
16
+ @indent_str + "#{name} = #{value}"
17
+ end
18
+ def eval_expr(expr)
19
+ if expr.include?('+')
20
+ (@items[expr[/\w+/]].to_i + 1).to_s
21
+ else
22
+ 0.to_s
23
+ end
24
+ end
25
+ def eval_items
26
+ @items = {}
27
+ get_items.each do |i|
28
+ node = Node.new(:node => i)
29
+ @items[node.get_attr('name')] = node.get_attr('enumvalueex') ? eval_expr(node.get_attr('enumvalueex')) : node.get_attr('enumvalue')
30
+ end
31
+ @items
32
+ end
33
+ def get_items
34
+ @node / './enumitem'
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,71 @@
1
+ module FFI
2
+ module Generator
3
+ require libpath('generator/type')
4
+ class Function < Type
5
+ class Argument < Type
6
+ def to_s
7
+ case get_attr('type')
8
+ when 'void'
9
+ nil
10
+ when 'v(...)'
11
+ ':varargs'
12
+ else
13
+ super
14
+ end
15
+ end
16
+ end
17
+ def initialize(params = { })
18
+ super
19
+ @type = get_attr('type')
20
+ end
21
+ def to_s
22
+ params = get_params(@node).inject([]) do |array, node|
23
+ array << Argument.new(:node => node, :typedefs => @typedefs).to_s
24
+ end
25
+ @indent_str + "attach_function :#{@symname}, [ #{params.join(', ')} ], #{get_rtype}"
26
+ end
27
+ private
28
+ def get_params(node)
29
+ parmlist = node / './attributelist/parmlist/parm'
30
+ end
31
+ def get_rtype
32
+ pointer = get_attr('decl').scan(/^f\(.*\)\.(p)/).flatten[0]
33
+ declaration = pointer ? "p.#{get_attr('type')}" : get_attr('type')
34
+ Type.new(:declaration => declaration, :typedefs => @typedefs).to_s
35
+ end
36
+ end
37
+ class Callback < Function
38
+ def initialize(params = { })
39
+ super(params)
40
+ @inline = true if params[:inline] == true
41
+ end
42
+ def to_s
43
+ unless @inline
44
+ @indent_str + "callback(:#{@symname}, [ #{get_params.join(', ')} ], #{get_rtype})"
45
+ else
46
+ @indent_str + "callback([ #{get_params.join(', ')} ], #{get_rtype})"
47
+ end
48
+ end
49
+ private
50
+ def get_params
51
+ params = (@node / './attributelist/parmlist/parm')
52
+ declaration = decl
53
+ unless params.empty?
54
+ result = params.inject([]) do |array, node|
55
+ declaration.gsub!(/#{Regexp.escape(Type.new(:node => node, :typedefs => @typedefs).get_attr('type'))}/, '')
56
+ array << Argument.new(:node => node, :typedefs => @typedefs).to_s
57
+ end
58
+ else
59
+ result = @full_decl.scan(/p.f\((.*)\)/).flatten[0].split(',').inject([]) do |array, type|
60
+ array << (type == 'void' ? '' : Type.new(:declaration => type, :typedefs => @typedefs).to_s)
61
+ end
62
+ end
63
+ @full_decl = declaration + type
64
+ result
65
+ end
66
+ def get_rtype
67
+ Type.new(:declaration => @full_decl.scan(/f\([a-zA-z0-9,.\s\(\)]*\)\.([a-zA-Z0-9\.,\s\(\)]+)/).flatten[0], :typedefs => @typedefs).to_s
68
+ end
69
+ end
70
+ end
71
+ end
@@ -3,8 +3,12 @@ require 'rake/tasklib'
3
3
  module FFI
4
4
  module Generator
5
5
  class Task < Rake::TaskLib
6
- def initialize(options = {})
6
+ attr_accessor :input_fn, :output_dir
7
+ def initialize(options = {}, &blk)
7
8
  @options = { :input_fn => '*.i', :output_dir => 'generated/' }.merge(options)
9
+ @input_fn = @options[:input_fn]
10
+ @output_dir = @options[:output_dir]
11
+ yield self if block_given?
8
12
  namespace 'ffi' do
9
13
  define_generate_task
10
14
  define_clean_task
@@ -14,19 +18,27 @@ module FFI
14
18
  def define_file_task(fn, xml_fn, output_fn)
15
19
  desc "Generate #{output_fn} from #{fn}"
16
20
  file output_fn => fn do
17
- mkdir_p @options[:output_dir], :verbose => false
18
- puts "Generating #{xml_fn} from #{fn} using SWIG..."
21
+ mkdir_p @output_dir, :verbose => false
22
+ Logger.info("#{fn} -> #{xml_fn}")
19
23
  `swig -xml #{xml_fn} #{fn}`
20
- puts "Generating #{output_fn} from #{xml_fn}..."
24
+ Logger.info("#{xml_fn} -> #{output_fn}")
25
+ parser = Parser.new
26
+ config_basename = File.basename(fn, File.extname(fn))
27
+ config_dir = File.dirname(fn)
28
+ config_fn = File.join(config_dir, "#{config_basename}.rb")
29
+ if File.exists?(config_fn)
30
+ Logger.info("Using configuration in #{config_fn}...")
31
+ parser.load_config(config_fn)
32
+ end
21
33
  File.open(output_fn, 'w') do |file|
22
- file << Parser.generate(Nokogiri::XML(File.open(xml_fn)))
34
+ file << parser.generate(Nokogiri::XML(File.open(xml_fn)))
23
35
  end
24
36
  end
25
37
  end
26
38
  def define_file_tasks
27
- Dir.glob(@options[:input_fn]).inject([]) do |output_fns, fn|
28
- output_fn = File.join(@options[:output_dir], "#{File.basename(fn, '.i')}_wrap.rb")
29
- xml_fn = File.join(@options[:output_dir], "#{File.basename(fn, '.i')}_wrap.xml")
39
+ Dir.glob(@input_fn).inject([]) do |output_fns, fn|
40
+ output_fn = File.join(@output_dir, "#{File.basename(fn, '.i')}_wrap.rb")
41
+ xml_fn = File.join(@output_dir, "#{File.basename(fn, '.i')}_wrap.xml")
30
42
  define_file_task(fn, xml_fn, output_fn)
31
43
  output_fns << output_fn
32
44
  end
@@ -37,7 +49,7 @@ module FFI
37
49
  def define_clean_task
38
50
  desc 'Remove all generated files'
39
51
  task :clean do
40
- rm_rf @options[:output_dir] unless @options[:output_dir] == '.'
52
+ rm_rf @output_dir unless @output_dir == '.'
41
53
  end
42
54
  end
43
55
  end
@@ -0,0 +1,29 @@
1
+ require 'logger'
2
+
3
+ module FFI
4
+ module Generator
5
+ module Logger
6
+ @logger = ::Logger.new(STDOUT)
7
+ @logger.progname = 'ffi-swig-generator'
8
+ def self.fatal(message)
9
+ @logger.fatl(message)
10
+ end
11
+ def self.error(message)
12
+ @logger.error(message)
13
+ end
14
+ def self.warn(message)
15
+ @logger.warn(message)
16
+ end
17
+ def self.info(message)
18
+ @logger.info(message)
19
+ end
20
+ def self.debug(message)
21
+ @logger.debug(message)
22
+ end
23
+ def set_level(level)
24
+ @logger.level = level
25
+ end
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,19 @@
1
+ module FFI
2
+ module Generator
3
+ class Node
4
+ attr_reader :symname
5
+ def initialize(params = { })
6
+ params = { :indent => 0 }.merge(params)
7
+ @node, @indent = params[:node], params[:indent]
8
+ @indent_str = ' ' * @indent
9
+ @symname = get_attr('name')
10
+ end
11
+ def get_attr(name)
12
+ if @node
13
+ attr = (@node / "./attributelist/attribute[@name='#{name}']").first
14
+ attr['value'] if attr
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,168 @@
1
+ require 'rubygems'
2
+ require 'nokogiri'
3
+
4
+ module FFI
5
+ module Generator
6
+ NestedStructureNotSupported =<<EOM
7
+ Nested structures are not correctly supported at the moment.
8
+ Please check the order of the declarations in the structure below.
9
+ EOM
10
+ class Parser
11
+ def initialize(indent = 2)
12
+ @indent = indent
13
+ @typedefs = {}
14
+ @nested_type = {}
15
+ @nested_structure = {}
16
+ @ignored = []
17
+ @ignore_at_second_pass = []
18
+ end
19
+ def generate(node)
20
+ result = ""
21
+ result = pass(node)
22
+ result = pass(node) unless @nested_type.empty?
23
+ result
24
+ end
25
+ def ignore(*ignored)
26
+ @ignored.concat(ignored)
27
+ end
28
+ def load_config(fn)
29
+ eval(File.read(fn), binding)
30
+ end
31
+ private
32
+ def get_attr(node, name)
33
+ nodes = (node / "./attributelist/attribute[@name='#{name}']")
34
+ nodes.first['value'] if nodes.first
35
+ end
36
+ def get_id(node)
37
+ node.id
38
+ end
39
+ def get_verbatim(node)
40
+ get_attr(node, 'code')
41
+ end
42
+ def add_type(ctype, rtype)
43
+ @typedefs[ctype] = rtype
44
+ end
45
+ def add_nested_structure(symname, id)
46
+ (@nested_structure[@nested_type[symname]] ||= []) << id
47
+ end
48
+ def insert_runtime?(node)
49
+ get_attr(node, 'section') == 'runtime'
50
+ end
51
+ def constant?(node)
52
+ node.name == 'constant'
53
+ end
54
+ def enum?(node)
55
+ node.name == 'enum'
56
+ end
57
+ def function_decl?(node)
58
+ node.name == 'cdecl' and get_attr(node, 'kind') == 'function'
59
+ end
60
+ def struct?(node)
61
+ node.name == 'class' and get_attr(node, 'kind') == 'struct'
62
+ end
63
+ def union?(node)
64
+ node.name == 'class' and get_attr(node, 'kind') == 'union'
65
+ end
66
+ def typedef?(node)
67
+ node.name == 'cdecl' and get_attr(node, 'kind') == 'typedef'
68
+ end
69
+ def callback?(node)
70
+ get_attr(node, 'decl') =~ /^p\.f\(/
71
+ end
72
+ def nested_type?(node)
73
+ get_attr(node, 'nested')
74
+ end
75
+ def fix_nested_structure(node)
76
+ result = ""
77
+ if struct?(node)
78
+ s = Struct.new(:node => node, :indent => @indent, :typedefs => @typedefs)
79
+ result << (@nested_structure.has_key?(s.symname) ? fixme(s.to_s, NestedStructureNotSupported) : s.to_s)
80
+ else
81
+ u = Union.new(:node => node, :indent => @indent, :typedefs => @typedefs)
82
+ result << (@nested_structure.has_key?(u.symname) ? fixme(u.to_s, NestedStructureNotSupported) : u.to_s)
83
+ end
84
+ result
85
+ end
86
+ # Search for nested structures and fix them.
87
+ def find_nested_struct(node, id)
88
+ result = ""
89
+ nested_node = (node.parent / "class[@id='#{id}']").first
90
+ if @nested_structure.has_key?(get_attr(nested_node, 'name'))
91
+ @nested_structure[get_attr(nested_node, 'name')].reverse.each do |id|
92
+ result << find_nested_struct(nested_node, id)
93
+ end
94
+ end
95
+ result << fix_nested_structure(nested_node) if nested_node
96
+ end
97
+ def fixme(code, message)
98
+ comment('FIXME: ' + message) << comment(code)
99
+ end
100
+ def comment(code)
101
+ code.split(/\n/).map { |line| "# #{line}" }.join("\n") << "\n"
102
+ end
103
+ def has_nested_structures?(node, symname)
104
+ @nested_structure[symname] and not @nested_structure[symname].empty?
105
+ end
106
+ def prepend_nested_structures(node, symname)
107
+ result = ""
108
+ if has_nested_structures?(node, symname)
109
+ @nested_structure[symname].reverse.each do |nested_id|
110
+ result << find_nested_struct(node, nested_id)
111
+ end
112
+ @nested_structure[symname].clear
113
+ end
114
+ result
115
+ end
116
+ def handle_nested_structure(node, symname)
117
+ if @nested_type[symname]
118
+ add_nested_structure(symname, node.attributes['id'])
119
+ @ignore_at_second_pass << node.attributes['id']
120
+ end
121
+ prepend_nested_structures(node, symname)
122
+ end
123
+ def pass(node)
124
+ result = ""
125
+ node.traverse do |node|
126
+ unless @ignored.include?(get_attr(node, 'name'))
127
+ if constant?(node)
128
+ result << Constant.new(:node => node, :indent => @indent).to_s << "\n"
129
+ elsif typedef?(node)
130
+ typedef = Type.new(:node => node)
131
+ add_type(typedef.symname, typedef.full_decl)
132
+ if callback?(node)
133
+ cb = Callback.new(:node => node, :indent => @indent, :typedefs => @typedefs).to_s << "\n"
134
+ add_type(typedef.symname, "callback #{typedef.symname}")
135
+ result << cb.to_s
136
+ end
137
+ elsif enum?(node)
138
+ e = Enum.new(:node => node, :indent => @indent)
139
+ add_type(e.symname, Generator::TYPES['int'])
140
+ result << e.to_s << "\n"
141
+ elsif struct?(node)
142
+ s = Struct.new(:node => node, :indent => @indent, :typedefs => @typedefs)
143
+ add_type(s.symname, "struct #{s.symname}")
144
+ unless @ignore_at_second_pass.include? node.attributes['id']
145
+ nested = handle_nested_structure(node, s.symname)
146
+ result << (nested.empty? ? s.to_s : nested << fixme(s.to_s, NestedStructureNotSupported))
147
+ end
148
+ elsif union?(node)
149
+ s = Union.new(:node => node, :indent => @indent, :typedefs => @typedefs)
150
+ add_type(s.symname, "union #{s.symname}")
151
+ unless @ignore_at_second_pass.include? node.attributes['id']
152
+ nested = handle_nested_structure(node, s.symname)
153
+ result << (nested.empty? ? s.to_s : nested << fixme(s.to_s, NestedStructureNotSupported))
154
+ end
155
+ elsif function_decl?(node)
156
+ result << Function.new(:node => node, :indent => @indent, :typedefs => @typedefs).to_s << "\n"
157
+ elsif nested_type?(node)
158
+ @nested_type[get_attr(node, 'type')] = get_attr(node, 'nested')
159
+ elsif node.name == 'insert' and not insert_runtime?(node) and not node.parent.name == 'class'
160
+ result << get_verbatim(node)
161
+ end
162
+ end
163
+ end
164
+ result
165
+ end
166
+ end
167
+ end
168
+ end
@@ -0,0 +1,76 @@
1
+ module FFI
2
+ module Generator
3
+ require libpath('generator/node')
4
+ class Struct< Type
5
+ def self.string_accessors(field_name, indent = 0)
6
+ result = <<-code
7
+ def #{field_name}=(str)
8
+ @#{field_name} = FFI::MemoryPointer.from_string(str)
9
+ self[:#{field_name}] = @#{field_name}
10
+ end
11
+ def #{field_name}
12
+ @#{field_name}.get_string(0)
13
+ end
14
+ code
15
+ result.split("\n").map { |line| ' ' * indent + line }.join("\n") << "\n"
16
+ end
17
+ def self.callback_accessors(field_name, indent = 0)
18
+ result = <<-code
19
+ def #{field_name}=(cb)
20
+ @#{field_name} = cb
21
+ self[:#{field_name}] = @#{field_name}
22
+ end
23
+ def #{field_name}
24
+ @#{field_name}
25
+ end
26
+ code
27
+ result.split("\n").collect { |line| ' ' * indent + line }.join("\n") << "\n"
28
+ end
29
+ def self.camelcase(name)
30
+ name.gsub(/^_?\w|\_\w/) { |c| c.upcase }.delete('_')
31
+ end
32
+ def initialize(params = { })
33
+ super
34
+ @name = self.class.camelcase(@symname)
35
+ end
36
+ def to_s
37
+ fields_str = fields.inject("") do |str, f|
38
+ str << @indent_str + ' ' * 9 << f.join(', ') << ",\n"
39
+ end
40
+ code = klass_string + @indent_str + " layout(\n" + fields_str.chomp.chomp(',') + "\n" + @indent_str + " )\n" + accessors + @indent_str + "end\n"
41
+ end
42
+ private
43
+ def klass_string
44
+ @indent_str + "class #{@name} < FFI::Struct\n"
45
+ end
46
+ def fields
47
+ (@node / 'cdecl').inject([]) do |array, field|
48
+ type_node = Type.new(:node => field, :typedefs => @typedefs).to_s
49
+ type = type_node.to_s
50
+ array << [":#{Node.new(:node => field).symname}", type == ':string' ? ':pointer' : type]
51
+ end
52
+ end
53
+ def accessors
54
+ result = ""
55
+ fields = (@node / 'cdecl').map do |field|
56
+ [Node.new(:node => field).symname, "#{Type.new(:node => field, :typedefs => @typedefs)}"]
57
+ end
58
+ fields.each do |field|
59
+ if field[1] == ':string'
60
+ result << self.class.string_accessors(field[0], @indent + 2)
61
+ elsif field[1] =~ /^callback/ or @typedefs[field[1].delete(':')] =~ /^callback/
62
+ result << self.class.callback_accessors(field[0], @indent + 2)
63
+ end
64
+ end
65
+ result += "\n" unless result.empty?
66
+ result
67
+ end
68
+ end
69
+ class Union < FFI::Generator::Struct
70
+ private
71
+ def klass_string
72
+ @indent_str + "class #{@name} < FFI::Union\n"
73
+ end
74
+ end
75
+ end
76
+ end