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.
- data/.hg/branch.cache +2 -0
- data/.hg/dirstate +0 -0
- data/.hg/store/00changelog.i +0 -0
- data/.hg/store/00manifest.i +0 -0
- data/.hg/store/data/.hgignore.i +0 -0
- data/.hg/store/data/.hgtags.i +0 -0
- data/.hg/store/data/_history.txt.i +0 -0
- data/.hg/store/data/_r_e_a_d_m_e.rdoc.i +0 -0
- data/.hg/store/data/_rakefile.i +0 -0
- data/.hg/store/data/cucumber.yml.i +0 -0
- data/.hg/store/data/examples/_rakefile.i +0 -0
- data/.hg/store/data/features/generate.feature.i +0 -0
- data/.hg/store/data/features/step__definitions/generate.rb.i +0 -0
- data/.hg/store/data/features/support/env.rb.i +0 -0
- data/.hg/store/data/features/support/results.rb.i +0 -0
- data/.hg/store/data/features/support/templates.rb.i +0 -0
- data/.hg/store/data/lib/ffi-swig-generator.rb.i +0 -0
- data/.hg/store/data/lib/generator/application.rb.i +0 -0
- data/.hg/store/data/lib/generator/constant.rb.i +0 -0
- data/.hg/store/data/lib/generator/enum.rb.i +0 -0
- data/.hg/store/data/lib/generator/function.rb.i +0 -0
- data/.hg/store/data/lib/generator/generator.rb.i +0 -0
- data/.hg/store/data/lib/generator/generatortask.rb.i +0 -0
- data/.hg/store/data/lib/generator/logger.rb.i +0 -0
- data/.hg/store/data/lib/generator/node.rb.i +0 -0
- data/.hg/store/data/lib/generator/parser.rb.i +0 -0
- data/.hg/store/data/lib/generator/struct.rb.i +0 -0
- data/.hg/store/data/lib/generator/type.rb.i +0 -0
- data/.hg/store/data/lib/generator/types.rb.i +0 -0
- data/.hg/store/data/spec/generator/constant__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/enum__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/function__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/generator__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/node__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/parser__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/struct__spec.rb.i +0 -0
- data/.hg/store/data/spec/generator/swig/constants.i.i +0 -0
- data/.hg/store/data/spec/generator/swig/functions.i.i +0 -0
- data/.hg/store/data/spec/generator/swig/testlib.i.i +0 -0
- data/.hg/store/data/spec/generator/swig/typedefs.i.i +0 -0
- data/.hg/store/data/spec/generator/swig/types.i.i +0 -0
- data/.hg/store/data/spec/generator/type__spec.rb.i +0 -0
- data/.hg/store/data/spec/spec__helper.rb.i +0 -0
- data/.hg/store/data/tasks/cucumber.rake.i +0 -0
- data/.hg/store/undo +0 -0
- data/.hg/undo.dirstate +0 -0
- data/.hgignore +3 -0
- data/.hgtags +1 -0
- data/History.txt +22 -2
- data/README.rdoc +26 -22
- data/Rakefile +2 -3
- data/cucumber.yml +1 -0
- data/examples/Rakefile +5 -3
- data/examples/generated/libc_wrap.rb +18 -0
- data/examples/generated/libc_wrap.xml +597 -0
- data/examples/generated/wiiuse_wrap.rb +322 -0
- data/examples/generated/wiiuse_wrap.xml +9025 -0
- data/features/generate.feature +45 -0
- data/features/step_definitions/generate.rb +32 -0
- data/features/support/env.rb +4 -0
- data/features/support/templates.rb +381 -0
- data/lib/ffi-swig-generator.rb +1 -1
- data/lib/generator/application.rb +1 -1
- data/lib/generator/constant.rb +24 -0
- data/lib/generator/enum.rb +38 -0
- data/lib/generator/function.rb +71 -0
- data/lib/generator/generatortask.rb +21 -9
- data/lib/generator/logger.rb +29 -0
- data/lib/generator/node.rb +19 -0
- data/lib/generator/parser.rb +168 -0
- data/lib/generator/struct.rb +76 -0
- data/lib/generator/type.rb +128 -0
- data/lib/generator/types.rb +36 -0
- data/spec/generator/constant_spec.rb +17 -0
- data/spec/generator/enum_spec.rb +29 -0
- data/spec/generator/function_spec.rb +66 -0
- data/spec/generator/parser_spec.rb +250 -0
- data/spec/generator/struct_spec.rb +77 -0
- data/spec/generator/swig/constants.i +5 -0
- data/spec/generator/swig/functions.i +8 -0
- data/spec/generator/swig/testlib.i +42 -0
- data/spec/generator/swig/typedefs.i +1 -0
- data/spec/generator/swig/types.i +1 -0
- data/spec/generator/type_spec.rb +38 -0
- data/spec/spec_helper.rb +6 -0
- data/tasks/cucumber.rake +8 -0
- metadata +58 -18
- data/lib/generator/generator.rb +0 -344
- data/spec/generator/generator_spec.rb +0 -248
data/lib/ffi-swig-generator.rb
CHANGED
@@ -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
|
-
|
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 @
|
18
|
-
|
21
|
+
mkdir_p @output_dir, :verbose => false
|
22
|
+
Logger.info("#{fn} -> #{xml_fn}")
|
19
23
|
`swig -xml #{xml_fn} #{fn}`
|
20
|
-
|
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 <<
|
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(@
|
28
|
-
output_fn = File.join(@
|
29
|
-
xml_fn = File.join(@
|
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 @
|
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
|