#!/usr/bin/env ruby
# coding: utf-8
#
# create built-in method table include file.
#
#  Copyright (C) 2015- Kyushu Institute of Technology.
#  Copyright (C) 2015- Shimane IT Open-Innovation Center.
#
#  This file is distributed under BSD 3-Clause License.
#
# (usage)
# ruby make_method_table.rb TARGET_CLASS_FILE.c
#

require_relative "common_sub"


##
# write header file
#
def write_file( param )
  flag_error = false

  begin
    if param[:file]
      file = File.open( param[:file], "w" )
    elsif param[:append]
      file = File.open( param[:append], "a" )
    else
      file = $stdout
    end

  rescue Errno::ENOENT
    puts "File can't open. #{param[:file]}"
    return false
  end

  if param[:file] || file == $stdout
    file.puts "/* Auto generated by make_method_table.rb */"
    file.puts "#include \"_autogen_builtin_symbol.h\""
  else
    file.puts
  end

  param[:classes].each {|cls|
    if cls[:class]
      type = :class
    elsif cls[:module]
      type = :module
    else
      raise
    end
    cls_name = cls[type]

    file.puts "\n/*===== #{cls_name} #{type} =====*/"
    if cls[:methods]
      cls[:methods].sort_by! {|m| m[:name] }    # sort by method name

      # write method symbol table.
      file.puts "static const mrbc_sym method_symbols_#{cls_name}[] = {"
      cls[:methods].each {|m|
        file.puts m[:if_exp].join("\n")  if m[:if_exp]
        file.puts "  MRBC_SYM(#{rename_for_symbol(m[:name])}),"
        m[:if_exp].size.times { file.puts "#endif" }  if m[:if_exp]
      }
      file.puts "};\n\n"

      # write method function table.
      file.puts "static const mrbc_func_t method_functions_#{cls_name}[] = {"
      cls[:methods].each {|m|
        file.puts m[:if_exp].join("\n")  if m[:if_exp]
        file.puts "  #{m[:func]},"
        m[:if_exp].size.times { file.puts "#endif" }  if m[:if_exp]
      }
      file.puts "};\n\n"
    end

    # write class struct.
    struct_name = cls[:methods] ? "RBuiltinClass" : "RBuiltinNoMethodClass"
    file.puts "struct #{struct_name} mrbc_class_#{cls_name} = {"
    file.puts "  .sym_id = MRBC_SYM(#{cls_name}),"
    file.puts "  .flag_builtin = 1,"
    if type == :module
      file.puts "  .flag_module = 1,"
    end
    n = cls[:methods] ? "sizeof(method_symbols_#{cls_name}) / sizeof(mrbc_sym)" : 0
    file.puts "  .num_builtin_method = #{n},"
    sp = case cls[:super]
         when nil
           raise "SUPER isn't specified."
         when /^[A-Z][A-Za-z0-9]+$/
           "MRBC_CLASS(#{cls[:super]})"
         else
           cls[:super]
         end
    file.puts "  .super = #{sp},"
    file.puts "#if defined(MRBC_DEBUG)"
    file.puts "  .name = \"#{cls_name}\","
    file.puts "#endif"
    if cls[:methods]
      file.puts "  .method_symbols = method_symbols_#{cls_name},"
      file.puts "  .method_functions = method_functions_#{cls_name},"
    end
    file.puts "};"
  }

  file.close  if file != $stdout
end


##
# main
#
if !ARGV[0]
  puts <<EOL
(usage)
ruby make_method_table.rb TARGET_CLASS_FILE.c
EOL
  exit 1
end
filename = ARGV[0]

begin
  file = File.open( filename )
rescue Errno::ENOENT
  puts "File not found. '#{filename}'"
  exit 1
end

src = get_method_table_source( file )
if !src
  puts %!Magic comment "MRBC_AUTOGEN_METHOD_TABLE" is not found. '#{filename}'!
  exit 1
end

while src
  param = parse_source_string( src )
  exit 1 if !param
  write_file( param )

  src = get_method_table_source( file )
end
