///|
impl FromSexp with sexp_path_key(_) {
  None
}

///|
pub impl FromSexp for Symbol with sexp_path_key(key) {
  Some(key)
}

///|
pub impl ToSexp for Int with to_sexp(self) {
  Int(self)
}

///|
pub impl FromSexp for Int with from_sexp(sexp, path) {
  match sexp {
    Int(n) => n
    _ => raise SexpError("Int::from_sexp: Expected Int", path)
  }
}

///|
pub impl ToSexp for UInt with to_sexp(self) {
  String(self.to_string())
}

///|
pub impl FromSexp for UInt with from_sexp(sexp, path) {
  guard sexp is String(str) else {
    raise SexpError(
      "UInt::from_sexp: Expected number in string representation", path,
    )
  }
  @string.parse_uint(str) catch {
    error =>
      raise SexpError("UInt::from_sexp: UInt parsing failure \{error}", path)
  }
}

///|
pub impl ToSexp for Int64 with to_sexp(self) {
  String(self.to_string())
}

///|
pub impl FromSexp for Int64 with from_sexp(sexp, path) {
  guard sexp is String(str) else {
    raise SexpError(
      "Int64::from_sexp: Expected number in string representation", path,
    )
  }
  @string.parse_int64(str) catch {
    error =>
      raise SexpError("Int64::from_sexp: Int64 parsing failure \{error}", path)
  }
}

///|
pub impl ToSexp for UInt64 with to_sexp(self) {
  String(self.to_string())
}

///|
pub impl FromSexp for UInt64 with from_sexp(sexp, path) {
  guard sexp is String(str) else {
    raise SexpError(
      "UInt64::from_sexp: Expected number in string representation", path,
    )
  }
  @string.parse_uint64(str) catch {
    error =>
      raise SexpError(
        "UInt64::from_sexp: UInt64 parsing failure \{error}",
        path,
      )
  }
}

///|
pub impl ToSexp for Double with to_sexp(self) {
  Double(self)
}

///|
pub impl FromSexp for Double with from_sexp(sexp, path) {
  match sexp {
    Double(n) => n
    Int(i) => i.to_double()
    _ => raise SexpError("Double::from_sexp: Expected Double", path)
  }
}

///|
pub impl ToSexp for Float with to_sexp(self) {
  Double(self.to_double())
}

///|
pub impl FromSexp for Float with from_sexp(sexp, path) {
  match sexp {
    Double(n) => Float::from_double(n)
    Int(i) => Float::from_double(i.to_double())
    _ => raise SexpError("Float::from_sexp: Expected Double", path)
  }
}

///|
pub impl ToSexp for Bool with to_sexp(self) {
  Bool(self)
}

///|
pub impl FromSexp for Bool with from_sexp(sexp, path) {
  match sexp {
    Bool(b) => b
    _ => raise SexpError("Bool::from_sexp: Expected Bool", path)
  }
}

///|
pub impl ToSexp for Char with to_sexp(self) {
  Sexp::Char(self)
}

///|
pub impl FromSexp for Char with from_sexp(sexp, path) {
  match sexp {
    Char(c) => c
    _ => raise SexpError("Char::from_sexp: Expected Char", path)
  }
}

///|
pub impl ToSexp for String with to_sexp(self) {
  String(self)
}

///|
pub impl FromSexp for String with from_sexp(sexp, path) {
  match sexp {
    String(s) => s
    _ => raise SexpError("String::from_sexp: Expected String", path)
  }
}

///|
pub impl ToSexp for StringView with to_sexp(self) {
  String(self.to_string())
}

///|
pub impl FromSexp for StringView with from_sexp(sexp, path) {
  (FromSexp::from_sexp(sexp, path) : String)
}

///|
pub impl FromSexp for Symbol with from_sexp(sexp, path) {
  match sexp {
    Symbol(s) => s
    _ => raise SexpError("Symbol::from_sexp: Expected Symbol", path)
  }
}

///|
pub impl ToSexp for Symbol with to_sexp(self) {
  Symbol(self)
}

///|
pub impl FromSexp for Unit with from_sexp(sexp, path) {
  match sexp {
    List(xs) if xs.length() == 0 => ()
    _ => raise SexpError("Unit::from_sexp: Expected empty list", path)
  }
}

///|
pub impl ToSexp for Unit with to_sexp(_) {
  List([])
}

///|
pub impl[T : ToSexp] ToSexp for ArrayView[T] with to_sexp(self) {
  Sexp::List(self.map(fn(x) { x.to_sexp() }))
}

///|
pub impl[T : FromSexp] FromSexp for ArrayView[T] with from_sexp(sexp, path) {
  (FromSexp::from_sexp(sexp, path) : Array[T])
}

///|
pub impl[T : ToSexp] ToSexp for Array[T] with to_sexp(self) {
  ToSexp::to_sexp(self[:])
}

///|
pub impl[T : FromSexp] FromSexp for Array[T] with from_sexp(sexp, path) {
  match sexp {
    List(xs) => {
      let arr = []
      for i = 0; i < xs.length(); i = i + 1 {
        arr.push(T::from_sexp(xs[i], path.add_index(i)))
      }
      arr
    }
    _ => raise SexpError("Array::from_sexp: Expected List", path)
  }
}

///|
pub impl[T : ToSexp] ToSexp for FixedArray[T] with to_sexp(self) {
  Array::from_fixed_array(self) |> ToSexp::to_sexp
}

///|
pub impl[T : FromSexp] FromSexp for FixedArray[T] with from_sexp(sexp, path) {
  FromSexp::from_sexp(sexp, path) |> FixedArray::from_array
}

///|
pub impl[T : ToSexp, U : ToSexp] ToSexp for (T, U) with to_sexp(self) {
  List([self.0.to_sexp(), self.1.to_sexp()])
}

///|
pub impl[T : FromSexp, U : FromSexp] FromSexp for (T, U) with from_sexp(
  sexp,
  path,
) {
  match sexp {
    List(xs) if xs.length() == 2 =>
      (
        T::from_sexp(xs[0], path.add_index(0)),
        U::from_sexp(xs[1], path.add_index(1)),
      )
    _ => raise SexpError("Tuple::from_sexp: Expected List of length 2", path)
  }
}

///|
pub impl[T : ToSexp] ToSexp for T? with to_sexp(self) {
  match self {
    Some(value) => List([value.to_sexp()])
    None => List([])
  }
}

///|
pub impl[T : FromSexp] FromSexp for T? with from_sexp(sexp, path) {
  match sexp {
    List([]) => None
    List([x]) => Some(T::from_sexp(x, path.add_index(0)))
    _ =>
      raise SexpError("Option::from_sexp: Expected List of length 0 or 1", path)
  }
}

///|
pub impl[T : ToSexp, E : ToSexp] ToSexp for Result[T, E] with to_sexp(self) {
  match self {
    Ok(value) => List([Symbol(Symbol::new("ok")), value.to_sexp()])
    Err(err) => List([Symbol(Symbol::new("err")), err.to_sexp()])
  }
}

///|
pub impl[T : FromSexp, E : FromSexp] FromSexp for Result[T, E] with from_sexp(
  sexp,
  path,
) {
  guard sexp is List([tag, value]) else {
    raise SexpError("Result::from_sexp: Expected List of length 2", path)
  }
  guard tag is Symbol(sym) else {
    raise SexpError("Result::from_sexp: Expected Symbol tag", path.add_index(0))
  }
  match sym.to_string() {
    "ok" => Ok(T::from_sexp(value, path.add_key(sym)))
    "err" => Err(E::from_sexp(value, path.add_key(sym)))
    _ =>
      raise SexpError(
        "Result::from_sexp: Expected 'ok' or 'err' tag",
        path.add_index(0),
      )
  }
}

///|
pub impl[K : ToSexp, V : ToSexp] ToSexp for Map[K, V] with to_sexp(self) {
  let entries = []
  for key, value in self {
    entries.push(List([key.to_sexp(), value.to_sexp()]))
  }
  List(entries)
}

///|
pub impl[K : FromSexp + Eq + Hash, V : FromSexp] FromSexp for Map[K, V] with from_sexp(
  sexp,
  path,
) {
  match sexp {
    List(xs) => {
      let map = {}
      for i = 0; i < xs.length(); i = i + 1 {
        match xs[i] {
          List(entry) if entry.length() == 2 => {
            let key = K::from_sexp(entry[0], path.add_index(i).add_index(0))
            let value = match key.sexp_path_key() {
              Some(sym) => V::from_sexp(entry[1], path.add_key(sym))
              None => V::from_sexp(entry[1], path.add_index(i).add_index(1))
            }
            map[key] = value
          }
          _ =>
            raise SexpError(
              "Map::from_sexp: Expected List of length 2 for entry",
              path.add_index(i),
            )
        }
      }
      map
    }
    _ => raise SexpError("Map::from_sexp: Expected List", path)
  }
}

///|
pub impl ToSexp for BytesView with to_sexp(self) {
  let sb = StringBuilder::new()
  for byte in self {
    if byte is (' '..='~') && byte != '"' && byte != '\\' {
      sb.write_char(byte.to_char())
    } else {
      sb..write_string("\\x").write_string(byte.to_hex())
    }
  }
  String(sb.to_string())
}

///|
pub impl FromSexp for BytesView with from_sexp(sexp, path) {
  (FromSexp::from_sexp(sexp, path) : Bytes)
}

///|
pub impl ToSexp for Bytes with to_sexp(self) {
  ToSexp::to_sexp(self[:])
}

// Copied from core/json/from_json.mbt

///|
pub impl FromSexp for Bytes with from_sexp(sexp, path) {
  guard sexp is String(a) else {
    raise SexpError("Bytes::from_sexp: Expected string", path)
  }
  let buffer = @buffer.new(size_hint=a.length())
  for xs = a[:] {
    match xs {
      [] => ()
      [
        .. "\\x",
        '0'..='9'
        | 'a'..='f' as x,
        '0'..='9'
        | 'a'..='f' as y,
        .. rest,
      ] => {
        let upper = (x.to_int() & 0xF) + (x.to_int() >> 6) * 9
        let lower = (y.to_int() & 0xF) + (y.to_int() >> 6) * 9
        buffer.write_byte(((upper << 4) | lower).to_byte())
        continue rest
      }
      [' '..='~' as ch, .. rest] => {
        guard ch != '\\' && ch != '"' else {
          raise SexpError("Bytes::from_sexp: Invalid escape sequence", path)
        }
        buffer.write_byte(ch.to_uint().to_byte())
        continue rest
      }
      _ => raise SexpError("Bytes::from_sexp: Invalid byte sequence", path)
    }
  }
  buffer.to_bytes()
}

///|
pub impl ToSexp for Sexp with to_sexp(self) {
  self
}

///|
pub impl FromSexp for Sexp with from_sexp(sexp, _) {
  sexp
}