// The value-method registry, and what a member access records.
//
// Ported from wax/src/lib-wax/members.ml -- the parts with a consumer in this
// port. What is here is what the CHECKER uses: the descriptor it records at
// each `recv.member` access, the classification that decides whether a receiver
// has value methods at all, and the type a SIMD intrinsic's operand stands for.
//
// What is NOT here is the other half of that file: the completion candidate
// LISTS (`fn(i32) -> i32` renderings of every method a memory, table, array,
// struct or v128 receiver offers). They exist for `recv.` completion in
// an editor, which this port does not have -- the reference's own `.mli` is
// explicit that the typer's method dispatch is match-based and NOT enumerable
// from these tables, so nothing in the checker consults them. They are
// transcription with no consumer and no test, and they cost more than they are
// worth until there is an editor to serve. See task 9 of implementation-plan.md.
//
// The two curated registries below are the exception: they are small, they name
// the methods the typer must accept, and upstream keeps them honest with a test
// that type-checks each one. That test is the reason to have them ported before
// the typer rather than after -- they are the checklist it is written against.

///|
/// What a value method's result type is, relative to its receiver.
pub(all) enum MethodResult {
  /// The receiver's own type.
  Same
  /// The equal-width opposite numeric family -- i32 <-> f32, i64 <-> f64 --
  /// as `from_bits` and `to_bits` reinterpret.
  Reinterpret
} derive(Eq, Debug)

///|
/// One value method: `x.clz()`, `x.rotl(y)`.
pub(all) struct ValueMethod {
  name : String
  /// Takes a second operand, of the receiver's type.
  binary : Bool
  result : MethodResult
} derive(Eq, Debug)

///|
fn meth(
  name : String,
  binary? : Bool = false,
  result? : MethodResult = Same,
) -> ValueMethod {
  { name, binary, result }
}

///|
/// The value methods an integer receiver offers.
pub let integer_methods : Array[ValueMethod] = [
  meth("clz"),
  meth("ctz"),
  meth("popcnt"),
  meth("extend8_s"),
  meth("extend16_s"),
  meth("from_bits", result=Reinterpret),
  meth("rotl", binary=true),
  meth("rotr", binary=true),
]

///|
/// The value methods a float receiver offers.
pub let float_methods : Array[ValueMethod] = [
  meth("abs"),
  meth("ceil"),
  meth("floor"),
  meth("trunc"),
  meth("nearest"),
  meth("sqrt"),
  meth("to_bits", result=Reinterpret),
  meth("min", binary=true),
  meth("max", binary=true),
  meth("copysign", binary=true),
]

///|
/// What a member access's receiver is, as the checker records it.
///
/// A lightweight descriptor rather than the candidate list itself: for a v128
/// or a memory receiver that list is large, and it is only ever wanted for the
/// one access under a cursor.
pub(all) enum MemberReceiver {
  /// A value receiver: its integer, float or v128 methods.
  RNumeric(@infer.InferredType)
  /// A struct, by its fields.
  RStruct(StructFields)
  /// An array, by its element type: `length`, `fill`, `copy`, `init`.
  RArray(FieldType)
  /// A memory, by its address type.
  RMemory(@wasm_types.AddressType)
  /// A table, by its address and element types.
  RTable(@wasm_types.AddressType, @wasm_types.RefType[@ast.Ident])
}

///|
/// A struct or array field's type, as the AST spells it.
type FieldType = @wasm_types.MutType[@wasm_types.StorageType[@ast.Ident]]

///|
/// A struct's fields, named and typed, as a type definition declares them.
type StructFields = Array[
  @basic.Annotated[(@ast.Ident, FieldType), @basic.Location],
]

///|
/// The value type a SIMD intrinsic's operand or result stands for.
pub fn simd_valtype(t : SimdTy) -> @infer.InferredValType {
  match t {
    TV128 =>
      (
        { typ: V128, internal: V128, anon_comptype: None } :
        @infer.InferredValType)
    TI32 => @infer.i32_valtype
    TI64 => @infer.i64_valtype
    TF32 => @infer.f32_valtype
    TF64 => @infer.f64_valtype
  }
}

///|
/// The types a SIMD intrinsic's operands and results take.
///
/// The reference reads this from `Wax_wasm.Simd`, the 856-line registry of
/// every vector op, which is NOT ported: it is an unlisted prerequisite of the
/// type checker (task 12), which dispatches `v.add_i32x4(w)` through it. This
/// enum is the part `simd_valtype` needs, so the checker-facing half of this
/// file does not wait on the other 856 lines.
pub(all) enum SimdTy {
  TV128
  TI32
  TI64
  TF32
  TF64
} derive(Eq, Debug)

///|
/// Does a value receiver of this type have value methods?
///
/// The cheap classification the recorder uses to decide whether to record at
/// all, without building a candidate list. A packed `i8`/`i16` has none -- it
/// must be cast first -- and neither has a non-numeric type.
pub fn numeric_receiver_kind(t : @infer.InferredType) -> MemberReceiver? {
  match t {
    Valtype({ typ: I32 | I64 | F32 | F64 | V128, .. })
    | Int
    | Number
    | LargeInt
    | Float => Some(RNumeric(t))
    _ => None
  }
}