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