lib/generator/generator.rb in remogatto-ffi-generator-0.1.0 vs lib/generator/generator.rb in remogatto-ffi-generator-0.2.0
- old
+ new
@@ -41,140 +41,151 @@
@typedefs[ctype] = rtype
end
end
class Node
attr_reader :symname
- def initialize(node, indent = 0)
- @node = node
- @indent = ' ' * indent
+ def initialize(params = { })
+ params = { :indent => 0 }.merge(params)
+ @node, @indent = params[:node], params[:indent]
+ @indent_str = ' ' * @indent
@symname = get_attr('name')
end
def get_attr(name)
- attr = (@node / "./attributelist/attribute[@name='#{name}']").first
- attr['value'] if attr
+ if @node
+ attr = (@node / "./attributelist/attribute[@name='#{name}']").first
+ attr['value'] if attr
+ end
end
end
class Type < Node
- def initialize(node, indent = 0)
+ def initialize(params = { })
super
- @type = get_attr('type')
- @decl = get_attr('decl')
- @statement = @type.to_s + @decl.to_s
+ @statement = params[:statement] || get_statement
end
def to_s
get_type
end
private
+ def get_statement
+ get_attr('decl').to_s + get_attr('type').to_s if @node
+ end
def is_native?
- Generator::TYPES.has_key?(@type)
+ Generator::TYPES.has_key?(@statement)
end
def is_pointer?
- @decl and @decl[/(\)\.p|^p)\./]
+ @statement[/^p\./] and not is_callback?
end
def is_enum?
- @type[/^enum/]
+ @statement[/^enum/]
end
def is_array?
- @decl and @decl[/\w+\(\d+\)/]
+ @statement and @statement[/\w+\(\d+\)/]
end
def is_struct?
- @type[/^struct/]
+ @statement[/^struct/]
end
def is_union?
- @type[/^union/]
+ @statement[/^union/]
end
def is_constant?
- @type[/^q\(const\)/]
+ @statement[/^q\(const\)/]
end
+ def is_callback?
+ @statement[/^p.f\(/]
+ end
def native
if is_native?
- @type = Generator::TYPES[@type]
+ @statement = Generator::TYPES[@statement]
get_type
end
end
def constant
if is_constant?
- @type = @type.scan(/^q\(const\)\.(.+)/).flatten[0]
+ @statement = @statement.scan(/^q\(const\)\.(.+)/).flatten[0]
get_type
end
end
def pointer
if is_pointer?
- if @type[/char/]
- @type = ':string'
- @decl.gsub!(/p./, '')
+ if @statement[/char/] and @statement.scan(/p\./).size == 1
+ @statement = ':string'
+ # @decl.gsub!(/p\./, '')
get_type
else
return ':pointer'
end
end
end
def array
if is_array?
- num = @decl.scan(/\w+\((\d+)\)/).flatten[0]
- @decl.gsub!(/\w+\(\d+\)/, '')
+ num = @statement.scan(/\w+\((\d+)\)/).flatten[0]
+ @statement.gsub!(/\w+\(\d+\)\./, '')
"[#{get_type}, #{num}]"
end
end
def struct
if is_struct?
- @type = Structure.camelcase(@type.scan(/^struct\s(\w+)/).flatten[0])
+ @statement = Structure.camelcase(@statement.scan(/^struct\s(\w+)/).flatten[0])
get_type
end
end
def union
if is_union?
- @type = Union.camelcase(@type.scan(/^union\s(\w+)/).flatten[0])
+ @statement = Union.camelcase(@statement.scan(/^union\s(\w+)/).flatten[0])
get_type
end
end
def enum
if is_enum?
- @type = Generator::TYPES['int']
+ @statement = Generator::TYPES['int']
get_type
end
end
+ def callback
+ Callback.new(:node => @node).to_s if is_callback?
+ end
def typedef
- if Generator.typedefs.has_key?(@type)
- @type = Generator.typedefs[@type]
+ if Generator.typedefs.has_key?(@statement)
+ @statement = Generator.typedefs[@statement]
get_type
end
end
def get_type
- constant || pointer || enum || typedef || native || struct || union || array || "#{@type}"
+ constant || pointer || enum || typedef || native || struct || union || array || callback || "#{@statement}"
end
end
class Typedef < Type
- attr_reader :symname, :type
- def initialize(node, indent = 0)
+ attr_reader :symname, :statement
+ def initialize(params = { })
super
@symname = get_attr('name')
- @type = is_pointer? ? ':pointer' : get_attr('type')
+ # @type = is_pointer? ? ':pointer' : get_attr('type')
+ # p @statement
end
end
class Constant < Node
- def initialize(node, indent = 0)
+ def initialize(params = { })
super
@name, @value = get_attr('sym_name'), get_attr('value')
end
def to_s
- @indent + "#{@name} = #{@value}"
+ @indent_str + "#{@name} = #{@value}"
end
end
class Enum < Node
- def initialize(node, indent = 0)
+ def initialize(params = { })
super
eval_items
end
def to_s
@items.sort { |i1, i2| i1[1] <=> i2[1] }.inject("") do |result, item|
result << assignment_str(item[0], item[1]) << "\n"
end
end
private
def assignment_str(name, value)
- @indent + "#{name} = #{value}"
+ @indent_str + "#{name} = #{value}"
end
def eval_expr(expr)
if expr.include?('+')
(@items[expr[/\w+/]].to_i + 1).to_s
else
@@ -182,11 +193,11 @@
end
end
def eval_items
@items = {}
get_items.each do |i|
- node = Node.new(i)
+ node = Node.new(:node => i)
@items[node.get_attr('name')] = node.get_attr('enumvalueex') ? eval_expr(node.get_attr('enumvalueex')) : node.get_attr('enumvalue')
end
@items
end
def get_items
@@ -195,54 +206,75 @@
end
class Structure < Node
def self.camelcase(name)
name.gsub(/^\w|\_\w/).each {|c| c.upcase }.delete('_')
end
- def initialize(node, indent = 0)
+ def initialize(params = { })
super
@name = self.class.camelcase(@symname)
end
def to_s
fields_str = fields.inject("") do |str, f|
- str << @indent + ' ' * 9 << f.join(', ') << ",\n"
+ str << @indent_str + ' ' * 9 << f.join(', ') << ",\n"
end
- code = klass_string + @indent + " layout(\n" + fields_str.chomp.chomp(',') + "\n" + @indent + " )\n" + @indent + "end\n"
+ code = klass_string + @indent_str + " layout(\n" + fields_str.chomp.chomp(',') + "\n" + @indent_str + " )\n" + @indent_str + "end\n"
end
private
def klass_string
- @indent + "class #{@name} < FFI::Struct\n"
+ @indent_str + "class #{@name} < FFI::Struct\n"
end
def fields
(@node / 'cdecl').inject([]) do |array, field|
- array << [":#{Node.new(field).symname}", "#{Type.new(field)}"]
+ array << [":#{Node.new(:node => field).symname}", "#{Type.new(:node => field)}"]
end
end
end
class Union < Structure
private
def klass_string
- @indent + "class #{@name} < FFI::Union\n"
+ @indent_str + "class #{@name} < FFI::Union\n"
end
end
- class Function < Node
+ class Function < Type
class Argument < Type
- def initialize(node, indent = 0)
- super
- @decl = @type
+ def to_s
+ get_attr('type') == 'void' ? nil : super
end
end
+ def initialize(params = { })
+ super
+ @type = get_attr('type')
+ end
def to_s
params = get_params(@node).inject([]) do |array, node|
- array << Argument.new(node).to_s
+ array << Argument.new(:node => node).to_s
end.collect { |p| "#{p}" }
- @indent + "attach_function :#{@symname}, [ #{params.join(', ')} ], #{Type.new(@node).to_s}"
+ @indent_str + "attach_function :#{@symname}, [ #{params.join(', ')} ], #{get_rvalue}"
end
private
def get_params(node)
parmlist = node / './attributelist/parmlist/parm'
end
+ def get_rvalue
+ Type.new(:node => @node, :statement => @statement.scan(/^f\(.*\)\.(.+)/).flatten[0]).to_s
+ end
end
+ class Callback < Type
+ def to_s
+ params = get_params.inject([]) do |array, type|
+ array << (type == 'void' ? '' : Type.new(:statement => type).to_s)
+ end
+ @indent_str + "callback(:#{@symname}, [ #{params.join(', ')} ], #{get_rtype})"
+ end
+ private
+ def get_params
+ @statement.scan(/p.f\((.*)\)/).flatten[0].split(',')
+ end
+ def get_rtype
+ Type.new(:statement => @statement.scan(/\)\.(\w+)/).flatten[0]).to_s
+ end
+ end
class Parser
@indent = 2
class << self
def get_verbatim(node)
node.xpath("./attributelist/attribute[@name='code']").first['value']
@@ -267,31 +299,39 @@
node.name == 'class' and (node / "./attributelist/attribute[@name='kind']").first['value'] == 'union'
end
def is_typedef?(node)
node.name == 'cdecl' and (node / "./attributelist/attribute[@name='kind']").first['value'] == 'typedef'
end
+ def is_callback?(node)
+ (node / "./attributelist/attribute[@name='decl']").first['value'] =~ /^p\.f\(/
+ end
def generate(node)
result = ""
node.traverse do |node|
if is_constant?(node)
- result << Constant.new(node, @indent).to_s << "\n"
+ result << Constant.new(:node => node, :indent => @indent).to_s << "\n"
elsif is_typedef?(node)
- typedef = Typedef.new(node)
- Generator.add_type(typedef.symname, typedef.type)
+ typedef = Typedef.new(:node => node)
+ Generator.add_type(typedef.symname, typedef.statement)
+ if is_callback?(node)
+ cb = Callback.new(:node => node, :indent => @indent).to_s << "\n"
+ Generator.add_type(typedef.symname, ":#{typedef.symname}")
+ result << cb.to_s
+ end
elsif is_enum?(node)
- e = Enum.new(node, @indent)
+ e = Enum.new(:node => node, :indent => @indent)
Generator.add_type(e.symname, Generator::TYPES['int'])
result << e.to_s << "\n"
elsif is_struct?(node)
- s = Structure.new(node, @indent)
+ s = Structure.new(:node => node, :indent => @indent)
Generator.add_type(s.symname, "struct #{s.symname}")
result << s.to_s
elsif is_union?(node)
- s = Union.new(node, @indent)
+ s = Union.new(:node => node, :indent => @indent)
Generator.add_type(s.symname, "union #{s.symname}")
result << s.to_s
elsif is_function_decl?(node)
- result << Function.new(node, @indent).to_s << "\n"
+ result << Function.new(:node => node, :indent => @indent).to_s << "\n"
elsif node.name == 'insert' and not is_insert_runtime?(node)
result << get_verbatim(node)
end
end
result