// A mirror of the Gleam compiler's untyped AST at v1.19.2
// (compiler-core/src/ast.rs, ast/untyped.rs, ast/constant.rs,
// parse/extra.rs, warning.rs). Field names are the compiler's; analysis-only
// fields are left out (see AGENTS.md > AST). Every list is a read-only view.

///|
/// What the compiler's `parse_module` returns: the module, the trivia
/// positions (`ModuleExtra`) and the parse-time warnings.
pub(all) struct Parsed {
  module_ : Module
  extra : ModuleExtra
  warnings : ArrayView[Warning]
} derive(Eq, Debug)

///|
/// `Module<(), Vec>` without `name` and
/// `documentation`, which the parser leaves empty.
pub(all) struct Module {
  definitions : ArrayView[TargetedDefinition]
} derive(Eq, Debug)

///|
pub(all) struct TargetedDefinition {
  definition : Definition
  target : Target?
} derive(Eq, Debug)

///|
/// `build::Target`.
pub(all) enum Target {
  Erlang
  JavaScript
} derive(Eq, Debug, Hash)

///|
pub fn Target::name(self : Target) -> String {
  match self {
    Erlang => "erlang"
    JavaScript => "javascript"
  }
}

///|
pub fn Target::of(name : String) -> Target? {
  match name {
    "erlang" => Some(Erlang)
    "javascript" => Some(JavaScript)
    _ => None
  }
}

///|
pub(all) enum Definition {
  Function(Function)
  TypeAlias(TypeAlias)
  CustomType(CustomType)
  Import(Import)
  ModuleConstant(ModuleConstant)
} derive(Eq, Debug)

///|
/// `(SrcSpan, EcoString)`: a name with its span.
pub(all) struct SpannedString {
  location : @span.SrcSpan
  name : String
} derive(Eq, Debug)

///|
/// `(u32, EcoString)`: a doc comment's start byte and text.
pub(all) struct Documentation {
  start : Int32
  text : String
} derive(Eq, Debug)

///|
/// `(EcoString, EcoString, SrcSpan)`: an `@external(target, module,
/// function)` attribute.
pub(all) struct External {
  module_ : String
  function : String
  location : @span.SrcSpan
} derive(Eq, Debug)

///|
pub(all) enum Publicity {
  Public
  Private
  Internal(attribute_location~ : @span.SrcSpan?)
} derive(Eq, Debug)

///|
/// `type_::Deprecation`.
pub(all) enum Deprecation {
  NotDeprecated
  Deprecated(message~ : String)
} derive(Eq, Debug)

///|
pub(all) struct Function {
  location : @span.SrcSpan
  body_start : Int32?
  end_position : Int32
  name : SpannedString?
  arguments : ArrayView[Arg]
  body : ArrayView[Statement]
  publicity : Publicity
  deprecation : Deprecation
  return_annotation : TypeAst?
  documentation : Documentation?
  external_erlang : External?
  external_javascript : External?
} derive(Eq, Debug)

///|
pub(all) struct Arg {
  names : ArgNames
  location : @span.SrcSpan
  annotation : TypeAst?
} derive(Eq, Debug)

///|
pub(all) enum ArgNames {
  Discard(name~ : String, location~ : @span.SrcSpan)
  LabelledDiscard(
    label~ : String,
    label_location~ : @span.SrcSpan,
    name~ : String,
    name_location~ : @span.SrcSpan
  )
  Named(name~ : String, location~ : @span.SrcSpan)
  NamedLabelled(
    label~ : String,
    label_location~ : @span.SrcSpan,
    name~ : String,
    name_location~ : @span.SrcSpan
  )
} derive(Eq, Debug)

///|
pub(all) struct Import {
  documentation : String?
  location : @span.SrcSpan
  module_location : @span.SrcSpan
  module_ : String
  as_name : ImportAlias?
  unqualified_values : ArrayView[UnqualifiedImport]
  unqualified_types : ArrayView[UnqualifiedImport]
} derive(Eq, Debug)

///|
/// `(AssignName, SrcSpan)`.
pub(all) struct ImportAlias {
  name : AssignName
  location : @span.SrcSpan
} derive(Eq, Debug)

///|
pub(all) enum AssignName {
  Variable(String)
  Discard(String)
} derive(Eq, Debug)

///|
pub(all) struct UnqualifiedImport {
  location : @span.SrcSpan
  name : String
  name_position : Int32
  as_name : String?
} derive(Eq, Debug)

///|
pub(all) struct ModuleConstant {
  documentation : Documentation?
  location : @span.SrcSpan
  publicity : Publicity
  name : String
  name_location : @span.SrcSpan
  annotation : TypeAst?
  value : Constant
  deprecation : Deprecation
} derive(Eq, Debug)

///|
pub(all) struct CustomType {
  location : @span.SrcSpan
  end_position : Int32
  name : String
  name_location : @span.SrcSpan
  publicity : Publicity
  constructors : ArrayView[RecordConstructor]
  documentation : Documentation?
  deprecation : Deprecation
  opaque_ : Bool
  parameters : ArrayView[SpannedString]
  external_erlang : External?
  external_javascript : External?
} derive(Eq, Debug)

///|
pub(all) struct RecordConstructor {
  location : @span.SrcSpan
  name_location : @span.SrcSpan
  name : String
  arguments : ArrayView[RecordConstructorArg]
  documentation : Documentation?
  deprecation : Deprecation
} derive(Eq, Debug)

///|
pub(all) struct RecordConstructorArg {
  label : SpannedString?
  ast : TypeAst
  location : @span.SrcSpan
  doc : Documentation?
} derive(Eq, Debug)

///|
pub(all) struct TypeAlias {
  location : @span.SrcSpan
  alias_ : String
  name_location : @span.SrcSpan
  parameters : ArrayView[SpannedString]
  type_ast : TypeAst
  publicity : Publicity
  documentation : Documentation?
  deprecation : Deprecation
} derive(Eq, Debug)

///|
pub(all) enum TypeAst {
  Constructor(TypeAstConstructor)
  Fn(TypeAstFn)
  Var(TypeAstVar)
  Tuple(TypeAstTuple)
  Hole(TypeAstHole)
} derive(Eq, Debug)

///|
pub(all) struct TypeAstConstructor {
  location : @span.SrcSpan
  name : TypeAstConstructorName
  arguments : ArrayView[TypeAst]
  start_parentheses : Int32?
} derive(Eq, Debug)

///|
pub(all) enum TypeAstConstructorName {
  /// `name` is `None` when the text stops after the dot (`module.`).
  Qualified(
    module_~ : String,
    module_location~ : @span.SrcSpan,
    dot_location~ : Int32,
    name~ : SpannedString?
  )
  Unqualified(name~ : String, location~ : @span.SrcSpan)
} derive(Eq, Debug)

///|
pub(all) struct TypeAstFn {
  location : @span.SrcSpan
  arguments : ArrayView[TypeAst]
  return_ : TypeAst
} derive(Eq, Debug)

///|
pub(all) struct TypeAstVar {
  location : @span.SrcSpan
  name : String
} derive(Eq, Debug)

///|
pub(all) struct TypeAstTuple {
  location : @span.SrcSpan
  elements : ArrayView[TypeAst]
} derive(Eq, Debug)

///|
pub(all) struct TypeAstHole {
  location : @span.SrcSpan
  name : String
} derive(Eq, Debug)

///|
pub(all) enum Statement {
  Expression(Expr)
  Assignment(Assignment)
  Use(Use)
  Assert(Assert)
} derive(Eq, Debug)

///|
pub(all) struct Assignment {
  location : @span.SrcSpan
  value : Expr
  pattern : Pattern
  kind : AssignmentKind
  annotation : TypeAst?
} derive(Eq, Debug)

///|
pub(all) enum AssignmentKind {
  Let
  Generated
  Assert(
    location~ : @span.SrcSpan,
    assert_keyword_start~ : Int32,
    message~ : Expr?
  )
} derive(Eq, Debug)

///|
pub(all) struct Use {
  call : Expr
  location : @span.SrcSpan
  right_hand_side_location : @span.SrcSpan
  assignments_location : @span.SrcSpan
  assignments : ArrayView[UseAssignment]
} derive(Eq, Debug)

///|
pub(all) struct UseAssignment {
  location : @span.SrcSpan
  pattern : Pattern
  annotation : TypeAst?
} derive(Eq, Debug)

///|
pub(all) struct Assert {
  location : @span.SrcSpan
  value : Expr
  message : Expr?
} derive(Eq, Debug)

///|
/// `UntypedExpr`.
pub(all) enum Expr {
  Int(location~ : @span.SrcSpan, value~ : String, int_value~ : BigInt)
  Float(location~ : @span.SrcSpan, value~ : String, float_value~ : Double)
  String(location~ : @span.SrcSpan, value~ : String)
  Block(location~ : @span.SrcSpan, statements~ : ArrayView[Statement])
  Var(location~ : @span.SrcSpan, name~ : String)
  Fn(
    location~ : @span.SrcSpan,
    kind~ : FunctionLiteralKind,
    end_of_head_byte_index~ : Int32,
    arguments~ : ArrayView[Arg],
    body~ : ArrayView[Statement],
    return_annotation~ : TypeAst?
  )
  List(location~ : @span.SrcSpan, elements~ : ArrayView[Expr], tail~ : Expr?)
  Call(
    location~ : @span.SrcSpan,
    fun~ : Expr,
    arguments~ : ArrayView[CallArg[Expr]],
    open_parenthesis~ : Int32
  )
  BinOp(
    location~ : @span.SrcSpan,
    operator~ : BinOp,
    operator_start~ : Int32,
    left~ : Expr,
    right~ : Expr
  )
  Pipeline(expressions~ : ArrayView[Expr])
  /// `clauses` is `None` when the `case` has no body.
  Case(
    location~ : @span.SrcSpan,
    subjects~ : ArrayView[Expr],
    clauses~ : ArrayView[Clause]?
  )
  FieldAccess(
    location~ : @span.SrcSpan,
    label_location~ : @span.SrcSpan,
    label~ : String,
    container~ : Expr
  )
  Tuple(location~ : @span.SrcSpan, elements~ : ArrayView[Expr])
  /// `index` is the compiler's `u64`.
  TupleIndex(location~ : @span.SrcSpan, index~ : UInt64, tuple~ : Expr)
  Todo(kind~ : TodoKind, location~ : @span.SrcSpan, message~ : Expr?)
  Panic(location~ : @span.SrcSpan, message~ : Expr?)
  Echo(
    location~ : @span.SrcSpan,
    keyword_end~ : Int32,
    expression~ : Expr?,
    message~ : Expr?
  )
  BitArray(
    location~ : @span.SrcSpan,
    segments~ : ArrayView[BitArraySegment[Expr]]
  )
  RecordUpdate(
    location~ : @span.SrcSpan,
    spread_start~ : Int32,
    constructor_~ : Expr,
    record~ : RecordBeingUpdated[Expr],
    arguments~ : ArrayView[RecordUpdateArg[Expr]]
  )
  NegateBool(location~ : @span.SrcSpan, value~ : Expr)
  NegateInt(location~ : @span.SrcSpan, value~ : Expr)
} derive(Eq, Debug)

///|
pub(all) enum FunctionLiteralKind {
  Capture(hole~ : @span.SrcSpan)
  Anonymous(head~ : @span.SrcSpan)
  Use(location~ : @span.SrcSpan)
} derive(Eq, Debug)

///|
pub(all) struct CallArg[A] {
  label : String?
  location : @span.SrcSpan
  value : A
  implicit : ImplicitCallArgOrigin?
} derive(Eq, Debug)

///|
pub(all) enum ImplicitCallArgOrigin {
  Use
  Pipe
  PatternFieldSpread
  IncorrectArityUse
  RecordUpdate
} derive(Eq, Debug, Hash)

///|
pub fn ImplicitCallArgOrigin::name(self : ImplicitCallArgOrigin) -> String {
  match self {
    Use => "use"
    Pipe => "pipe"
    PatternFieldSpread => "pattern_field_spread"
    IncorrectArityUse => "incorrect_arity_use"
    RecordUpdate => "record_update"
  }
}

///|
pub fn ImplicitCallArgOrigin::of(name : String) -> ImplicitCallArgOrigin? {
  match name {
    "use" => Some(Use)
    "pipe" => Some(Pipe)
    "pattern_field_spread" => Some(PatternFieldSpread)
    "incorrect_arity_use" => Some(IncorrectArityUse)
    "record_update" => Some(RecordUpdate)
    _ => None
  }
}

///|
pub(all) enum BinOp {
  And
  Or
  Eq
  NotEq
  LtInt
  LtEqInt
  LtFloat
  LtEqFloat
  GtEqInt
  GtInt
  GtEqFloat
  GtFloat
  AddInt
  AddFloat
  SubInt
  SubFloat
  MultInt
  MultFloat
  DivInt
  DivFloat
  RemainderInt
  Concatenate
} derive(Eq, Debug, Hash)

///|
/// Every operator, in the compiler's order.
pub fn BinOp::all() -> Array[BinOp] {
  [
    And,
    Or,
    Eq,
    NotEq,
    LtInt,
    LtEqInt,
    LtFloat,
    LtEqFloat,
    GtEqInt,
    GtInt,
    GtEqFloat,
    GtFloat,
    AddInt,
    AddFloat,
    SubInt,
    SubFloat,
    MultInt,
    MultFloat,
    DivInt,
    DivFloat,
    RemainderInt,
    Concatenate,
  ]
}

///|
pub fn BinOp::name(self : BinOp) -> String {
  match self {
    And => "and"
    Or => "or"
    Eq => "eq"
    NotEq => "not_eq"
    LtInt => "lt_int"
    LtEqInt => "lt_eq_int"
    LtFloat => "lt_float"
    LtEqFloat => "lt_eq_float"
    GtEqInt => "gt_eq_int"
    GtInt => "gt_int"
    GtEqFloat => "gt_eq_float"
    GtFloat => "gt_float"
    AddInt => "add_int"
    AddFloat => "add_float"
    SubInt => "sub_int"
    SubFloat => "sub_float"
    MultInt => "mult_int"
    MultFloat => "mult_float"
    DivInt => "div_int"
    DivFloat => "div_float"
    RemainderInt => "remainder_int"
    Concatenate => "concatenate"
  }
}

///|
pub fn BinOp::of(name : String) -> BinOp? {
  for op in BinOp::all() {
    if op.name() == name {
      return Some(op)
    }
  }
  None
}

///|
pub(all) struct Clause {
  location : @span.SrcSpan
  pattern : ArrayView[Pattern]
  alternative_patterns : ArrayView[ArrayView[Pattern]]
  guard_ : ClauseGuard?
  then : Expr
} derive(Eq, Debug)

///|
/// `ClauseGuard` without `ModuleSelect` and `Invalid`, which analysis
/// builds, not the parser (`module.name` in a guard is `FieldAccess`).
pub(all) enum ClauseGuard {
  Block(location~ : @span.SrcSpan, value~ : ClauseGuard)
  BinaryOperator(
    location~ : @span.SrcSpan,
    operator~ : BinOp,
    operator_start~ : Int32,
    left~ : ClauseGuard,
    right~ : ClauseGuard
  )
  Not(location~ : @span.SrcSpan, expression~ : ClauseGuard)
  Var(location~ : @span.SrcSpan, name~ : String)
  TupleIndex(location~ : @span.SrcSpan, index~ : UInt64, tuple~ : ClauseGuard)
  FieldAccess(
    label_location~ : @span.SrcSpan,
    index~ : UInt64?,
    label~ : String,
    container~ : ClauseGuard
  )
  Constant(Constant)
} derive(Eq, Debug)

///|
/// `Pattern` without `Invalid` (analysis only).
pub(all) enum Pattern {
  Int(location~ : @span.SrcSpan, value~ : String, int_value~ : BigInt)
  Float(location~ : @span.SrcSpan, value~ : String, float_value~ : Double)
  String(location~ : @span.SrcSpan, value~ : String)
  Variable(location~ : @span.SrcSpan, name~ : String)
  BitArraySize(BitArraySize)
  Assign(name~ : String, location~ : @span.SrcSpan, pattern~ : Pattern)
  Discard(name~ : String, location~ : @span.SrcSpan)
  List(
    location~ : @span.SrcSpan,
    elements~ : ArrayView[Pattern],
    tail~ : TailPattern?
  )
  Constructor(
    location~ : @span.SrcSpan,
    name_location~ : @span.SrcSpan,
    name~ : String,
    arguments~ : ArrayView[CallArg[Pattern]],
    module_~ : SpannedString?,
    spread~ : @span.SrcSpan?
  )
  Tuple(location~ : @span.SrcSpan, elements~ : ArrayView[Pattern])
  BitArray(
    location~ : @span.SrcSpan,
    segments~ : ArrayView[BitArraySegment[Pattern]]
  )
  StringPrefix(
    location~ : @span.SrcSpan,
    left_location~ : @span.SrcSpan,
    left_side_assignment~ : StringPrefixLeftSideAssignment?,
    right_location~ : @span.SrcSpan,
    left_side_string~ : String,
    right_side_assignment~ : AssignName
  )
} derive(Eq, Debug)

///|
pub(all) struct TailPattern {
  location : @span.SrcSpan
  pattern : Pattern
} derive(Eq, Debug)

///|
pub(all) struct StringPrefixLeftSideAssignment {
  name : String
  name_start_position : Int32
  location : @span.SrcSpan
} derive(Eq, Debug)

///|
pub(all) enum BitArraySize {
  Int(location~ : @span.SrcSpan, value~ : String, int_value~ : BigInt)
  Variable(location~ : @span.SrcSpan, name~ : String)
  BinaryOperator(
    location~ : @span.SrcSpan,
    operator~ : IntOperator,
    left~ : BitArraySize,
    right~ : BitArraySize
  )
  Block(location~ : @span.SrcSpan, inner~ : BitArraySize)
} derive(Eq, Debug)

///|
pub(all) enum IntOperator {
  Add
  Subtract
  Multiply
  Divide
  Remainder
} derive(Eq, Debug, Hash)

///|
pub fn IntOperator::name(self : IntOperator) -> String {
  match self {
    Add => "add"
    Subtract => "subtract"
    Multiply => "multiply"
    Divide => "divide"
    Remainder => "remainder"
  }
}

///|
pub fn IntOperator::of(name : String) -> IntOperator? {
  match name {
    "add" => Some(Add)
    "subtract" => Some(Subtract)
    "multiply" => Some(Multiply)
    "divide" => Some(Divide)
    "remainder" => Some(Remainder)
    _ => None
  }
}

///|
pub(all) struct BitArraySegment[V] {
  location : @span.SrcSpan
  value : V
  options : ArrayView[BitArrayOption[V]]
} derive(Eq, Debug)

///|
pub(all) enum BitArrayOption[V] {
  Bytes(location~ : @span.SrcSpan)
  Int(location~ : @span.SrcSpan)
  Float(location~ : @span.SrcSpan)
  Bits(location~ : @span.SrcSpan)
  Utf8(location~ : @span.SrcSpan)
  Utf16(location~ : @span.SrcSpan)
  Utf32(location~ : @span.SrcSpan)
  Utf8Codepoint(location~ : @span.SrcSpan)
  Utf16Codepoint(location~ : @span.SrcSpan)
  Utf32Codepoint(location~ : @span.SrcSpan)
  Signed(location~ : @span.SrcSpan)
  Unsigned(location~ : @span.SrcSpan)
  Big(location~ : @span.SrcSpan)
  Little(location~ : @span.SrcSpan)
  Native(location~ : @span.SrcSpan)
  Size(location~ : @span.SrcSpan, value~ : V, short_form~ : Bool)
  Unit(location~ : @span.SrcSpan, value~ : Int32)
} derive(Eq, Debug)

///|
pub(all) enum TodoKind {
  Keyword
  EmptyFunction(function_location~ : @span.SrcSpan)
  IncompleteUse
  EmptyBlock
} derive(Eq, Debug)

///|
pub(all) struct RecordBeingUpdated[A] {
  base : A
  location : @span.SrcSpan
} derive(Eq, Debug)

///|
pub(all) struct RecordUpdateArg[A] {
  label : String
  location : @span.SrcSpan
  value : A
} derive(Eq, Debug)

///|
/// `Constant<()>` without `Invalid` (analysis only).
pub(all) enum Constant {
  Int(location~ : @span.SrcSpan, value~ : String, int_value~ : BigInt)
  Float(location~ : @span.SrcSpan, value~ : String, float_value~ : Double)
  String(location~ : @span.SrcSpan, value~ : String)
  Tuple(location~ : @span.SrcSpan, elements~ : ArrayView[Constant])
  List(
    location~ : @span.SrcSpan,
    elements~ : ArrayView[Constant],
    tail~ : Constant?
  )
  /// `arguments` is `None` for a bare constructor (`Nil`), `Some([])` for
  /// `Nil()`.
  Record(
    location~ : @span.SrcSpan,
    arguments_start_position~ : Int32,
    module_~ : SpannedString?,
    name~ : String,
    arguments~ : ArrayView[CallArg[Constant]]?
  )
  RecordUpdate(
    location~ : @span.SrcSpan,
    constructor_location~ : @span.SrcSpan,
    module_~ : SpannedString?,
    name~ : String,
    record~ : RecordBeingUpdated[Constant],
    arguments~ : ArrayView[RecordUpdateArg[Constant]]
  )
  BitArray(
    location~ : @span.SrcSpan,
    segments~ : ArrayView[BitArraySegment[Constant]]
  )
  Var(location~ : @span.SrcSpan, module_~ : SpannedString?, name~ : String)
  BinaryOperator(
    location~ : @span.SrcSpan,
    operator_start~ : Int32,
    operator~ : BinOp,
    left~ : Constant,
    right~ : Constant
  )
  Todo(location~ : @span.SrcSpan, message~ : Constant?)
} derive(Eq, Debug)

///|
/// `parse::extra::ModuleExtra`: byte positions of the trivia the formatter
/// needs. Comment spans are the compiler's (after the slashes).
pub(all) struct ModuleExtra {
  module_comments : ArrayView[@span.SrcSpan]
  doc_comments : ArrayView[@span.SrcSpan]
  comments : ArrayView[@span.SrcSpan]
  empty_lines : ArrayView[Int32]
  new_lines : ArrayView[Int32]
  trailing_commas : ArrayView[Int32]
} derive(Eq, Debug)

///|
pub fn ModuleExtra::empty() -> ModuleExtra {
  {
    module_comments: [][:],
    doc_comments: [][:],
    comments: [][:],
    empty_lines: [][:],
    new_lines: [][:],
    trailing_commas: [][:],
  }
}

///|
/// The warnings `parse_module` emits: `DeprecatedSyntaxWarning` and
/// `DetachedDocComment`.
pub(all) enum Warning {
  DeprecatedListPrepend(location~ : @span.SrcSpan)
  DeprecatedListPattern(location~ : @span.SrcSpan)
  DeprecatedListCatchAllPattern(location~ : @span.SrcSpan)
  DeprecatedListSpreadAsVariablePattern(
    location~ : @span.SrcSpan,
    name~ : String
  )
  DeprecatedRecordSpreadPattern(location~ : @span.SrcSpan)
  DeprecatedEmptyClauseGuard(location~ : @span.SrcSpan)
  DeprecatedTargetShorthand(target~ : Target, location~ : @span.SrcSpan)
  DetachedDocComment(location~ : @span.SrcSpan)
} derive(Eq, Debug)