///|
pub(all) enum BinOp {
  Add
  Sub
  Mul
  Div
  Mod
  EqEq
  EqEqEq
  NotEq
  NotEqEq
  Lt
  Gt
  LtEq
  GtEq
  And
  Or
  NullishCoalesce // ??
  BitAnd
  BitOr
  BitXor
  LShift
  RShift
  URShift
  Exp
  In
  Instanceof
} derive(Debug, Eq)

///|
pub(all) enum UnaryOp {
  Neg
  Pos
  Not
  Typeof
  Void
  Delete
  BitNot
} derive(Debug, Eq)

///|
pub(all) enum UpdateOp {
  Increment
  Decrement
} derive(Debug, Eq)

///|
pub(all) enum CompoundOp {
  AddAssign
  SubAssign
  MulAssign
  DivAssign
  ModAssign
  ExpAssign
  BitAndAssign
  BitOrAssign
  BitXorAssign
  LShiftAssign
  RShiftAssign
  URShiftAssign
  LogicalAndAssign // &&=
  LogicalOrAssign // ||=
  NullishAssign // ??=
} derive(Debug, Eq)

///|
pub(all) enum VarKind {
  LetKind
  ConstKind
  VarKind
} derive(Debug, Eq)

///|
pub(all) enum PropKind {
  Init // normal property
  Get // getter
  Set // setter
  Spread // spread property: { ...expr }
} derive(Debug, Eq)

///|
pub(all) struct Property {
  key : Expr // StringLit for static keys, any Expr for computed keys
  value : Expr
  kind : PropKind
  computed : Bool // true for [expr]: value syntax
  // True for method-shorthand (`{ m() {} }`, `{ *m() {} }`, `{ async m() {} }`)
  // and for getters/setters. False for `key: value` entries even when the
  // value happens to be a function. Drives whether the function gets a
  // §15.2.5 FunctionExpressionName self-binding when evaluated.
  is_method : Bool
} derive(Debug, Eq)

///|
pub(all) struct SwitchCase {
  condition : Expr?
  body : Array[Stmt]
} derive(Debug, Eq)

///|
pub(all) enum Expr {
  NumberLit(Double, @token.LexForm, @token.Loc)
  StringLit(String, Bool, @token.LexForm, @token.Loc) // value, has_escape, lex_form, loc
  BoolLit(Bool, @token.Loc)
  NullLit(@token.Loc)
  UndefinedLit(@token.Loc)
  ArrayHole(@token.Loc) // elision in array literal: [1,,3] → hole at index 1
  Ident(String, @token.Loc)
  Binary(BinOp, Expr, Expr, @token.Loc)
  Unary(UnaryOp, Expr, @token.Loc)
  Assign(String, Expr, @token.Loc)
  Call(Expr, Array[Expr], @token.Loc)
  Member(Expr, String, @token.Loc)
  Ternary(Expr, Expr, Expr, @token.Loc)
  FuncExpr(String?, Array[String], Array[Stmt], @token.Loc, String?)
  FuncExprExt(String?, Array[Param], String?, Array[Stmt], @token.Loc, String?) // name, params, rest_param, body, source_text
  Grouping(Expr, @token.Loc)
  ObjectLit(Array[Property], @token.Loc)
  ArrayLit(Array[Expr], @token.Loc)
  ComputedMember(Expr, Expr, @token.Loc)
  MemberAssign(Expr, String, Expr, @token.Loc)
  PrivateMemberAssign(Expr, String, Expr, @token.Loc) // obj.#name = value
  ComputedAssign(Expr, Expr, Expr, @token.Loc)
  NewExpr(Expr, Array[Expr], @token.Loc)
  ThisExpr(@token.Loc)
  UpdateExpr(UpdateOp, Expr, Bool, @token.Loc)
  CompoundAssign(CompoundOp, Expr, Expr, @token.Loc)
  Comma(Expr, Expr, @token.Loc)
  TemplateLit(Array[(String, String?)], Array[Expr], @token.Loc) // quasis: (raw, cooked?)
  ArrowFunc(Array[String], Array[Stmt], @token.Loc, String?)
  ArrowFuncExt(Array[Param], String?, Array[Stmt], @token.Loc, String?) // params, rest_param, body, source_text
  SpreadExpr(Expr, @token.Loc)
  RegexLit(String, String, @token.Loc) // pattern, flags
  DestructureAssign(Pattern, Expr, @token.Loc)
  OptionalMember(Expr, String, @token.Loc) // obj?.prop
  OptionalComputedMember(Expr, Expr, @token.Loc) // obj?.[expr]
  OptionalCall(Expr, Array[Expr], @token.Loc) // func?.(args)
  ChainMember(Expr, String, @token.Loc) // optional chain continuation .prop
  ChainComputedMember(Expr, Expr, @token.Loc) // optional chain continuation [expr]
  // Class expressions
  ClassExpr(String?, Expr?, Array[ClassMember], @token.Loc, String?) // name, superclass, members, source_text
  SuperCall(Array[Expr], @token.Loc) // super(args)
  SuperMember(String, @token.Loc) // super.prop
  SuperComputedMember(Expr, @token.Loc) // super[expr]
  SuperMemberAssign(String, Expr, @token.Loc) // super.prop = value
  SuperComputedAssign(Expr, Expr, @token.Loc) // super[key] = value
  NewTargetExpr(@token.Loc) // new.target
  TaggedTemplate(Expr, Array[(String, String?)], Array[Expr], @token.Loc) // tag, quasis (raw, cooked?), exprs
  // Generator expressions
  GeneratorExpr(String?, Array[String], Array[Stmt], @token.Loc, String?) // name, params, body, source_text
  GeneratorExprExt(
    String?,
    Array[Param],
    String?,
    Array[Stmt],
    @token.Loc,
    String?
  ) // name, params, rest_param, body, source_text
  // Yield expression
  YieldExpr(Expr?, Bool, @token.Loc) // argument, delegate (yield* vs yield)
  // Async function expressions
  AsyncFuncExpr(String?, Array[String], Array[Stmt], @token.Loc, String?) // name, params, body, source_text
  AsyncFuncExprExt(
    String?,
    Array[Param],
    String?,
    Array[Stmt],
    @token.Loc,
    String?
  ) // name, params, rest_param, body, source_text
  // Async arrow functions
  AsyncArrowFunc(Array[String], Array[Stmt], @token.Loc, String?) // params, body, source_text
  AsyncArrowFuncExt(Array[Param], String?, Array[Stmt], @token.Loc, String?) // params, rest_param, body, source_text
  // Async generator expressions
  AsyncGeneratorExpr(String?, Array[String], Array[Stmt], @token.Loc, String?) // name, params, body, source_text
  AsyncGeneratorExprExt(
    String?,
    Array[Param],
    String?,
    Array[Stmt],
    @token.Loc,
    String?
  ) // name, params, rest_param, body, source_text
  // Await expression
  AwaitExpr(Expr, @token.Loc) // argument
  // Annex B §B.3.4: f()=g() non-strict — eval call for side effects, then throw ReferenceError (strict: SyntaxError)
  WebCompatCallAssign(Expr, Expr, @token.Loc)
  // Private name reference
  PrivateIdent(String, @token.Loc) // #name as expression (e.g. `#x in obj`)
  PrivateMember(Expr, String, @token.Loc) // obj.#name
} derive(Debug, Eq)

///|
pub(all) struct Param {
  name : String
  default_val : Expr?
  pattern : Pattern? // For destructuring parameters like ({x, y}) or ([a, b])
  is_rest_pattern : Bool // true only for destructuring rest parameters like ...[x]
} derive(Debug, Eq)

///|
pub(all) enum Pattern {
  IdentPat(String)
  ArrayPat(Array[Pattern?], Pattern?) // elements (None = hole), rest pattern
  ObjectPat(Array[PropPat], Pattern?) // properties, rest pattern
  DefaultPat(Pattern, Expr) // pattern with default initializer: pat = expr
  AssignTarget(Expr) // member expression target for destructuring assignment: [obj.x] = [1]
} derive(Debug, Eq)

///|
pub(all) struct PropPat {
  key : String
  key_lex_form : @token.LexForm // provenance for strict-mode legacy-octal validation
  key_loc : @token.Loc // location of the key token, for error pinpointing
  value : Pattern
  default_val : Expr?
  computed_key : Expr? // For computed property keys: { [expr]: pattern }
} derive(Debug, Eq)

///|
pub(all) struct ImportSpecifier {
  local_name : String // local binding name
  imported : String // name exported by the module
} derive(Debug, Eq)

///|
pub(all) struct ExportSpecifier {
  local_name : String // local binding name
  exported : String // name to export as
} derive(Debug, Eq)

///|
pub(all) struct ClassMethod {
  key : Expr // StringLit for static keys, any Expr for computed keys
  value : Expr // Usually FuncExpr or FuncExprExt
  kind : PropKind // Init for regular method, Get for getter, Set for setter
  computed : Bool // true for [expr]() syntax
  is_static : Bool // true for static methods
  is_private : Bool // true for private (#name) methods
} derive(Debug, Eq)

///|
pub(all) struct ClassField {
  key : Expr // StringLit for string keys, any Expr for computed keys
  initializer : Expr? // None if no `= expr`
  computed : Bool // true for [expr] syntax
  is_static : Bool // true for static fields
  is_private : Bool // true for private (#name) fields
} derive(Debug, Eq)

///|
pub(all) enum ClassMember {
  Method(ClassMethod)
  Field(ClassField)
  StaticBlock(Array[Stmt], @token.Loc) // static { ... } block
} derive(Debug, Eq)

///|
pub(all) enum Stmt {
  ExprStmt(Expr, @token.Loc)
  VarDecl(VarKind, String, Expr?, @token.Loc)
  Block(Array[Stmt], @token.Loc)
  StmtList(Array[Stmt], @token.Loc) // Multiple statements without new scope (for comma-separated decls)
  IfStmt(Expr, Stmt, Stmt?, @token.Loc)
  WhileStmt(Expr, Stmt, @token.Loc)
  ForStmt(Stmt?, Expr?, Expr?, Stmt, @token.Loc)
  FuncDecl(String, Array[String], Array[Stmt], @token.Loc, String?)
  FuncDeclExt(String, Array[Param], String?, Array[Stmt], @token.Loc, String?) // name, params, rest_param, body, source_text
  ReturnStmt(Expr?, @token.Loc)
  BreakStmt(String?, @token.Loc)
  ContinueStmt(String?, @token.Loc)
  ThrowStmt(Expr, @token.Loc)
  TryCatchStmt(Array[Stmt], Pattern?, Array[Stmt]?, Array[Stmt]?, @token.Loc)
  SwitchStmt(Expr, Array[SwitchCase], @token.Loc)
  DoWhileStmt(Stmt, Expr, @token.Loc)
  ForInStmt(VarKind?, String, Expr, Stmt, @token.Loc)
  ForOfStmt(VarKind?, String, Expr, Stmt, @token.Loc)
  ForInStmtPat(VarKind?, Pattern, Expr, Stmt, @token.Loc)
  ForOfStmtPat(VarKind?, Pattern, Expr, Stmt, @token.Loc)
  ForInExpr(Expr, Expr, Stmt, @token.Loc) // for (expr in obj) where expr is member/computed
  ForOfExpr(Expr, Expr, Stmt, @token.Loc) // for (expr of iterable) where expr is member/computed
  // Async for-await-of statements
  AsyncForOfStmt(VarKind?, String, Expr, Stmt, @token.Loc) // for await (var x of iterable)
  AsyncForOfStmtPat(VarKind?, Pattern, Expr, Stmt, @token.Loc) // for await (var [a,b] of iterable)
  AsyncForOfExpr(Expr, Expr, Stmt, @token.Loc) // for await (expr of iterable) where expr is member/computed
  DestructureDecl(VarKind, Pattern, Expr, @token.Loc)
  LabeledStmt(String, Stmt, @token.Loc)
  // Generator declarations
  GeneratorDecl(String, Array[String], Array[Stmt], @token.Loc, String?) // name, params, body, source_text
  GeneratorDeclExt(
    String,
    Array[Param],
    String?,
    Array[Stmt],
    @token.Loc,
    String?
  ) // name, params, rest_param, body, source_text
  // Async function declarations
  AsyncFuncDecl(String, Array[String], Array[Stmt], @token.Loc, String?) // name, params, body, source_text
  AsyncFuncDeclExt(
    String,
    Array[Param],
    String?,
    Array[Stmt],
    @token.Loc,
    String?
  ) // name, params, rest_param, body, source_text
  // Async generator declarations
  AsyncGeneratorDecl(String, Array[String], Array[Stmt], @token.Loc, String?) // name, params, body, source_text
  AsyncGeneratorDeclExt(
    String,
    Array[Param],
    String?,
    Array[Stmt],
    @token.Loc,
    String?
  ) // name, params, rest_param, body, source_text
  // Class declaration
  ClassDecl(String, Expr?, Array[ClassMember], @token.Loc, String?) // name, superclass, members, source_text
  // With statement (sloppy mode only)
  WithStmt(Expr, Stmt, @token.Loc) // with(expr) stmt
  // Module declarations
  // import defaultExport from 'source'
  // import { spec1, spec2 } from 'source'
  // import * as namespace from 'source'
  // import 'source' (side effect only)
  ImportDecl(
    String?, // default import name
    Array[ImportSpecifier], // named imports: { a, b as c }
    String?, // namespace import: * as name
    String, // module source string
    @token.LexForm, // source_lex_form: provenance for strict validator
    @token.Loc
  )
  // export { spec1, spec2 }
  // export { spec1 } from 'source' (re-export)
  ExportNamedDecl(
    Stmt?, // declaration (var/let/const/func/class)
    Array[ExportSpecifier], // named specifiers
    String?, // source for re-export
    @token.LexForm, // source_lex_form: LexNormal if no source
    @token.Loc
  )
  // export default expression
  ExportDefaultDecl(Expr, @token.Loc)
  // export * from 'source'
  // export * as name from 'source'
  ExportAllDecl(
    String?, // optional alias: export * as name
    String, // source module
    @token.LexForm, // source_lex_form: provenance for strict validator
    @token.Loc
  )
} derive(Debug, Eq)

///|
pub(all) struct Program {
  stmts : Array[Stmt]
} derive(Debug, Eq)

// Show impls delegating to Debug (derive(Show) is deprecated in MoonBit v0.9)

///|
pub impl Show for BinOp with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for UnaryOp with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for UpdateOp with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for CompoundOp with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for VarKind with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for PropKind with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for Property with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for SwitchCase with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for Expr with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for Param with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for Pattern with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for PropPat with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for ImportSpecifier with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for ExportSpecifier with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for ClassMethod with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for ClassField with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for ClassMember with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for Stmt with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}

///|
pub impl Show for Program with fn output(self, logger) {
  Debug::to_repr(self).output(logger)
}