///|
/// Spec for a small, R6RS-compatible Scheme interpreter.
///
/// Phase 1 scope (implemented by tests in this module):
/// - exact integers, exact rationals, inexact floats, booleans, symbols,
///   characters, strings, pairs/lists
/// - quote, quasiquote, unquote, unquote-splicing, if, begin, lambda, define,
///   set!, let (including named let), let*, letrec, letrec*, let-syntax,
///   letrec-syntax, and, or, cond (including =>), case, do, case-lambda,
///   let-values, let*-values, define-values, parameterize, guard,
///   syntax-case (limited), identifier-syntax
/// - dynamic-wind
/// - define-syntax + syntax-rules (hygienic identifiers), including "..." repetition
/// - application with left-to-right evaluation
/// - proper tail calls for procedure application
/// - call/cc (call-with-current-continuation)
/// - define-record-type (records with inheritance and protocol)
/// - library and import (minimal library support)
/// - builtins: + - * / = < > <= >= cons car cdr list null? pair? symbol?
///   identifier? syntax? free-identifier=? bound-identifier=?
///   symbol? symbol=? symbol->string string->symbol syntax syntax->datum datum->syntax
///   boolean? boolean=? number? integer? exact-integer? rational? real? complex? exact? inexact?
///   zero? positive? negative? odd? even? finite? infinite? nan?
///   exact->inexact inexact->exact number->string string->number
///   make-rectangular make-polar real-part imag-part magnitude angle
///   sqrt exp log expt sin cos tan asin acos atan
///   numerator denominator abs quotient remainder modulo div mod div0 mod0
///   div-and-mod div0-and-mod0 gcd lcm
///   max min floor ceiling truncate round
///   procedure? record? record-rtd record-type-descriptor?
///   record-constructor-descriptor? record-type-name record-type-parent
///   record-type-uid record-type-generative?
///   record-type-sealed? record-type-opaque? record-type-field-names
///   record-type-field-mutable?
///   record-constructor-descriptor record-constructor
///   record-predicate record-accessor record-mutator
///   make-record-type-descriptor make-record-constructor-descriptor
///   not apply call/cc call-with-current-continuation eq? eqv?
///   equal? list? make-list list-ref list-tail length append
///   reverse c[ad]+r member memq memv assoc assq assv map for-each list-copy
///   set-car! set-cdr!
///   char? char=? char? char<=? char>=?
///   char-ci=? char-ci? char-ci<=? char-ci>=?
///   char->integer integer->char char-alphabetic? char-numeric? char-whitespace?
///   char-upper-case? char-lower-case? char-upcase char-downcase char-foldcase
///   char-general-category
///   string make-string string? string=? string? string<=? string>=?
///   string-ci=? string-ci? string-ci<=? string-ci>=?
///   string-length string-append string-ref string-set! string-copy string-copy!
///   string-fill! string->list list->string
///   string-map string-for-each
///   string-upcase string-downcase string-foldcase
///   string-normalize-nfc string-normalize-nfd string-normalize-nfkc string-normalize-nfkd
///   string-hash string-ci-hash symbol-hash equal-hash
///   vector make-vector vector? vector-length vector-ref vector-set!
///   vector-copy vector-copy! vector-fill! vector-map vector-for-each
///   vector->list list->vector
///   bytevector make-bytevector bytevector? bytevector-length bytevector=?
///   bytevector-u8-ref bytevector-u8-set! bytevector-s8-ref bytevector-s8-set!
///   bytevector-copy bytevector-copy!
///   bytevector-fill!
///   bytevector->u8-list u8-list->bytevector string->utf8 utf8->string
///   endianness native-endianness bytevector-uint-ref bytevector-sint-ref
///   bytevector-uint-set! bytevector-sint-set!
///   display write newline open-output-string get-output-string current-output-port
///   with-exception-handler raise raise-continuable
///   values call-with-values make-parameter dynamic-wind parameterize
///   guard
///   (equality is structural for datums in this subset)
/// - printing via value_to_string (booleans as #t/#f, empty list as ())
///
/// Out of scope for now: full numeric tower.

///|
pub(all) suberror ParseError {
  ParseError(String)
} derive(Debug, Eq)

///|
pub extend ParseError with @debug.Debug::{to_repr}

///|
pub extend ParseError with Eq::{not_equal, equal}

///|
pub(all) suberror EvalError {
  EvalError(String)
} derive(Debug, Eq)

///|
pub extend EvalError with @debug.Debug::{to_repr}

///|
pub extend EvalError with Eq::{not_equal, equal}

///|
/// Datum is the reader-level data representation used by quote.
/// It is also used to store list/vector elements, so it includes runtime-only
/// variants for records/conditions even though the reader never produces them.
pub(all) enum Datum {
  Nil
  Bool(Bool)
  Int(Int)
  BigInt(@bigint.BigInt)
  Rat(Int, Int)
  BigRat(@bigint.BigInt, @bigint.BigInt)
  Float(Float)
  Complex(Ref[Datum], Ref[Datum])
  Label(Int, Ref[Datum])
  Char(Char)
  String(Ref[String])
  Symbol(String)
  Pair(Ref[Datum], Ref[Datum])
  Vector(Array[Datum])
  ByteVector(Array[Int])
  Record(Record)
  Condition(Condition)
  Value(Value)
}

///|
/// Primitive procedures provided by the base environment.

///|
pub(all) enum Primitive {
  Add
  Sub
  Mul
  Div
  NumEq
  Less
  Greater
  LessEq
  GreaterEq
  Eq
  Eqv
  Equal
  Cons
  Car
  Cdr
  List
  NullP
  PairP
  SymbolP
  SymbolEq
  IdentifierP
  SyntaxP
  FreeIdentifierEq
  BoundIdentifierEq
  SymbolToString
  StringToSymbol
  StringHash
  StringCiHash
  SymbolHash
  EqualHash
  SyntaxToDatum
  DatumToSyntax
  BooleanP
  BooleanEq
  NumberP
  IntegerP
  ExactIntegerP
  RationalP
  RealP
  ComplexP
  ExactP
  InexactP
  ZeroP
  PositiveP
  NegativeP
  OddP
  EvenP
  FiniteP
  InfiniteP
  NanP
  ProcedureP
  RecordP
  RecordRtd
  RecordTypeDescriptorP
  RecordConstructorDescriptorP
  RecordTypeName
  RecordTypeParent
  RecordTypeUid
  RecordTypeGenerativeP
  RecordTypeSealedP
  RecordTypeOpaqueP
  RecordTypeFieldNames
  RecordTypeFieldMutableP
  RecordConstructorDescriptor
  RecordConstructor
  RecordPredicate
  RecordAccessor
  RecordMutator
  MakeRecordTypeDescriptor
  MakeRecordConstructorDescriptor
  Condition
  ConditionP
  SimpleConditions
  ConditionPredicate
  ConditionAccessor
  MakeEqHashtable
  MakeEqvHashtable
  MakeHashtable
  HashtableP
  HashtableSize
  HashtableRef
  HashtableSet
  HashtableDelete
  HashtableContainsP
  HashtableUpdate
  HashtableCopy
  HashtableClear
  HashtableKeys
  HashtableEntries
  HashtableEquivalenceFunction
  HashtableHashFunction
  HashtableMutableP
  MakeEnumeration
  EnumSetUniverse
  EnumSetIndexer
  EnumSetConstructor
  EnumSetP
  EnumSetMemberP
  EnumSetSubsetP
  EnumSetEq
  EnumSetUnion
  EnumSetIntersection
  EnumSetDifference
  EnumSetComplement
  EnumSetProjection
  EnumSetToList
  Not
  Apply
  CallCC
  Values
  CallWithValues
  MakeParameter
  DynamicWind
  Eval
  Environment
  PromiseP
  MakePromise
  Force
  ExactToInexact
  InexactToExact
  ExactIntegerSqrt
  Rationalize
  NumberToString
  StringToNumber
  MakeRectangular
  MakePolar
  RealPart
  ImagPart
  Magnitude
  Angle
  Sqrt
  Exp
  Log
  Expt
  Sin
  Cos
  Tan
  Asin
  Acos
  Atan
  Numerator
  Denominator
  Abs
  Quotient
  Remainder
  Modulo
  Gcd
  Lcm
  Max
  Min
  Floor
  Ceiling
  Truncate
  Round
  BitwiseAnd
  BitwiseIor
  BitwiseXor
  BitwiseNot
  BitwiseIf
  ArithmeticShift
  BitwiseBitCount
  BitwiseLength
  BitwiseFirstBitSet
  BitwiseBitSetP
  BitwiseCopyBit
  BitwiseBitField
  BitwiseCopyBitField
  BitwiseRotateBitField
  BitwiseReverseBitField
  FixnumP
  FixnumWidth
  LeastFixnum
  GreatestFixnum
  FxEq
  FxLess
  FxGreater
  FxLessEq
  FxGreaterEq
  FxZeroP
  FxPositiveP
  FxNegativeP
  FxOddP
  FxEvenP
  FxMin
  FxMax
  FxAdd
  FxSub
  FxMul
  FxDiv
  FxMod
  FxDiv0
  FxMod0
  FxAddCarry
  FxSubCarry
  FxMulCarry
  FxNot
  FxAnd
  FxIor
  FxXor
  FxIf
  FxBitCount
  FxLength
  FxFirstBitSet
  FxBitSetP
  FxCopyBit
  FxBitField
  FxCopyBitField
  FxRotateBitField
  FxReverseBitField
  FxArithmeticShift
  FxArithmeticShiftLeft
  FxArithmeticShiftRight
  FlonumP
  RealToFlonum
  FixnumToFlonum
  FlEq
  FlLess
  FlGreater
  FlLessEq
  FlGreaterEq
  FlIntegerP
  FlZeroP
  FlPositiveP
  FlNegativeP
  FlOddP
  FlEvenP
  FlFiniteP
  FlInfiniteP
  FlNanP
  FlMax
  FlMin
  FlAdd
  FlMul
  FlSub
  FlDiv
  FlAbs
  FlDivAndMod
  FlDivInt
  FlMod
  FlDiv0AndMod0
  FlDiv0
  FlMod0
  FlNumerator
  FlDenominator
  FlFloor
  FlCeiling
  FlTruncate
  FlRound
  FlExp
  FlLog
  FlSin
  FlCos
  FlTan
  FlAsin
  FlAcos
  FlAtan
  FlSqrt
  FlExpt
  MakeVariableTransformer
  GenerateTemporaries
  ListP
  MakeList
  Length
  Append
  Reverse
  ListRef
  ListTail
  Cxr(String)
  Member
  Memq
  Memv
  Assoc
  Assq
  Assv
  Map
  ForEach
  SetCar
  SetCdr
  ListCopy
  CharEq
  CharLess
  CharGreater
  CharLessEq
  CharGreaterEq
  CharCiEq
  CharCiLess
  CharCiGreater
  CharCiLessEq
  CharCiGreaterEq
  CharP
  CharToInteger
  IntegerToChar
  CharAlphabeticP
  CharNumericP
  CharWhitespaceP
  CharUpperCaseP
  CharLowerCaseP
  CharUpcase
  CharDowncase
  CharFoldcase
  CharGeneralCategory
  StringEq
  StringLess
  StringGreater
  StringLessEq
  StringGreaterEq
  StringCiEq
  StringCiLess
  StringCiGreater
  StringCiLessEq
  StringCiGreaterEq
  String
  MakeString
  StringP
  StringLength
  StringAppend
  StringRef
  StringSet
  StringCopy
  Substring
  StringCopyBang
  StringFill
  StringToList
  ListToString
  StringMap
  StringForEach
  StringUpcase
  StringDowncase
  StringFoldcase
  StringNormalizeNfc
  StringNormalizeNfd
  StringNormalizeNfkc
  StringNormalizeNfkd
  Vector
  MakeVector
  VectorP
  VectorLength
  VectorRef
  VectorSet
  VectorFill
  VectorToList
  ListToVector
  VectorCopy
  VectorCopyBang
  VectorAppend
  VectorMap
  VectorForEach
  ByteVector
  MakeByteVector
  ByteVectorP
  ByteVectorLength
  ByteVectorEq
  ByteVectorU8Ref
  ByteVectorU8Set
  ByteVectorCopy
  ByteVectorCopyBang
  ByteVectorAppend
  ByteVectorFill
  ByteVectorToU8List
  U8ListToByteVector
  StringToUtf8
  Utf8ToString
  NativeEndianness
  ByteVectorUintRef
  ByteVectorSintRef
  ByteVectorUintSet
  ByteVectorSintSet
  Display
  Write
  Newline
  OpenOutputString
  GetOutputString
  CurrentOutputPort
  WithExceptionHandler
  Raise
  RaiseContinuable
  Error
  AssertionViolation
  ImplementationRestrictionViolation
  UndefinedViolation
  SyntaxViolation
} derive(Eq)

///|
pub extend Primitive with Eq::{not_equal, equal}

///|
/// Lexical environment (stack of frames) captured by closures.
/// The implementation treats this as an internal detail.

///|
pub struct Binding {
  id : Int
  value : Value
}

///|
pub type Env = Array[Map[String, Binding]]

///|
/// A syntax-rules transformer.

///|
pub struct SyntaxRule {
  pattern : Datum
  template : Datum
  fender : Datum?
}

///|
/// A collection of syntax-rules with literal identifiers.

///|
pub(all) enum SyntaxRulesKind {
  SyntaxRules
  SyntaxCase
}

///|
pub struct SyntaxRules {
  literals : Array[String]
  rules : Array[SyntaxRule]
  ellipsis : String
  kind : SyntaxRulesKind
  def_env : Env
}

///|
pub(all) enum MacroTransformer {
  Rules(SyntaxRules)
  Procedure(Value, Env)
}

///|
/// Closure is an opaque procedure value produced by lambda.
/// The interpreter may store extra data internally; tests treat it as opaque.

///|
pub struct Closure {
  id : Int
  params : Array[String]
  rest : String?
  body : Array[Datum]
  env : Env
}

///|
pub struct CaseClause {
  params : Array[String]
  rest : String?
  body : Array[Datum]
}

///|
pub struct CaseClosure {
  id : Int
  clauses : Array[CaseClause]
  env : Env
}

///|
pub struct GuardHandler {
  id : Int
  name : String
  clauses : Array[Datum]
  env : Env
  resume_value : Value
  handlers : Array[Value]
}

///|
pub struct Parameter {
  id : Int
  value : Ref[Value]
  converter : Value?
}

///|
pub(all) enum PromiseState {
  Thunk(Value)
  Value(Value)
}

///|
pub struct Promise {
  id : Int
  state : Ref[PromiseState]
}

///|
pub struct EvalEnv {
  id : Int
  env : Env
}

///|
pub struct SyntaxObject {
  datum : Datum
  scopes : Array[Int]
  binding_id : Int?
}

///|
/// Runtime values produced by evaluation.

///|
pub(all) enum Value {
  Void
  Datum(Datum)
  Primitive(Primitive)
  Closure(Closure)
  CaseClosure(CaseClosure)
  Values(Array[Value])
  GuardHandler(GuardHandler)
  Parameter(Parameter)
  Promise(Promise)
  EvalEnv(EvalEnv)
  Continuation(Continuation)
  Port(Port)
  Record(Record)
  RecordProc(RecordProc)
  ConditionProc(ConditionProc)
  Hashtable(Hashtable)
  EnumSet(EnumSet)
  EnumSetProc(EnumSetProc)
  RecordTypeDescriptor(RecordTypeDescriptor)
  RecordConstructorDescriptor(RecordConstructorDescriptor)
  SyntaxObject(SyntaxObject)
  SyntaxKeyword(String)
  Macro(MacroTransformer)
}

///|
pub impl Show for ParseError with fn output(self, logger) {
  logger.write_string(@debug.render(self.to_repr(), max_depth=2147483647))
}

///|
pub extend ParseError with Show::{to_string, output}

///|
pub impl Show for EvalError with fn output(self, logger) {
  logger.write_string(@debug.render(self.to_repr(), max_depth=2147483647))
}

///|
pub extend EvalError with Show::{to_string, output}