# pparse.rb
#   $Id: pparse.rb,v 1.5 2000/11/12 00:37:33 keiko Exp $


require "prototype"

class PrototypeParser
  def parse(&block)
    @cpp.each do |line|
      case line
      when /^\s*$/, /^#/
	next
      when /(\S+)\s+FUNCTION\s+(\w+)\s*\(/
	func($1, $2)
      when /SUBROUTINE\s+(\w+)\s*\(/
	subr($1)
      when /^\s*\)\s*$/
	close(&block)
      else
	line.gsub!(/(^\s+)|(\s+$)/, "")
	line.chomp!
	var(line)
      end
    end
  end

  private

  def delete_comment(src) # [ruby-list:32030] [ruby-list:32049]
    src.gsub(%r@ ([\'\"])(\\.|.)*?\1
               | (/\*.*?\*/)      
               | (//)[^\n]*$      
               @mx) {
      if $3
	s = $3.delete("^\n")
	s = ' ' if s.empty?
	s
      elsif $4
	''
      else
	$&
      end
    }
  end

  def initialize(file)
    input = if file.is_a? IO
	      file
	    else
	      File::open(file)
	    end

    @cpp = delete_comment(input.read)
  end

  def func(type, name)
    @type, @name = type, name.downcase
    @var = []
    @rtn = []

    if (/(\d+)|\*\((.*)\)/ =~ type)
      charlen = ($1||$2).downcase   # may be charcter length
      @type = type.scan(/\w+/)[0]
    else
      charlen = nil
    end
    @rtn.push Variable("rtn_val", @type, "o", false, charlen)

    p [:func, type, name] if $DEBUG
  end

  def subr(name)
    @type, @name = nil, name.downcase
    @var = []
    @rtn = []

    p [:subr, name] if $DEBUG
  end

  def var(decl)
    name, *tmp = decl.split.reverse
    tmp.reverse!
    attr = (tmp[-1] =~ /^[a-z]+$/ ? tmp.pop : "i")
    ary =  (tmp[-1] =~ /^\((.*)\)/ ? (tmp.pop; $1.downcase) : false)
    vtype = tmp.pop
    if (/(\d+)|\*\((.*)\)/ =~ vtype)
      charlen = ($1||$2).downcase   # may be charcter length
      vtype = vtype.scan(/\w+/)[0]
    else
      charlen = nil
    end
    attr0 = attr.scan(/./).sort.uniq.join
    r = (attr =~ /i/ ? (attr.delete!("i"); true) : false)
    w = (attr =~ /o/ ? (attr.delete!("o"); true) : false)
    t = (attr =~ /t/ ? (attr.delete!("t"); true) : false)
    raise "unexpected decl `#{decl.inspect}'" unless tmp.empty?
    raise "unexpected read/write attr `#{decl.inspect}'" unless attr.empty?
    v = Variable(name.downcase, vtype, attr0, ary, charlen)
    @var.push v
    @rtn.push v if (!@type && w)

    p decl if $DEBUG
    p v if $DEBUG
    
  end

  def close
    val = Prototype.new(@type, @name, @var, @rtn)
    p val if $DEBUG
    @var = nil
    @rtn = nil
    # puts if $DEBUG
    if block_given?
      yield(val)
    else
      val
    end
  end

  def method_missing(m, *a, &b)
    p [m, a.join(" ")]
  end

#  Var = Struct.new("Var", 
#		   "vtype", "ary?", "attr", "r?", "w?", "t?", "name",
#		   "ignore?").freeze
end
