ruby


ヘッダ解析

#! /usr/bin/ruby

$QUICK_RUBY_VERSION = RUBY_VERSION.split('.').inject(0){|vv,v| vv * 100 + v.to_i }

class HeaderParser

  def initialize
    @treat_as_void = (['void']).uniq
    @local_as_void = @treat_as_void
    @c_strippables = ['(?:__attribute__\s*\(+.*?\)+)']
    @treat_externs = :exclude
    @module_name = 'hoge'
  end

  def parse(source)


    print "###  1  ###\n"
    print source

    # let's clean up the encoding in case they've done anything weird with the characters we might find
    source = source.force_encoding("ISO-8859-1").encode("utf-8", :replace => nil) if ($QUICK_RUBY_VERSION > 10900)

    print "###  2  ###\n"
    print source

    # void must be void for cmock _ExpectAndReturn calls to process properly, not some weird typedef which equates to void
    # to a certain extent, this action assumes we're chewing on pre-processed header files, otherwise we'll most likely just get stuff from @treat_as_void
    @local_as_void = @treat_as_void
    void_types = source.scan(/typedef\s+(?:\(\s*)?void(?:\s*\))?\s+([\w\d]+)\s*;/)
    if void_types
      @local_as_void += void_types.flatten.uniq.compact
    end

    # smush multiline macros into single line (checking for continuation character at end of line '\')
    source.gsub!(/\s*\\\s*/m, ' ')

    print "###  3  ###\n"
    print source

    #remove comments (block and line, in three steps to ensure correct precedence)
    source.gsub!(/\/\/(?:.+\/\*|\*(?:$|[^\/])).*$/, '')  # remove line comments that comment out the start of blocks

    print "###  4  ###\n"
    print source

    source.gsub!(/\/\*.*?\*\//m, '')                     # remove block comments

    print "###  5  ###\n"
    print source

    source.gsub!(/\/\/.*$/, '')                          # remove line comments (all that remain)

    print "###  6  ###\n"
    print source

    # remove assembler pragma sections
    source.gsub!(/^\s*#\s*pragma\s+asm\s+.*?#\s*pragma\s+endasm/m, '')

    print "###  7  ###\n"
    print source

    # remove gcc's __attribute__ tags
    source.gsub!(/__attribute(?:__)?\s*\(\(+.*\)\)+/, '')

    print "###  8  ###\n"
    print source

    # remove preprocessor statements and extern "C"
    source.gsub!(/^\s*#.*/, '')

    print "###  9  ###\n"
    print source

    source.gsub!(/extern\s+\"C\"\s+\{/, '')

    print "###  10  ###\n"
    print source

    # enums, unions, structs, and typedefs can all contain things (e.g. function pointers) that parse like function prototypes, so yank them
    # forward declared structs are removed before struct definitions so they don't mess up real thing later. we leave structs keywords in function prototypes
    source.gsub!(/^[\w\s]*struct[^;\{\}\(\)]+;/m, '')                                      # remove forward declared structs

    print "###  11  ###\n"
    print source

=begin
    source.gsub!(/^[\w\s]*(enum|union|struct|typepdef)[\w\s]*\{[^\}]+\}[\w\s\*\,]*;/m, '') # remove struct, union, and enum definitions and typedefs with braces

    print "###  12  ###\n"
    print source

    source.gsub!(/(\W)(?:register|auto|static|restrict)(\W)/, '\1\2')                      # remove problem keywords

    print "###  13  ###\n"
    print source

    source.gsub!(/\s*=\s*['"a-zA-Z0-9_\.]+\s*/, '')                                        # remove default value statements from argument lists

    print "###  14  ###\n"
    print source

    source.gsub!(/^(?:[\w\s]*\W)?typedef\W[^;]*/m, '')                                     # remove typedef statements

    print "###  15  ###\n"
    print source

    source.gsub!(/\)(\w)/, ') \1')                                                         # add space between parenthese and alphanumeric

    print "###  16  ###\n"
    print source

    source.gsub!(/(^|\W+)(?:#{@c_strippables.join('|')})(?=$|\W+)/,'\1') unless @c_strippables.empty? # remove known attributes slated to be stripped

    print "###  17  ###\n"
    print source

    #scan for functions which return function pointers, because they are a pain
    source.gsub!(/([\w\s\*]+)\(*\(\s*\*([\w\s\*]+)\s*\(([\w\s\*,]*)\)\)\s*\(([\w\s\*,]*)\)\)*/) do |m|
      functype = "cmock_#{@module_name}_func_ptr#{@typedefs.size + 1}"
      @typedefs << "typedef #{$1.strip}(*#{functype})(#{$4});"
      "#{functype} #{$2.strip}(#{$3});"
    end

    print "###  18  ###\n"
    print source

=end

    #drop extra white space to make the rest go faster
    source.gsub!(/^\s+/, '')          # remove extra white space from beginning of line

    print "###  19  ###\n"
    print source

    source.gsub!(/\s+$/, '')          # remove extra white space from end of line

    print "###  20  ###\n"
    print source

    source.gsub!(/\s*\(\s*/, '(')     # remove extra white space from before left parens

    print "###  21  ###\n"
    print source

    source.gsub!(/\s*\)\s*/, ')')     # remove extra white space from before right parens

    print "###  22  ###\n"
    print source

    source.gsub!(/\s+/, ' ')          # remove remaining extra white space

    print "###  23  ###\n"
    print source

    source.gsub!(/\s+;/, ';')          # [Add] remove remaining extra white space

=begin

    #split lines on semicolons and remove things that are obviously not what we are looking for
    src_lines = source.split(/\s*;\s*/)
    src_lines.delete_if {|line| line.strip.length == 0}                            # remove blank lines
    src_lines.delete_if {|line| !(line =~ /[\w\s\*]+\(+\s*\*[\*\s]*[\w\s]+(?:\[[\w\s]*\]\s*)+\)+\s*\((?:[\w\s\*]*,?)*\s*\)/).nil?}     #remove function pointer arrays
    if (@treat_externs == :include)
      src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:inline)\s+/).nil?}        # remove inline functions
    else
      src_lines.delete_if {|line| !(line =~ /(?:^|\s+)(?:extern|inline)\s+/).nil?} # remove inline and extern functions
    end
    src_lines.delete_if {|line| !(line =~ /\{/).nil? }  # remove lines with opening braces { because this isn't a declaration, it's a definition!
    src_lines.map!{|line| line.gsub(/.*\}/,'')} #remove braces left at the beginning of lines
    src_lines.delete_if {|line| line.empty? } #drop empty lines

=end

  end

  def getMemberInfo(member)

    info = {}

    info["name"] = member.split(' ')[-1].gsub('*', '')

    type = ""

    member.split(' ')[0..-2].map do |item|
      type += " " if type.length != 0
      type += item
    end

    type += " *" if member.index('*') and !type.index('*')

    info["type"] = type

    return info

  end

  def getMemberName(member)

    member.split(' ')[-1].gsub('*', '').gsub(/\[.*\]/, '')

  end

  def isArrayOrPointerMember?(member)

    return true if member.index('*') or member.index('[')

  end

  def getStructTree(parseSrc, name)

    src_lines = parseSrc.split(/\s*typedef struct\s*/)
    src_lines.delete_if {|line| line.strip.length == 0}

    list = []

    src_lines.each do |line|

      structDef = {"name"=>"", "members"=>[]}

      structElements = line.split(/[{};]/, 0)
      structElements.delete_if {|line| line == " "}

      structElements.each_with_index do |ele, i|

        ele.gsub!(/^\s+/, '')       # remove extra white space from beginning of line

        structDef["name"] = ele if i == (structElements.length-1)

        if i != 0 and i != (structElements.length-1) then
          structDef["members"].push(ele)
        end

      end

      list.push(structDef)

    end

    p list


    list.each do |item|

      return item if item["name"] == name

    end

  end

end



parser = HeaderParser.new()

src = parser.parse(File.read(ARGV[0]))

print "\n\n###  PARSE  ###\n"

print src

print "\n###  PARSE END  ###\n"

tree = parser.getStructTree(src, ARGV[1])

p tree

p parser.getMemberName(tree["members"][1])


EXCLUDE_MEMBERS = ["id1", "id2"]
TEST_CMD_VAR_NAME = "test_cmd"
FLAG_BASE_NAME = "_enable"


autoCode = "int " + tree["name"] + "_test(void)\n" + "{\n"

autoCode += "    " + tree["name"] + " " + TEST_CMD_VAR_NAME + ";\n\n"

tree["members"].each do |item|
  memName = parser.getMemberName(item)
  next if EXCLUDE_MEMBERS.include?(memName)
  next if memName.index(FLAG_BASE_NAME)
  autoCode += "    " + item + ";\n"
end


autoCode += "\n"
autoCode += "    int idx;\n\n"
autoCode += "    idx = 0;\n\n"


IDX_COMMENT_OFFSET = 50
idx = 0
tree["members"].each do |item|

  memName = parser.getMemberName(item)

  next if EXCLUDE_MEMBERS.include?(memName)
  next if memName.index(FLAG_BASE_NAME)

  ptr = ""
  if parser.isArrayOrPointerMember?(item) then
    ptr = memName
  else
    ptr = "&" + memName
  end

  memsetStr = "    " + "memset(" + ptr + ", idx++, " + "sizeof(" + memName + "));"
  if memsetStr.length < IDX_COMMENT_OFFSET
    memsetStr += " " * (IDX_COMMENT_OFFSET - memsetStr.length)
  else
    memsetStr += " "
  end

  memsetStr += "/*  " + idx.to_s + "  */\n"

  autoCode +=  memsetStr

  idx += 1
end


autoCode += "\n"

autoCode += "    " + TEST_CMD_VAR_NAME + ".id1 = 1;\n"
autoCode += "    " + TEST_CMD_VAR_NAME + ".id2 = 4;\n"

autoCode += "\n"

tree["members"].each do |item|

  memName = parser.getMemberName(item)
  autoCode += "    " + TEST_CMD_VAR_NAME + "." + memName + " = 1;\n" if memName.index(FLAG_BASE_NAME)

end

autoCode += "\n"

tree["members"].each do |item|

  subStr = ""
  memName = parser.getMemberName(item)

  next if EXCLUDE_MEMBERS.include?(memName)
  next if memName.index(FLAG_BASE_NAME)

  if parser.isArrayOrPointerMember?(item) then
    subStr = "    memcpy(" + TEST_CMD_VAR_NAME + "." + memName + ", " + memName + ", sizeof(" + memName + "));\n"
  else
    subStr = "    test_cmd." + memName + " = " + memName + ";\n"
  end

  autoCode +=  subStr
end


autoCode += "\n}"


print "\n\n###  PROGRAM START  ###\n"

print autoCode

print "\n\n###  PROGRAM END  ###\n"

最新の20件

2021-03-31 2020-06-09 2020-05-30 2019-12-02 2018-10-18 2018-07-05 2018-01-11 2015-12-07 2015-08-03 2015-07-20
  • ruby
2012-07-19 2012-06-30 2012-06-23 2012-01-25 2011-11-09 2011-11-08 2011-10-02

今日の3件

  • counter: 272
  • today: 1
  • yesterday: 0
  • online: 1