ffi-swig-generator 0.2.1 → 0.3.0

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 (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