EmptyType = :t0_empty # the type of an empty list, it can be anything
IntType = :t1_int
CharType = :t2_char
XSpec = :t3_s_x
ACharSpec = :t4_s_achar
AIntSpec = :t4_s_aint
ASpec = :t4_s_a
BSpec = :t5_s_b

def concrete_rank(spec)
  !(spec == ASpec || spec == BSpec || spec == ACharSpec || spec == AIntSpec)
end

def concrete_type(spec)
  !(spec == ASpec || spec == BSpec || spec == XSpec)
end

def spec_rank(part)
  t = spec_type(part)
  r = part.scan(/\[|\{/).size
  concrete_rank(t) ? r : ~r
end

def spec_type(part)
  case part.tr('[]{}','')
  when "x"
    XSpec
  when "a"
    ASpec
  when "achar"
    ACharSpec
  when "aint"
    AIntSpec
  when "b"
    BSpec
  when "int"
    IntType
  when "char"
    CharType
  when "", "empty"
    EmptyType
  else raise "unknown arg type %p" % part
  end
end

def is_special_rep(part)
  part[0] == '{'
end

def type_to_str(sym)
  sym.to_s.sub(/^.*_/,'')
end

def type_and_rank_to_str(type,rank)
  r = "["*rank + type_to_str(type) + "]"*rank
  r == "[empty]" ? "[]" : r
end

# determine how well arg types match type specs
def spec_matches(specs, args)
  specs = specs.map{|s|
    if s == XSpec
      s
    else
      target_type_var_to_type(specs, args, s)
    end
  }
  specs.zip(args).map{|s, a|
    case s
      when XSpec
        a == EmptyType ? :m1_defaults : :m0_matches
      when IntType
        case a
          when EmptyType; :m1_defaults
          when IntType; :m0_matches
          when CharType; :m4_no_match
        end
      when CharType
        case a
        when EmptyType; :m2_defaults_char
        when IntType; :m3_coerces
        when CharType; :m0_matches
        end
      when EmptyType
        :m0_matches
      else; raise s.to_s
    end
  }
end

def target_type_var_to_type(specs, args, target)
  return IntType if target == AIntSpec
  return CharType if target == ACharSpec
  return target if concrete_type(target)
  args.filter.with_index{|a,i| specs[i] == target }.max || EmptyType
end

def spec_to_ret(way, args)
  return IntType if OpsUsingZero.include?(way.name) && way.mod == 0 && args[0] == EmptyType

  t = target_type_var_to_type(way.arg_types, args,  way.ret_type)
  way.ret_type == XSpec ? [t, IntType].max : t
end

def coerce?(specs,args)
  specs.zip(args).map{|s,a|
    a == IntType && target_type_var_to_type(specs, args, s) == CharType
  }
end
