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