Thursday, October 28, 2010

Code Generation in Ruby

While doing the code generation exercise (chapter 3, code generators) in the book The Pragmatic Programmer, I realized it quickly that C# isn't the best language to do code generation. The main block was that it is too verbose to do regular expression on strings. I had to use Regex.IsMatch and then Regex.Split to get the the tokens. Thats when I looked into ruby.

Regular expressions is a built in feature of Ruby, put between two forward slashes (/). You can use =~ operator for a regex match which sets the special variables $~.  $& holds the text matched by the whole regular expression. $1, $2, etc. hold the text matched by the first, second, and following capturing groups. Some good info here.

class CSharpCode
 def BlankLine
  print "\n"
 end
 
 def Comment comment 
  print "// #{comment}\n"
 end 
 
 def StartMsg name
  print "public struct #{name} {\n"
 end
 
 def EndMsg
  print "}\n"
 end
 
 def SimpleType name, type
  print "\t#{type} #{name};\n"
 end
 
 def ComplexType name, type, size
  if(type == "char[")
   type = "string"
   print "\t#{type} #{name};\n"
  end
 end

end

if __FILE__ == $0
 unless ARGV[0]
  print "cml usage: cml Input.txt\n"
  exit
 end

 if(File.exist?(ARGV[0]))
  CG = CSharpCode.new
  File.open(ARGV[0]).each_line { |line|
   line.chomp!;
   
   if(line =~ /^\s*S/)
    CG.BlankLine
   elsif line =~ /^\#(.*)/
    CG.Comment $1
   elsif line =~ /^M\s*(.+)/
    CG.StartMsg $1
   elsif line =~ /^E/
    CG.EndMsg
   elsif line =~ /F\s*(\w+)\s*(\w+\[)(\d+)\]/
    CG.ComplexType $1, $2, $3
   elsif line =~ /^F\s+(\w+)\s*(\w+)/
    CG.SimpleType $1, $2
   else
    print "Invalid line"
   end
  }
 end
end

Input File
 # Add a product
 # to the 'on-order' list
 M AddProduct
 F id   int
 F name   char[30]
 F order_code int
 E
Output
//  Add a product
//  to the 'on-order' list
public struct AddProduct {
 int id;
 string name;
 int order_code;
}

No comments: