// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Direct structural representation for debugging/diffing/pretty-printing.
///
// `pub` makes it readonly outside this package: it can be pattern-matched but
// not directly constructed. Use the smart constructors below.
//
// Design notes:
// - `Record` is encoded as `Record([RecordField(name, value), ...])` (not as an array
// of `(String, Repr)` pairs) to keep generic traversal/rewrite code simple:
// every tree edge is a `Repr`, so `children/with_children`, pruning, and diff
// can work uniformly across node kinds.
// - Labeled enum arguments are encoded as
// `Enum(name, [EnumLabeledArg(label, value), ...])`,
// which supports mixing positional and labeled args.
// - `Map` is encoded as `Map([MapEntry(key, value), ...])`, which is
// intended for "map-like" collections (MoonBit map literals like `{ k: v }`).
// - `Opaque` is intended for "container-like" wrappers that keep a type/tag but
// otherwise behave structurally through their children.
enum Repr {
UnitLit
/// Integer literal stored as string representation.
/// Covers Int16, Int, Int64, UInt16, UInt, UInt64, BigInt, etc.
Integer(String)
/// 64-bit floating-point literal leaf.
DoubleLit(Double)
/// 32-bit floating-point literal leaf.
FloatLit(Float)
/// Boolean literal leaf.
BoolLit(Bool)
/// Character literal leaf.
CharLit(Char)
/// String literal leaf.
StringLit(String)
/// Tuple node. Children are the tuple elements (`Tuple([])` is unit).
Tuple(Array[Repr])
/// Array container node.
Array(Array[Repr])
/// Record container node. Conventionally contains only `RecordField` children.
Record(Array[Repr])
/// Enum constructor/application node: `Enum(name, args)`.
Enum(String, Array[Repr])
/// Association/kv container for map literals.
Map(Array[Repr])
/// Record field node. Conventionally appears only under `Record`.
RecordField(String, Repr)
/// Labeled argument node. Conventionally appears only under `Enum`.
EnumLabeledArg(String, Repr)
/// Opaque wrapper node: prints as `` or `` and keeps children.
Opaque(String, Repr)
/// Pre-rendered leaf string (already formatted, no quoting/escaping applied).
Literal(String)
/// Key/value node. Conventionally appears only under `Map`.
MapEntry(Repr, Repr)
/// Pruned subtree marker used by depth-limited pretty-printing.
Omitted
}
///|
/// Child nodes of a `Repr` node.
///
/// Design notes:
/// - Leaves return `[]`; container nodes return their stored children.
/// - `RecordField`/`EnumLabeledArg` have one child; `MapEntry` has two.
/// - `children` and `with_children` form a partial lens for tree rewrites:
/// `self.with_children(self.children()) == self`.
fn Repr::children(self : Repr) -> Array[Repr] {
match self {
UnitLit
| Integer(_)
| DoubleLit(_)
| FloatLit(_)
| BoolLit(_)
| CharLit(_)
| StringLit(_)
| Literal(_)
| Omitted => []
Tuple(xs) | Array(xs) | Record(xs) | Enum(_, xs) | Map(xs) => xs
Opaque(_, value) | RecordField(_, value) | EnumLabeledArg(_, value) =>
[value]
MapEntry(key, value) => [key, value]
}
}
///|
/// Rebuild a `Repr` node with a new child list (payload is preserved).
///
/// Notes:
/// - Leaf nodes ignore `children` and return themselves.
/// - `RecordField`/`EnumLabeledArg` expect exactly one child and fall back to
/// `RecordField(name, Omitted)`/`EnumLabeledArg(label, Omitted)`.
/// - `MapEntry` expects exactly two children and falls back to `MapEntry(Omitted, Omitted)`.
/// - This is intentionally not a total inverse of `children`: invalid arity is
/// clamped to keep the tree well-formed for generic traversal.
fn Repr::with_children(self : Repr, children : Array[Repr]) -> Repr {
match self {
UnitLit
| Integer(_)
| DoubleLit(_)
| FloatLit(_)
| BoolLit(_)
| CharLit(_)
| StringLit(_)
| Literal(_)
| Omitted => self
Tuple(_) => Tuple(children)
Array(_) => Array(children)
Record(_) => Record(children)
Enum(name, _) => Enum(name, children)
Map(_) => Map(children)
RecordField(name, _) =>
match children {
[value] => RecordField(name, value)
_ => RecordField(name, Omitted)
}
EnumLabeledArg(label, _) =>
match children {
[value] => EnumLabeledArg(label, value)
_ => EnumLabeledArg(label, Omitted)
}
MapEntry(_, _) =>
match children {
[key, value] => MapEntry(key, value)
_ => MapEntry(Omitted, Omitted)
}
Opaque(name, _) =>
match children {
[value] => Opaque(name, value)
_ => Opaque(name, Omitted)
}
}
}
///|
/// Traverse a `Repr` tree and rewrite each node with `f`.
///
/// `f` runs after children are traversed (post-order), so it sees rewritten
/// children and can hide fields by rewriting `RecordField`/`EnumLabeledArg` nodes.
#doc(hidden)
pub fn Repr::traverse(self : Repr, f : (Repr) -> Repr) -> Repr {
fn go(node : Repr) -> Repr {
let children = node.children()
let next_children = children.map(child => go(child))
f(node.with_children(next_children))
}
go(self)
}
///|
/// Converts any value implementing `Debug` into a `Repr`.
///
/// This is the constructor of `Repr`, so it is written `Repr(value)`, and it is
/// re-exported by the prelude — no import is needed.
///
/// Parameters:
///
/// * `value` : The value to convert.
///
/// Returns the `Repr` representation of `value`.
///
/// Example:
///
/// ```mbt check
/// test {
/// inspect(Repr(42), content="42")
/// inspect(Repr("hello"), content="\"hello\"")
/// inspect(Repr([1, 2]), content="[1, 2]")
/// }
/// ```
pub fn[T : Debug] Repr::Repr(value : T) -> Repr {
Debug::to_repr(value)
}
///|
/// Construct a `Integer` leaf.
#doc(hidden)
pub fn Repr::integer(x : String) -> Repr {
Integer(x)
}
///|
/// Construct a `DoubleLit` leaf.
#doc(hidden)
pub fn Repr::double(x : Double) -> Repr {
DoubleLit(x)
}
///|
/// Construct a `FloatLit` leaf.
#doc(hidden)
pub fn Repr::float(x : Float) -> Repr {
FloatLit(x)
}
///|
/// Construct a `BoolLit` leaf.
#doc(hidden)
pub fn Repr::bool(x : Bool) -> Repr {
BoolLit(x)
}
///|
/// Construct a `CharLit` leaf.
#doc(hidden)
pub fn Repr::char(x : Char) -> Repr {
CharLit(x)
}
///|
/// Construct a `StringLit` leaf.
#doc(hidden)
pub fn Repr::string(x : String) -> Repr {
StringLit(x)
}
///|
/// Construct a `Tuple` node from pre-built child `Repr`s.
#doc(hidden)
pub fn Repr::tuple(children : Array[Repr]) -> Repr {
Tuple(children)
}
///|
/// Construct an `Array` node from pre-built child `Repr`s.
#doc(hidden)
pub fn Repr::array(children : Array[Repr]) -> Repr {
Array(children)
}
///|
/// Construct a `Record` node from pre-built child `Repr`s.
#doc(hidden)
pub fn Repr::record(fields : Map[String, Repr]) -> Repr {
Record([ for name, value in fields => RecordField(name, value) ])
}
///|
/// Construct an `Opaque(name, children)` node.
///
/// This is useful for values where you want to keep a tag/type name but still
/// show a structural summary through children (e.g. ``).
#doc(hidden)
pub fn Repr::opaque_(name : String, children : Repr) -> Repr {
Opaque(name, children)
}
///|
/// Construct a `Literal(value)` leaf (already formatted).
#doc(hidden)
pub fn Repr::literal(value : String) -> Repr {
Literal(value)
}
///|
/// Construct a `Map` node from key/value `Repr` pairs (for map literals).
#doc(hidden)
pub fn Repr::map(contents : Array[(Repr, Repr)]) -> Repr {
Map(
contents.map(pair => {
let (k, v) = pair
MapEntry(k, v)
}),
)
}
///|
/// Construct an `Omitted` marker node.
#doc(hidden)
pub fn Repr::omitted() -> Repr {
Omitted
}
///|
/// Function `unit`.
#doc(hidden)
pub fn Repr::unit() -> Repr {
UnitLit
}
///|
/// Construct an `Enum(name, args)` node for enum/constructor applications.
///
/// Use `None` for positional arguments and `Some(label)` for labeled ones.
#doc(hidden)
pub fn Repr::ctor(name : String, args : Array[(String?, Repr)]) -> Repr {
Enum(
name,
args.map(arg => {
let (label, value) = arg
match label {
None => value
Some(label) => EnumLabeledArg(label, value)
}
}),
)
}
///|
/// A shallow copy of `self` containing only its "label" (children replaced).
///
/// This is used by diff/pretty-print when the structure is preserved but
/// children are rendered separately.
fn Repr::shallow(self : Repr) -> Repr {
match self {
UnitLit
| Integer(_)
| DoubleLit(_)
| FloatLit(_)
| BoolLit(_)
| CharLit(_)
| StringLit(_)
| Literal(_)
| Omitted => self
Tuple(_) => Tuple([])
Array(_) => Array([])
Record(_) => Record([])
Enum(name, _) => Enum(name, [])
Opaque(name, _) => Opaque(name, Omitted)
Map(_) => Map([])
RecordField(name, _) => RecordField(name, Omitted)
EnumLabeledArg(label, _) => EnumLabeledArg(label, Omitted)
MapEntry(_, _) => MapEntry(Omitted, Omitted)
}
}