// Module-level syntax: attributes, imports, segments, and the fields a module
// is a list of.

///|
/// An attribute, `#[name]` or `#[name = value]`.
pub(all) struct Attribute {
  attr_name : String
  /// `#[export = "f"]` carries one; `#[start]` does not.
  attr_value : Instr[Location]?
  /// A conditional-compilation guard, `#[export = "f", if not(portable)]`,
  /// making just this attribute conditional -- independently of whether the
  /// field it decorates is itself reachable. Only `export` may be guarded.
  /// Located at its `if` keyword.
  attr_guard : Annotated[Cond, Location]?
  /// The span of the whole `#[...]`, so a diagnostic about the attribute names
  /// the attribute rather than falling back to the value (which a valueless
  /// attribute lacks) or to the whole field.
  attr_span : Location
} derive(Eq, Debug)

///|
pub type Attributes = Array[Attribute]

///|
/// What an `import "module" { ... }` entry brings in.
///
/// Imports have no body, so these carry only type-level information.
pub(all) enum ImportKind {
  /// `exact` marks an exact function import, `fn f: !t`.
  Func(typ~ : Ident?, sign~ : FuncType?, exact~ : Bool)
  Global(mut_~ : Bool, typ~ : ValType)
  Tag(typ~ : Ident?, sign~ : FuncType?)
  Memory(
    address_type~ : AddressType,
    limits~ : (UInt64, UInt64?)?,
    page_size_log2~ : Int?,
    shared~ : Bool
  )
  Table(
    address_type~ : AddressType,
    reftype~ : RefType,
    limits~ : (UInt64, UInt64?)?
  )
} derive(Eq, Debug)

///|
/// A single imported entity.
///
/// It is imported under `id` unless a name-only `#[import = "name"]` attribute
/// overrides it. `attributes` also carries e.g. `#[export]` to re-export it.
pub(all) struct ImportDecl {
  id : Ident
  kind : ImportKind
  attributes : Attributes
} derive(Eq, Debug)

///|
/// One element of a data segment's contents.
///
/// Holds no instructions: every value is a literal. In a run the element type
/// is stated once and the values are RAW literal strings, packed
/// little-endian -- raw for the same round-tripping reason as numeric literals.
pub(all) enum DataElem {
  /// A string literal's raw bytes.
  Str(Bytes)
  /// A scalar numeric run, `[f32: 1.5, nan, ...]`.
  Run(StorageType, Array[Annotated[String, Location]])
  /// A v128 run, `[v128: i32x4(1,2,3,4), ...]`.
  V128Run(Array[Annotated[V128, Location]])
} derive(Eq, Debug)

///|
/// A data segment attached to a memory definition.
pub(all) struct MemData[Info] {
  data_name : Ident?
  offset : Instr[Info]
  init : Array[DataElem]
} derive(Eq, Debug)

///|
/// Whether a data segment is copied in at instantiation or only on demand.
pub(all) enum DataMode[Info] {
  Passive
  Active(Ident, Instr[Info])
} derive(Eq, Debug)

///|
pub(all) enum ElemMode[Info] {
  EPassive
  EActive(Ident, Instr[Info])
} derive(Eq, Debug)

///|
/// A top-level definition.
pub(all) enum ModuleField[Info] {
  Type(RecType)
  Func(
    name~ : Ident,
    typ~ : Ident?,
    sign~ : FuncType?,
    body~ : (Label?, Array[Instr[Info]]),
    attributes~ : Attributes
  )
  Global(
    name~ : Ident,
    mut_~ : Bool,
    typ~ : ValType?,
    def~ : Instr[Info],
    attributes~ : Attributes
  )
  Tag(name~ : Ident, typ~ : Ident?, sign~ : FuncType?, attributes~ : Attributes)
  Memory(
    name~ : Ident,
    address_type~ : AddressType,
    limits~ : (UInt64, UInt64?)?,
    page_size_log2~ : Int?,
    shared~ : Bool,
    data~ : Array[MemData[Info]],
    attributes~ : Attributes
  )
  Data(
    name~ : Ident?,
    mode~ : DataMode[Info],
    init~ : Array[DataElem],
    attributes~ : Attributes
  )
  Table(
    name~ : Ident,
    address_type~ : AddressType,
    reftype~ : RefType,
    limits~ : (UInt64, UInt64?)?,
    init~ : Instr[Info]?,
    attributes~ : Attributes
  )
  Elem(
    name~ : Ident,
    reftype~ : RefType,
    mode~ : ElemMode[Info],
    init~ : Array[Instr[Info]],
    attributes~ : Attributes
  )
  /// A single import, `import "module" fn f();`.
  Import(
    module_~ : Annotated[Bytes, Location],
    decl~ : Annotated[ImportDecl, Location]
  )
  /// A grouped import block: several imports sharing one module name. Kept
  /// distinct from a run of single imports because the text form is
  /// authoritative for import layout, and the binary encoder reproduces it.
  ImportGroup(
    module_~ : Annotated[Bytes, Location],
    decls~ : Array[Annotated[ImportDecl, Location]]
  )
  /// A module-level inner attribute, `#![module = "name"]`. Attached to the
  /// whole module rather than to a field.
  ModuleAnnotation(Attributes)
  /// A conditional-compilation group at module level. Built by the same
  /// post-pass as the statement-level `IfAnnotation`; see parser.mbty.
  Conditional(
    cond~ : Cond,
    then_fields~ : Annotated[
      Array[Annotated[ModuleField[Info], Location]],
      Location,
    ],
    else_fields~ : Annotated[
      Array[Annotated[ModuleField[Info], Location]],
      Location,
    ]?
  )
} derive(Eq, Debug)

///|
/// A module: a list of spanned fields.
///
/// There is no wrapper node -- a Wax file IS its field list.
pub type Module[Info] = Array[Annotated[ModuleField[Info], Location]]

///|
/// The module type the parser produces, before any type checking.
pub type LocModule = Module[Location]