// JSON encoders mirroring the `encode` functions of stil4m/elm-syntax 7.3.9.
// Object keys are inserted in the same order as elm-syntax writes them, so
// `Json::stringify` of the result has the same key order as the Elm output.

///|
/// Elm.Json.Util.encodeTyped: `{"type": tag, tag: value}`.
fn encode_typed(tag : String, value : Json) -> Json {
  Json::object(Map([("type", Json::string(tag)), (tag, value)]))
}

///|
fn obj(fields : Array[(String, Json)]) -> Json {
  Json::object(Map(fields))
}

///|
fn encode_int(i : Int32) -> Json {
  Json::number(i.to_double())
}

///|
fn encode_int64(i : Int64, exact_ints~ : Bool) -> Json {
  if exact_ints {
    Json::number(i.to_double(), repr=i.to_string())
  } else {
    Json::number(i.to_double())
  }
}

///|
fn encode_string(s : String) -> Json {
  Json::string(s)
}

///|
/// Elm `String.fromChar c`.
fn encode_char(c : Char) -> Json {
  Json::string(c.to_string())
}

///|
fn[T] encode_list(xs : ArrayView[T], f : (T) -> Json) -> Json {
  Json::array(xs.map(f))
}

///|
/// `Maybe.map f m |> Maybe.withDefault JE.null`.
fn[T] encode_maybe(m : T?, f : (T) -> Json) -> Json {
  match m {
    Some(x) => f(x)
    None => Json::null()
  }
}

///|
/// Elm.Syntax.Range.encode: `[startRow, startColumn, endRow, endColumn]`.
pub fn encode_range(r : Range) -> Json {
  Json::array([
    encode_int(r.start.row),
    encode_int(r.start.column),
    encode_int(r.end.row),
    encode_int(r.end.column),
  ])
}

///|
/// Elm.Syntax.Node.encode: `{"range": ..., "value": ...}`.
pub fn[T] encode_node(n : Node[T], f : (T) -> Json) -> Json {
  obj([("range", encode_range(n.range)), ("value", f(n.value))])
}

///|
/// A place in a JSON object that a nested node fills later. `Map` in `Json`
/// is mutable and keeps its insertion order, so the slot is filled in place
/// and the key order stays elm-syntax's.
priv enum Slot {
  Key(Map[String, Json], String)
}

///|
fn Slot::fill(self : Slot, value : Json) -> Unit {
  match self {
    Key(m, key) => m[key] = value
  }
}

///|
/// A nested node whose JSON is still to be written into its slot.
priv enum Pending {
  E(Slot, Expression)
  P(Slot, Pattern)
  T(Slot, TypeAnnotation)
  F(Slot, Function)
}

///|
/// The encoder state. The encoder does not recurse per AST level: each
/// encoder writes its own object with `null` in the slot of every nested
/// expression, pattern, type or function and queues the node; `run` pops
/// the queue until it is empty. Deep trees (long chains, many nested
/// parentheses) encode on every target without a stack limit.
priv struct Enc {
  exact_ints : Bool
  pending : Array[Pending]
}

///|
fn Enc::new(exact_ints : Bool) -> Enc {
  { exact_ints, pending: [], }
}

///|
/// Encode every pending node (and the nodes they queue) into its slot.
fn Enc::run(self : Enc) -> Unit {
  while self.pending.pop() is Some(item) {
    match item {
      E(slot, e) => slot.fill(self.expression(e))
      P(slot, p) => slot.fill(self.pattern(p))
      T(slot, t) => slot.fill(self.type_annotation(t))
      F(slot, f) => slot.fill(self.function(f))
    }
  }
}

///|
/// `encode_node` for a nested expression: the node's object, with its
/// `value` queued.
fn Enc::expr_node(self : Enc, node : Node[Expression]) -> Json {
  let m : Map[String, Json] = Map([
    ("range", encode_range(node.range)),
    ("value", Json::null()),
  ])
  self.pending.push(E(Key(m, "value"), node.value))
  Json::object(m)
}

///|
fn Enc::pattern_node(self : Enc, node : Node[Pattern]) -> Json {
  let m : Map[String, Json] = Map([
    ("range", encode_range(node.range)),
    ("value", Json::null()),
  ])
  self.pending.push(P(Key(m, "value"), node.value))
  Json::object(m)
}

///|
fn Enc::type_node(self : Enc, node : Node[TypeAnnotation]) -> Json {
  let m : Map[String, Json] = Map([
    ("range", encode_range(node.range)),
    ("value", Json::null()),
  ])
  self.pending.push(T(Key(m, "value"), node.value))
  Json::object(m)
}

///|
fn Enc::expr_list(self : Enc, xs : ArrayView[Node[Expression]]) -> Json {
  let arr : Array[Json] = Array::make(xs.length(), Json::null())
  for i, x in xs {
    arr[i] = self.expr_node(x)
  }
  Json::array(arr)
}

///|
fn Enc::pattern_list(self : Enc, xs : ArrayView[Node[Pattern]]) -> Json {
  let arr : Array[Json] = Array::make(xs.length(), Json::null())
  for i, x in xs {
    arr[i] = self.pattern_node(x)
  }
  Json::array(arr)
}

///|
fn Enc::type_list(self : Enc, xs : ArrayView[Node[TypeAnnotation]]) -> Json {
  let arr : Array[Json] = Array::make(xs.length(), Json::null())
  for i, x in xs {
    arr[i] = self.type_node(x)
  }
  Json::array(arr)
}

///|
/// Elm.Syntax.ModuleName.encode.
pub fn encode_module_name(m : ModuleName) -> Json {
  encode_list(m, encode_string)
}

///|
/// Elm.Syntax.File.encode.
/// With `exact_ints`, Int literals are written with their exact digits
/// (elm-syntax writes the nearest Double, which differs above 2^53).
/// With `exact_ints=false` the result is the same as `encode_file`.
///
/// ```mbt check
/// test {
///   let source = @scanner.SourceText::new(
///     "module Main exposing (..)\n\nbig =\n    9007199254740993\n",
///   )
///   let result = @parser.parse_module(source, @scanner.DefaultScanner::new())
///   guard result.ast is Some(file) else { fail("no AST") }
///   // elm-syntax writes the nearest Double.
///   let nearest = @ast.encode_file(file).stringify()
///   inspect(nearest.contains("9007199254740992"), content="true")
///   // exact_ints keeps the digits of the literal.
///   let exact = @ast.encode_file_with(file, exact_ints=true).stringify()
///   inspect(exact.contains("9007199254740993"), content="true")
///   // The decoder reads both forms; the exact form gives the literal back.
///   inspect(@ast.decode_file(@json.parse(exact)) == file, content="true")
/// }
/// ```
pub fn encode_file_with(file : File, exact_ints~ : Bool) -> Json {
  let enc = Enc::new(exact_ints)
  let json = obj([
    ("moduleDefinition", encode_node(file.module_definition, encode_module)),
    ("imports", encode_list(file.imports, n => encode_node(n, encode_import))),
    (
      "declarations",
      encode_list(file.declarations, n => {
        encode_node(n, v => enc.declaration(v))
      }),
    ),
    ("comments", encode_list(file.comments, n => encode_node(n, encode_string))),
  ])
  enc.run()
  json
}

///|
/// Elm.Syntax.Module.encode.
pub fn encode_module(m : Module) -> Json {
  match m {
    NormalModule(d) => encode_typed("normal", encode_default_module_data(d))
    PortModule(d) => encode_typed("port", encode_default_module_data(d))
    EffectModule(d) => encode_typed("effect", encode_effect_module_data(d))
  }
}

///|
fn encode_default_module_data(d : DefaultModuleData) -> Json {
  obj([
    ("moduleName", encode_node(d.module_name, encode_module_name)),
    ("exposingList", encode_node(d.exposing_list, encode_exposing)),
  ])
}

///|
fn encode_effect_module_data(d : EffectModuleData) -> Json {
  obj([
    ("moduleName", encode_node(d.module_name, encode_module_name)),
    ("exposingList", encode_node(d.exposing_list, encode_exposing)),
    ("command", encode_maybe(d.command, n => encode_node(n, encode_string))),
    (
      "subscription",
      encode_maybe(d.subscription, n => encode_node(n, encode_string)),
    ),
  ])
}

///|
/// Elm.Syntax.Exposing.encode.
pub fn encode_exposing(e : Exposing) -> Json {
  match e {
    All(r) => encode_typed("all", encode_range(r))
    Explicit(l) =>
      encode_typed(
        "explicit",
        encode_list(l, n => encode_node(n, encode_top_level_expose)),
      )
  }
}

///|
/// Elm.Syntax.Exposing.encodeTopLevelExpose (without the Node wrapper).
pub fn encode_top_level_expose(t : TopLevelExpose) -> Json {
  match t {
    InfixExpose(x) => encode_typed("infix", obj([("name", encode_string(x))]))
    FunctionExpose(x) =>
      encode_typed("function", obj([("name", encode_string(x))]))
    TypeOrAliasExpose(x) =>
      encode_typed("typeOrAlias", obj([("name", encode_string(x))]))
    TypeExpose(t) => encode_typed("typeexpose", encode_exposed_type(t))
  }
}

///|
fn encode_exposed_type(t : ExposedType) -> Json {
  obj([
    ("name", encode_string(t.name)),
    ("open", encode_maybe(t.open, encode_range)),
  ])
}

///|
/// Elm.Syntax.Import.encode.
pub fn encode_import(i : Import) -> Json {
  obj([
    ("moduleName", encode_node(i.module_name, encode_module_name)),
    (
      "moduleAlias",
      encode_maybe(i.module_alias, n => encode_node(n, encode_module_name)),
    ),
    (
      "exposingList",
      encode_maybe(i.exposing_list, n => encode_node(n, encode_exposing)),
    ),
  ])
}

///|
/// Elm.Syntax.Declaration.encode.
/// With `exact_ints`, Int literals are written with their exact digits
/// (elm-syntax writes the nearest Double, which differs above 2^53).
pub fn encode_declaration_with(d : Declaration, exact_ints~ : Bool) -> Json {
  let enc = Enc::new(exact_ints)
  let json = enc.declaration(d)
  enc.run()
  json
}

///|
fn Enc::declaration(self : Enc, d : Declaration) -> Json {
  match d {
    FunctionDeclaration(f) => {
      let m : Map[String, Json] = Map([
        ("type", Json::string("function")),
        ("function", Json::null()),
      ])
      self.pending.push(F(Key(m, "function"), f))
      Json::object(m)
    }
    AliasDeclaration(a) => encode_typed("typeAlias", self.type_alias(a))
    CustomTypeDeclaration(t) => encode_typed("typedecl", self.custom_type(t))
    PortDeclaration(s) => encode_typed("port", self.signature(s))
    InfixDeclaration(i) => encode_typed("infix", encode_infix(i))
    Destructuring(p, e) =>
      encode_typed(
        "destructuring",
        obj([
          ("pattern", self.pattern_node(p)),
          ("expression", self.expr_node(e)),
        ]),
      )
  }
}

///|
/// Elm.Syntax.Infix.encode.
pub fn encode_infix(i : Infix) -> Json {
  obj([
    ("direction", encode_node(i.direction, encode_infix_direction)),
    ("precedence", encode_node(i.precedence, encode_int)),
    ("operator", encode_node(i.operator, encode_string)),
    ("function", encode_node(i.function, encode_string)),
  ])
}

///|
/// Elm.Syntax.Infix.encodeDirection.
pub fn encode_infix_direction(d : InfixDirection) -> Json {
  match d {
    Left => Json::string("left")
    Right => Json::string("right")
    Non => Json::string("non")
  }
}

///|
/// Elm.Syntax.Signature.encode.
pub fn encode_signature(s : Signature) -> Json {
  let enc = Enc::new(false)
  let json = enc.signature(s)
  enc.run()
  json
}

///|
fn Enc::signature(self : Enc, s : Signature) -> Json {
  obj([
    ("name", encode_node(s.name, encode_string)),
    ("typeAnnotation", self.type_node(s.type_annotation)),
  ])
}

///|
/// Elm.Syntax.TypeAlias.encode.
pub fn encode_type_alias(a : TypeAlias) -> Json {
  let enc = Enc::new(false)
  let json = enc.type_alias(a)
  enc.run()
  json
}

///|
fn Enc::type_alias(self : Enc, a : TypeAlias) -> Json {
  obj([
    (
      "documentation",
      encode_maybe(a.documentation, n => encode_node(n, encode_string)),
    ),
    ("name", encode_node(a.name, encode_string)),
    ("generics", encode_list(a.generics, n => encode_node(n, encode_string))),
    ("typeAnnotation", self.type_node(a.type_annotation)),
  ])
}

///|
/// Elm.Syntax.Type.encode.
pub fn encode_type(t : Type) -> Json {
  let enc = Enc::new(false)
  let json = enc.custom_type(t)
  enc.run()
  json
}

///|
fn Enc::custom_type(self : Enc, t : Type) -> Json {
  obj([
    (
      "documentation",
      encode_maybe(t.documentation, n => encode_node(n, encode_string)),
    ),
    ("name", encode_node(t.name, encode_string)),
    ("generics", encode_list(t.generics, n => encode_node(n, encode_string))),
    (
      "constructors",
      encode_list(t.constructors, n => {
        encode_node(n, v => self.value_constructor(v))
      }),
    ),
  ])
}

///|
/// Elm.Syntax.Type.encodeValueConstructor.
pub fn encode_value_constructor(c : ValueConstructor) -> Json {
  let enc = Enc::new(false)
  let json = enc.value_constructor(c)
  enc.run()
  json
}

///|
fn Enc::value_constructor(self : Enc, c : ValueConstructor) -> Json {
  obj([
    ("name", encode_node(c.name, encode_string)),
    ("arguments", self.type_list(c.arguments)),
  ])
}

///|
/// Elm.Syntax.TypeAnnotation.encode.
pub fn encode_type_annotation(t : TypeAnnotation) -> Json {
  let enc = Enc::new(false)
  let json = enc.type_annotation(t)
  enc.run()
  json
}

///|
/// The type's own object; its nested types are queued.
fn Enc::type_annotation(self : Enc, t : TypeAnnotation) -> Json {
  match t {
    GenericType(name) =>
      encode_typed("generic", obj([("value", encode_string(name))]))
    Typed(module_name_and_name, args) =>
      encode_typed(
        "typed",
        obj([
          (
            "moduleNameAndName",
            encode_node(module_name_and_name, mn => {
              obj([
                ("moduleName", encode_module_name(mn.0)),
                ("name", encode_string(mn.1)),
              ])
            }),
          ),
          ("args", self.type_list(args)),
        ]),
      )
    Unit => encode_typed("unit", obj([]))
    Tupled(values) =>
      encode_typed("tupled", obj([("values", self.type_list(values))]))
    FunctionTypeAnnotation(left, right) =>
      encode_typed(
        "function",
        obj([("left", self.type_node(left)), ("right", self.type_node(right))]),
      )
    Record(fields) =>
      encode_typed("record", obj([("value", self.record_definition(fields))]))
    GenericRecord(name, fields) =>
      encode_typed(
        "genericRecord",
        obj([
          ("name", encode_node(name, encode_string)),
          ("values", encode_node(fields, v => self.record_definition(v))),
        ]),
      )
  }
}

///|
fn Enc::record_definition(self : Enc, fields : RecordDefinition) -> Json {
  encode_list(fields, n => encode_node(n, v => self.record_field(v)))
}

///|
fn Enc::record_field(self : Enc, f : RecordField) -> Json {
  obj([
    ("name", encode_node(f.name, encode_string)),
    ("typeAnnotation", self.type_node(f.type_annotation)),
  ])
}

///|
/// Elm.Syntax.Pattern.encode.
/// With `exact_ints`, Int literals are written with their exact digits
/// (elm-syntax writes the nearest Double, which differs above 2^53).
pub fn encode_pattern_with(p : Pattern, exact_ints~ : Bool) -> Json {
  let enc = Enc::new(exact_ints)
  let json = enc.pattern(p)
  enc.run()
  json
}

///|
/// The pattern's own object; its nested patterns are queued.
fn Enc::pattern(self : Enc, p : Pattern) -> Json {
  let exact_ints = self.exact_ints
  match p {
    AllPattern => encode_typed("all", obj([]))
    UnitPattern => encode_typed("unit", obj([]))
    CharPattern(c) => encode_typed("char", obj([("value", encode_char(c))]))
    StringPattern(s) =>
      encode_typed("string", obj([("value", encode_string(s))]))
    HexPattern(h) =>
      encode_typed("hex", obj([("value", encode_int64(h, exact_ints~))]))
    IntPattern(i) =>
      encode_typed("int", obj([("value", encode_int64(i, exact_ints~))]))
    FloatPattern(f) => encode_typed("float", obj([("value", Json::number(f))]))
    TuplePattern(ps) =>
      encode_typed("tuple", obj([("value", self.pattern_list(ps))]))
    RecordPattern(names) =>
      encode_typed(
        "record",
        obj([("value", encode_list(names, n => encode_node(n, encode_string)))]),
      )
    UnConsPattern(left, right) =>
      encode_typed(
        "uncons",
        obj([
          ("left", self.pattern_node(left)),
          ("right", self.pattern_node(right)),
        ]),
      )
    ListPattern(ps) =>
      encode_typed("list", obj([("value", self.pattern_list(ps))]))
    VarPattern(name) =>
      encode_typed("var", obj([("value", encode_string(name))]))
    NamedPattern(qualified, ps) =>
      encode_typed(
        "named",
        obj([
          (
            "qualified",
            obj([
              ("moduleName", encode_module_name(qualified.module_name)),
              ("name", encode_string(qualified.name)),
            ]),
          ),
          ("patterns", self.pattern_list(ps)),
        ]),
      )
    AsPattern(destructured, name) =>
      encode_typed(
        "as",
        obj([
          ("name", encode_node(name, encode_string)),
          ("pattern", self.pattern_node(destructured)),
        ]),
      )
    // Tag spelling "parentisized" is elm-syntax's own (sic).
    ParenthesizedPattern(inner) =>
      encode_typed("parentisized", obj([("value", self.pattern_node(inner))]))
  }
}

///|
/// Elm.Syntax.Expression.encode.
/// With `exact_ints`, Int literals are written with their exact digits
/// (elm-syntax writes the nearest Double, which differs above 2^53).
pub fn encode_expression_with(e : Expression, exact_ints~ : Bool) -> Json {
  let enc = Enc::new(exact_ints)
  let json = enc.expression(e)
  enc.run()
  json
}

///|
/// The expression's own object; its nested nodes are queued.
fn Enc::expression(self : Enc, e : Expression) -> Json {
  let exact_ints = self.exact_ints
  match e {
    UnitExpr => encode_typed("unit", Json::null())
    Application(l) => encode_typed("application", self.expr_list(l))
    OperatorApplication(op, dir, left, right) =>
      encode_typed(
        "operatorapplication",
        obj([
          ("operator", encode_string(op)),
          ("direction", encode_infix_direction(dir)),
          ("left", self.expr_node(left)),
          ("right", self.expr_node(right)),
        ]),
      )
    FunctionOrValue(module_name, name) =>
      encode_typed(
        "functionOrValue",
        obj([
          ("moduleName", encode_module_name(module_name)),
          ("name", encode_string(name)),
        ]),
      )
    IfBlock(c, t, e) =>
      encode_typed(
        "ifBlock",
        obj([
          ("clause", self.expr_node(c)),
          ("then", self.expr_node(t)),
          ("else", self.expr_node(e)),
        ]),
      )
    PrefixOperator(x) => encode_typed("prefixoperator", encode_string(x))
    Operator(x) => encode_typed("operator", encode_string(x))
    Hex(h) => encode_typed("hex", encode_int64(h, exact_ints~))
    Integer(x) => encode_typed("integer", encode_int64(x, exact_ints~))
    // JSON has no infinity or NaN; elm-syntax writes null.
    Floatable(x) =>
      encode_typed(
        "float",
        if x.is_inf() || x.is_nan() {
          Json::null()
        } else {
          Json::number(x)
        },
      )
    Negation(x) => encode_typed("negation", self.expr_node(x))
    Literal(x) => encode_typed("literal", encode_string(x))
    CharLiteral(c) => encode_typed("charLiteral", encode_char(c))
    TupledExpression(xs) => encode_typed("tupled", self.expr_list(xs))
    ListExpr(xs) => encode_typed("list", self.expr_list(xs))
    ParenthesizedExpression(x) =>
      encode_typed("parenthesized", self.expr_node(x))
    LetExpression(x) => encode_typed("let", self.let_block(x))
    CaseExpression(x) => encode_typed("case", self.case_block(x))
    LambdaExpression(x) => encode_typed("lambda", self.lambda(x))
    RecordAccess(exp, name) =>
      encode_typed(
        "recordAccess",
        obj([
          ("expression", self.expr_node(exp)),
          ("name", encode_node(name, encode_string)),
        ]),
      )
    RecordAccessFunction(x) =>
      encode_typed("recordAccessFunction", encode_string(x))
    RecordExpr(xs) =>
      encode_typed(
        "record",
        encode_list(xs, n => encode_node(n, v => self.record_setter(v))),
      )
    RecordUpdateExpression(name, updates) =>
      encode_typed(
        "recordUpdate",
        obj([
          ("name", encode_node(name, encode_string)),
          (
            "updates",
            encode_list(updates, n => encode_node(n, v => self.record_setter(v))),
          ),
        ]),
      )
    GLSLExpression(x) => encode_typed("glsl", encode_string(x))
  }
}

///|
fn Enc::let_block(self : Enc, b : LetBlock) -> Json {
  obj([
    (
      "declarations",
      encode_list(b.declarations, n => {
        encode_node(n, v => self.let_declaration(v))
      }),
    ),
    ("expression", self.expr_node(b.expression)),
  ])
}

///|
fn Enc::record_setter(self : Enc, s : RecordSetter) -> Json {
  obj([
    ("field", encode_node(s.field, encode_string)),
    ("expression", self.expr_node(s.expression)),
  ])
}

///|
fn Enc::let_declaration(self : Enc, d : LetDeclaration) -> Json {
  match d {
    LetFunction(f) => {
      let m : Map[String, Json] = Map([
        ("type", Json::string("function")),
        ("function", Json::null()),
      ])
      self.pending.push(F(Key(m, "function"), f))
      Json::object(m)
    }
    LetDestructuring(p, e) =>
      encode_typed(
        "destructuring",
        obj([
          ("pattern", self.pattern_node(p)),
          ("expression", self.expr_node(e)),
        ]),
      )
  }
}

///|
/// Elm.Syntax.Expression.encodeFunction.
/// With `exact_ints`, Int literals are written with their exact digits
/// (elm-syntax writes the nearest Double, which differs above 2^53).
pub fn encode_function_with(f : Function, exact_ints~ : Bool) -> Json {
  let enc = Enc::new(exact_ints)
  let json = enc.function(f)
  enc.run()
  json
}

///|
fn Enc::function(self : Enc, f : Function) -> Json {
  obj([
    (
      "documentation",
      encode_maybe(f.documentation, n => encode_node(n, encode_string)),
    ),
    (
      "signature",
      encode_maybe(f.signature, n => encode_node(n, v => self.signature(v))),
    ),
    (
      "declaration",
      encode_node(f.declaration, v => self.function_implementation(v)),
    ),
  ])
}

///|
fn Enc::function_implementation(self : Enc, f : FunctionImplementation) -> Json {
  obj([
    ("name", encode_node(f.name, encode_string)),
    ("arguments", self.pattern_list(f.arguments)),
    ("expression", self.expr_node(f.expression)),
  ])
}

///|
/// elm-syntax writes "cases" before "expression" here.
fn Enc::case_block(self : Enc, b : CaseBlock) -> Json {
  obj([
    (
      "cases",
      encode_list(b.cases, c => {
        obj([
          ("pattern", self.pattern_node(c.pattern)),
          ("expression", self.expr_node(c.expression)),
        ])
      }),
    ),
    ("expression", self.expr_node(b.expression)),
  ])
}

///|
/// The Lambda field `args` is written under the key "patterns".
fn Enc::lambda(self : Enc, l : Lambda) -> Json {
  obj([
    ("patterns", self.pattern_list(l.args)),
    ("expression", self.expr_node(l.expression)),
  ])
}

///|
/// Elm.Syntax.File.encode.
///
/// The JSON is the JSON that elm-syntax 7.3.9 writes for the same file, with
/// the object keys in elm-syntax's order. The parity check compares the two
/// on a corpus of real packages. An Int literal
/// above 2^53 becomes the nearest Double, as in elm-syntax; use
/// `encode_file_with(file, exact_ints=true)` to keep its digits.
///
/// ```mbt check
/// test {
///   let source = @scanner.SourceText::new("module Main exposing (x)\n\nx = 1\n")
///   let result = @parser.parse_module(source, @scanner.DefaultScanner::new())
///   guard result.ast is Some(file) else { fail("no AST") }
///   let json = @ast.encode_file(file)
///   guard json is { "declarations": [declaration], .. } else {
///     fail("unexpected shape")
///   }
///   inspect(
///     declaration.stringify(),
///     content=(
///       #|{"range":[3,1,3,6],"value":{"type":"function","function":{"documentation":null,"signature":null,"declaration":{"range":[3,1,3,6],"value":{"name":{"range":[3,1,3,2],"value":"x"},"arguments":[],"expression":{"range":[3,5,3,6],"value":{"type":"integer","integer":1}}}}}}}
///     ),
///   )
/// }
/// ```
pub fn encode_file(file : File) -> Json {
  encode_file_with(file, exact_ints=false)
}

///|
/// Elm.Syntax.Declaration.encode.
pub fn encode_declaration(d : Declaration) -> Json {
  encode_declaration_with(d, exact_ints=false)
}

///|
/// Elm.Syntax.Pattern.encode.
pub fn encode_pattern(p : Pattern) -> Json {
  encode_pattern_with(p, exact_ints=false)
}

///|
/// Elm.Syntax.Expression.encode.
pub fn encode_expression(e : Expression) -> Json {
  encode_expression_with(e, exact_ints=false)
}

///|
/// Elm.Syntax.Expression.encodeFunction.
pub fn encode_function(f : Function) -> Json {
  encode_function_with(f, exact_ints=false)
}