// 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 : Int) -> 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))])
}
///|
/// 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).
pub fn encode_file_with(file : File, exact_ints~ : Bool) -> 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 => encode_declaration_with(v, exact_ints~))
}),
),
("comments", encode_list(file.comments, n => encode_node(n, encode_string))),
])
}
///|
/// 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 {
match d {
FunctionDeclaration(f) =>
encode_typed("function", encode_function_with(f, exact_ints~))
AliasDeclaration(a) => encode_typed("typeAlias", encode_type_alias(a))
CustomTypeDeclaration(t) => encode_typed("typedecl", encode_type(t))
PortDeclaration(s) => encode_typed("port", encode_signature(s))
InfixDeclaration(i) => encode_typed("infix", encode_infix(i))
Destructuring(p, e) =>
encode_typed(
"destructuring",
obj([
("pattern", encode_node(p, v => encode_pattern_with(v, exact_ints~))),
(
"expression",
encode_node(e, v => encode_expression_with(v, exact_ints~)),
),
]),
)
}
}
///|
/// 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 {
obj([
("name", encode_node(s.name, encode_string)),
("typeAnnotation", encode_node(s.type_annotation, encode_type_annotation)),
])
}
///|
/// Elm.Syntax.TypeAlias.encode.
pub fn encode_type_alias(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", encode_node(a.type_annotation, encode_type_annotation)),
])
}
///|
/// Elm.Syntax.Type.encode.
pub fn encode_type(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, encode_value_constructor)),
),
])
}
///|
/// Elm.Syntax.Type.encodeValueConstructor.
pub fn encode_value_constructor(c : ValueConstructor) -> Json {
obj([
("name", encode_node(c.name, encode_string)),
(
"arguments",
encode_list(c.arguments, n => encode_node(n, encode_type_annotation)),
),
])
}
///|
/// Elm.Syntax.TypeAnnotation.encode.
pub fn encode_type_annotation(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",
encode_list(args, n => encode_node(n, encode_type_annotation)),
),
]),
)
Unit => encode_typed("unit", obj([]))
Tupled(values) =>
encode_typed(
"tupled",
obj([
(
"values",
encode_list(values, n => encode_node(n, encode_type_annotation)),
),
]),
)
FunctionTypeAnnotation(left, right) =>
encode_typed(
"function",
obj([
("left", encode_node(left, encode_type_annotation)),
("right", encode_node(right, encode_type_annotation)),
]),
)
Record(fields) =>
encode_typed("record", obj([("value", encode_record_definition(fields))]))
GenericRecord(name, fields) =>
encode_typed(
"genericRecord",
obj([
("name", encode_node(name, encode_string)),
("values", encode_node(fields, encode_record_definition)),
]),
)
}
}
///|
fn encode_record_definition(fields : RecordDefinition) -> Json {
encode_list(fields, n => encode_node(n, encode_record_field))
}
///|
fn encode_record_field(f : RecordField) -> Json {
obj([
("name", encode_node(f.name, encode_string)),
("typeAnnotation", encode_node(f.type_annotation, encode_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 {
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",
encode_list(ps, n => {
encode_node(n, v => encode_pattern_with(v, exact_ints~))
}),
),
]),
)
RecordPattern(names) =>
encode_typed(
"record",
obj([("value", encode_list(names, n => encode_node(n, encode_string)))]),
)
UnConsPattern(left, right) =>
encode_typed(
"uncons",
obj([
("left", encode_node(left, v => encode_pattern_with(v, exact_ints~))),
(
"right",
encode_node(right, v => encode_pattern_with(v, exact_ints~)),
),
]),
)
ListPattern(ps) =>
encode_typed(
"list",
obj([
(
"value",
encode_list(ps, n => {
encode_node(n, v => encode_pattern_with(v, exact_ints~))
}),
),
]),
)
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",
encode_list(ps, n => {
encode_node(n, v => encode_pattern_with(v, exact_ints~))
}),
),
]),
)
AsPattern(destructured, name) =>
encode_typed(
"as",
obj([
("name", encode_node(name, encode_string)),
(
"pattern",
encode_node(destructured, v => encode_pattern_with(v, exact_ints~)),
),
]),
)
// Tag spelling "parentisized" is elm-syntax's own (sic).
ParenthesizedPattern(inner) =>
encode_typed(
"parentisized",
obj([
(
"value",
encode_node(inner, v => encode_pattern_with(v, exact_ints~)),
),
]),
)
}
}
///|
fn encode_nested(n : Node[Expression], exact_ints~ : Bool) -> Json {
encode_node(n, v => encode_expression_with(v, exact_ints~))
}
///|
/// 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 {
match e {
UnitExpr => encode_typed("unit", Json::null())
Application(l) =>
encode_typed(
"application",
encode_list(l, v => encode_nested(v, exact_ints~)),
)
OperatorApplication(op, dir, left, right) =>
encode_typed(
"operatorapplication",
obj([
("operator", encode_string(op)),
("direction", encode_infix_direction(dir)),
("left", encode_nested(left, exact_ints~)),
("right", encode_nested(right, exact_ints~)),
]),
)
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", encode_nested(c, exact_ints~)),
("then", encode_nested(t, exact_ints~)),
("else", encode_nested(e, exact_ints~)),
]),
)
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", encode_nested(x, exact_ints~))
Literal(x) => encode_typed("literal", encode_string(x))
CharLiteral(c) => encode_typed("charLiteral", encode_char(c))
TupledExpression(xs) =>
encode_typed(
"tupled",
encode_list(xs, v => encode_nested(v, exact_ints~)),
)
ListExpr(xs) =>
encode_typed("list", encode_list(xs, v => encode_nested(v, exact_ints~)))
ParenthesizedExpression(x) =>
encode_typed("parenthesized", encode_nested(x, exact_ints~))
LetExpression(x) => encode_typed("let", encode_let_block(x, exact_ints~))
CaseExpression(x) => encode_typed("case", encode_case_block(x, exact_ints~))
LambdaExpression(x) => encode_typed("lambda", encode_lambda(x, exact_ints~))
RecordAccess(exp, name) =>
encode_typed(
"recordAccess",
obj([
("expression", encode_nested(exp, exact_ints~)),
("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 => encode_record_setter(v, exact_ints~))
}),
)
RecordUpdateExpression(name, updates) =>
encode_typed(
"recordUpdate",
obj([
("name", encode_node(name, encode_string)),
(
"updates",
encode_list(updates, n => {
encode_node(n, v => encode_record_setter(v, exact_ints~))
}),
),
]),
)
GLSLExpression(x) => encode_typed("glsl", encode_string(x))
}
}
///|
fn encode_let_block(b : LetBlock, exact_ints~ : Bool) -> Json {
obj([
(
"declarations",
encode_list(b.declarations, n => {
encode_node(n, v => encode_let_declaration(v, exact_ints~))
}),
),
("expression", encode_nested(b.expression, exact_ints~)),
])
}
///|
fn encode_record_setter(s : RecordSetter, exact_ints~ : Bool) -> Json {
obj([
("field", encode_node(s.field, encode_string)),
("expression", encode_nested(s.expression, exact_ints~)),
])
}
///|
fn encode_let_declaration(d : LetDeclaration, exact_ints~ : Bool) -> Json {
match d {
LetFunction(f) =>
encode_typed("function", encode_function_with(f, exact_ints~))
LetDestructuring(p, e) =>
encode_typed(
"destructuring",
obj([
("pattern", encode_node(p, v => encode_pattern_with(v, exact_ints~))),
("expression", encode_nested(e, exact_ints~)),
]),
)
}
}
///|
/// 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 {
obj([
(
"documentation",
encode_maybe(f.documentation, n => encode_node(n, encode_string)),
),
(
"signature",
encode_maybe(f.signature, n => encode_node(n, encode_signature)),
),
(
"declaration",
encode_node(f.declaration, v => {
encode_function_implementation(v, exact_ints~)
}),
),
])
}
///|
fn encode_function_implementation(
f : FunctionImplementation,
exact_ints~ : Bool,
) -> Json {
obj([
("name", encode_node(f.name, encode_string)),
(
"arguments",
encode_list(f.arguments, n => {
encode_node(n, v => encode_pattern_with(v, exact_ints~))
}),
),
("expression", encode_nested(f.expression, exact_ints~)),
])
}
///|
/// elm-syntax writes "cases" before "expression" here.
fn encode_case_block(b : CaseBlock, exact_ints~ : Bool) -> Json {
obj([
(
"cases",
encode_list(b.cases, c => {
obj([
(
"pattern",
encode_node(c.pattern, v => encode_pattern_with(v, exact_ints~)),
),
("expression", encode_nested(c.expression, exact_ints~)),
])
}),
),
("expression", encode_nested(b.expression, exact_ints~)),
])
}
///|
/// The Lambda field `args` is written under the key "patterns".
fn encode_lambda(l : Lambda, exact_ints~ : Bool) -> Json {
obj([
(
"patterns",
encode_list(l.args, n => {
encode_node(n, v => encode_pattern_with(v, exact_ints~))
}),
),
("expression", encode_nested(l.expression, exact_ints~)),
])
}
///|
/// Elm.Syntax.File.encode.
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)
}