Sign in

    #The Ion data model (moonrockz/ion/ion)

    The core package of moonrockz/ion: the thirteen Ion types and the values that hold them, with no dependency on either encoding. The text and binary codecs, Ion Hash, JSON conversion, and Ion Schema all build on it.

    • Ion is the node type: an IonValueKind plus an ordered list of annotations. IonValue remains an alias for the same type. Build values with Ion(value) and read them with kind(). Explicit helpers such as Ion::typed_null, Ion::sexp and Ion::from_fields select Ion-specific forms.
    • Decimal and integers are exact (BigInt coefficients), so precision and negative zero survive every round trip.
    • Timestamp keeps the components it was given; its precision follows from which ones are present.
    • SymbolToken has optional text and an optional symbol ID, and SymbolTable maps IDs to text as a stream's symbol tables declare them.
    • IonVisitor and IonFold traverse a value depth-first.
    • Every failure is an IonError: Syntax, DataModel, Unsupported, or FuelExhausted.
    • Nesting is unrestricted by default. Readers and schema operations accept optional Fuel::Limited(Budget(...)) for defensive coding. Fuel bounds open nested operations, with siblings reusing the enclosing budget. Exhaustion carries the budget, operation and location.
    • Readers, equality, folds, visitors, writers, events, Ion Hash and JSON conversion keep a bounded machine stack at any nesting depth.

    #Building values

    Values are immutable. Equivalence follows the Ion data model: annotations count, struct fields form an unordered multiset, and all NaNs are equivalent.

    Ion(value) uses the open ToIon trait. Integers retain their full precision, floating-point values become Ion floats, and strings become Ion strings. Arrays become lists, string-keyed maps become structs in iteration order, bytes become blobs, and unit becomes untyped null. Decimals, timestamps and symbol tokens keep their Ion types. Constructing from an existing Ion value preserves its annotations. Core Json values follow the JSON-to-Ion number mapping documented in the JSON package.

    The short helpers decimal, timestamp and symbol construct Decimal, Timestamp and SymbolToken payloads. Wrap a payload with Ion(...) to construct an Ion value. The field helper constructs a Field from a name and an Ion value.

    ///|
    test "construct Ion values from MoonBit values" {
    let counts : @ion.IonValue = @ion.Ion(Map([("counts", [1, 2, 3])]))
    inspect(@text.write_ion(counts), content="{counts: [1, 2, 3]}")
    let json = @ion.Ion(Json({ "ready": true }))
    inspect(@text.write_ion(json), content="{ready: true}")
    let decimal = @ion.decimal(123N, -2)
    inspect(@text.write_ion(@ion.Ion(decimal)), content="1.23")
    let timestamp = @ion.timestamp(year=2024)
    inspect(@text.write_ion(@ion.Ion(timestamp)), content="2024T")
    let field = @ion.field("status", @ion.Ion(@ion.symbol("ready")))
    inspect(
    @text.write_ion(@ion.Ion::from_fields([field])),
    content="{status: ready}",
    )
    }

    Implement ToIon for application types. Callers use the same constructor:

    ///|
    struct ExampleCount {
    count : Int32
    }

    ///|
    impl @ion.ToIon for ExampleCount with fn to_ion(self) -> @ion.Ion {
    @ion.Ion(Map([("count", self.count)]))
    }

    ///|
    test "construct Ion from an application type" {
    inspect(
    @text.write_ion(@ion.Ion(ExampleCount::{ count: 3, })),
    content="{count: 3}",
    )
    }

    The old IonValue::new, Field::new, Decimal::new, Timestamp::new and SymbolToken::new calls remain available with deprecation warnings. Use Ion(kind), field(...), decimal(...), timestamp(...) and symbol(...). The short helpers alias the named constructors, which also remain available.

    ///|
    test "build a value and compare it" {
    let book = @ion.IonValue::from_fields([
    @ion.field("title", @ion.IonValue::string("Ion")),
    @ion.field(
    "tags",
    @ion.IonValue::list([
    @ion.IonValue::symbol("data"),
    @ion.IonValue::symbol("format"),
    ]),
    ),
    ]).with_annotation(@ion.symbol("book"))
    inspect(book.ion_type().to_text(), content="struct")
    debug_inspect(
    book.annotations().map(token => token.text()),
    content=(
    #|[Some("book")]
    ),
    )
    // Field order does not matter to equivalence.
    let reordered = @ion.IonValue::from_fields([
    @ion.field(
    "tags",
    @ion.IonValue::list([
    @ion.IonValue::symbol("data"),
    @ion.IonValue::symbol("format"),
    ]),
    ),
    @ion.field("title", @ion.IonValue::string("Ion")),
    ]).with_annotation(@ion.symbol("book"))
    assert_true(book.equals(reordered))
    // Annotations do.
    assert_false(book.equals(reordered.with_annotations([])))
    }

    Match on kind() to read a value:

    ///|
    test "read a value by its kind" {
    let value = @text.read_ion("{ name: \"Ada\", born: 1815 }")
    match value.kind() {
    Struct(fields) =>
    debug_inspect(
    fields.map(field => field.name().text().unwrap_or("?")),
    content=(
    #|["name", "born"]
    ),
    )
    _ => fail("expected a struct")
    }
    }

    #Exact numbers

    A decimal keeps its coefficient and exponent as written, so 1.50 and 1.5 are different Ion values, and so are 0.0 and -0.0. Arithmetic goes through moonbitlang/x/decimal, and returns None for what that type cannot represent.

    ///|
    test "decimals keep their precision" {
    let price = @ion.Decimal::parse("1.50")
    inspect(price.coefficient(), content="150")
    inspect(price.exponent(), content="-2")
    assert_false(
    @ion.IonValue::decimal(price).equals(
    @ion.IonValue::decimal(@ion.Decimal::parse("1.5")),
    ),
    )
    inspect(@ion.Decimal::parse("-0.0").is_negative_zero(), content="true")
    debug_inspect(
    price.add(@ion.Decimal::parse("0.25")).map(sum => sum.to_ion_string()),
    content=(
    #|Some("1.75")
    ),
    )
    }

    #Timestamps

    A timestamp has the precision its components give it, and an optional offset. validate checks that it describes a real moment, which the readers apply to every timestamp they read.

    ///|
    test "timestamps" {
    let launch = @ion.Timestamp::parse("1969-07-20T20:17:40Z")
    debug_inspect(launch.precision(), content="Second")
    inspect(launch.to_ion_string(), content="1969-07-20T20:17:40Z")
    // The offset is kept, in minutes; `-00:00` is an unknown offset.
    let eastern = @ion.Timestamp::parse("2024-01-01T01:30+02:00")
    debug_inspect(eastern.offset(), content="Some(Known(120))")
    debug_inspect(
    @ion.Timestamp::parse("2024-01-01T01:30-00:00").offset(),
    content="Some(Unknown)",
    )
    // 2001 was not a leap year.
    let invalid = @ion.timestamp(year=2001, month=Some(2), day=Some(29))
    let message = try invalid.validate() catch {
    error => error.message()
    } noraise {
    _ => "valid"
    }
    inspect(message, content="timestamp day is out of range for its month")
    }

    with_offset moves a timestamp to another offset, keeping the instant it names, and as_utc moves it to Z. compare_instant compares the instants two timestamps name, whatever their precision and offset; equals compares Ion values, which differ when the precision or the offset does.

    ///|
    test "offsets and instants" {
    let eastern = @ion.Timestamp::parse("2024-01-01T01:30+02:00")
    inspect(eastern.as_utc().to_ion_string(), content="2023-12-31T23:30Z")
    inspect(
    eastern.with_offset(@ion.TimestampOffset::Known(-300)).to_ion_string(),
    content="2023-12-31T18:30-05:00",
    )
    let midnight = @ion.Timestamp::parse("2024-01-01")
    let also_midnight = @ion.Timestamp::parse("2024-01-01T01:00:00.000+01:00")
    inspect(midnight.compare_instant(also_midnight), content="0")
    assert_false(
    @ion.IonValue::timestamp(midnight).equals(
    @ion.IonValue::timestamp(also_midnight),
    ),
    )
    }

    #Symbols and symbol tables

    A symbol has text, a symbol ID, or both. The Ion 1.0 system symbols take IDs 1 to 9; a local symbol table declares more from 10. The readers keep a SymbolTable per stream and resolve IDs against it.

    ///|
    test "a symbol table resolves symbol IDs" {
    let table = @ion.SymbolTable::system()
    debug_inspect(table.text(4), content="Some(\"name\")")
    let declaration = @text.read_ion("{ symbols: [\"red\", \"green\"] }").with_annotation(
    @ion.symbol("$ion_symbol_table"),
    )
    assert_true(table.apply_local_symbol_table(declaration))
    inspect(table.max_id(), content="11")
    debug_inspect(
    table.lookup(11).text(),
    content=(
    #|Some("green")
    ),
    )
    // An ID beyond the table is an error.
    let beyond = try table.lookup(12) catch {
    error => error.message()
    } noraise {
    token => token.text().unwrap_or("?")
    }
    inspect(
    beyond,
    content="symbol ID $12 is beyond the symbol table, whose largest ID is 11",
    )
    }

    A local symbol table can import shared symbol tables by name and version. The readers resolve them against a Catalog, which a program loads from its $ion_shared_symbol_table::{...} structs:

    ///|
    test "imports resolve against a catalog" {
    let catalog = @ion.Catalog::load(
    @text.read_ion_datagram(
    "$ion_shared_symbol_table::{ name: \"colors\", version: 1, symbols: [\"red\", \"green\"] }",
    ),
    )
    let values = @text.read_ion_datagram(
    "$ion_symbol_table::{ imports: [{ name: \"colors\", version: 1 }], symbols: [\"local\"] } $11 $12",
    catalog~,
    )
    inspect(
    @text.write_all(values),
    content=(
    #|green
    #|local
    ),
    )
    }

    Unknown symbols imported from a shared table retain an import_location with its name and one-based slot. SymbolToken::equivalent and IonValue::equals use that location, ignoring the stream-local ID and table version. Unknown local symbols remain equivalent to $0. The token's derived Eq and Hash compare its complete representation, including reader metadata. Shared-table imports fields are informational metadata; loading a catalog does not resolve them or add slots for them.

    ///|
    test "unknown imports keep their identity through text" {
    let symbol = @ion.SymbolToken::from_import(20, "example", 1, 2)
    let shifted = @ion.SymbolToken::from_import(30, "example", 2, 2)
    assert_true(symbol.equivalent(shifted))
    assert_false(symbol.equivalent(@ion.SymbolToken::from_sid(0)))
    let value = @ion.IonValue::symbol_token(symbol)
    assert_true(@text.read_ion(@text.write_ion(value)).equals(value))
    }

    Text and binary writers preserve the import name, version, and slot while assigning output-local IDs. Unknown local IDs are reserved with null slots. The incremental writers reset their context when an unknown imported symbol needs a different table. A single writer context containing unknown symbols from conflicting versions of one shared table raises IonError::Unsupported. This applies to one text value or one binary datagram; write separate contexts with the incremental writer when the versions differ between values.

    #Traversal

    IonValue::accept walks a value depth-first with an IonVisitor, whose methods all default to doing nothing. IonValue::fold threads an accumulator instead, and each callback can continue, skip a value's children, or stop. None of these recurse: they work at any depth.

    ///|
    struct FieldNames {
    names : Array[String]
    }

    ///|
    impl @ion.IonVisitor for FieldNames with fn visit_field_name(self, token) {
    self.names.push(token.text().unwrap_or("?"))
    }

    ///|
    test "visit every field name" {
    let value = @text.read_ion("{ a: 1, b: { c: 2 }, d: [ { e: 3 } ] }")
    let visitor : FieldNames = { names: [], }
    value.accept(visitor)
    debug_inspect(
    visitor.names,
    content=(
    #|["a", "b", "c", "d", "e"]
    ),
    )
    }

    ///|
    test "sum the integers outside annotated values" {
    let value = @text.read_ion("[1, 2, skip::[100, 200], (3 4)]")
    let sum = value.fold(0, {
    ..@ion.IonFold::default(),
    visit_value: (total, value) => {
    if value.has_annotations() {
    return SkipChildren(total)
    }
    match value.kind() {
    Int(n) => Continue(total + n.to_int())
    _ => Continue(total)
    }
    },
    })
    inspect(sum, content="10")
    }

    IonValue::walk gives the same depth-first order as events: Enter and Exit around each list, S-expression and struct, and Scalar for every other value, each with its struct field name. IonValue::walker gives the walk as a pull-based ValueWalker, which can also skip a container's children. IonValue::fold_up folds bottom-up: each container gets its children's results. None of them recurse, so they work at any depth.

    ///|
    test "walk a value as events" {
    let value = @text.read_ion("{ a: [1, 2], b: null.list }")
    let events = value
    .walk()
    .map(event => {
    match event {
    Enter(v, ..) => "enter " + v.ion_type().to_text()
    Exit(v, ..) => "exit " + v.ion_type().to_text()
    Scalar(v, ..) => "scalar " + v.ion_type().to_text()
    }
    })
    .collect()
    debug_inspect(
    events,
    content=(
    #|[
    #| "enter struct",
    #| "enter list",
    #| "scalar int",
    #| "scalar int",
    #| "exit list",
    #| "scalar list",
    #| "exit struct",
    #|]
    ),
    )
    }

    ///|
    test "skip the children of annotated containers" {
    let walker = @text.read_ion("[1, skip::[2, 3], 4]").walker()
    let ints : Array[String] = []
    while walker.next() is Some(event) {
    match event {
    Enter(value, ..) => if value.has_annotations() { walker.skip_children() }
    Scalar(value, ..) =>
    if value.kind() is Int(n) {
    ints.push(n.to_string())
    }
    Exit(_, ..) => ()
    }
    }
    debug_inspect(
    ints,
    content=(
    #|["1", "4"]
    ),
    )
    }

    ///|
    test "measure the depth with fold_up" {
    let value = @text.read_ion("[1, [2, [3]], {a: (4)}]")
    let depth = value.fold_up(scalar=_ => 0, container=(_, children) => {
    let mut deepest = 0
    for child in children {
    if child.1 > deepest {
    deepest = child.1
    }
    }
    deepest + 1
    })
    inspect(depth, content="3")
    }

    IonVisitor

    pub(open) trait IonVisitor {
    fn visit_value(Self, Ion) -> Unit = _
    fn visit_annotation(Self, SymbolToken) -> Unit = _
    fn visit_field_name(Self, SymbolToken) -> Unit = _
    fn visit_list(Self, Array[Ion]) -> Unit = _
    fn visit_sexp(Self, Array[Ion]) -> Unit = _
    fn visit_struct(Self, Array[Field]) -> Unit = _
    }

    A visitor for traversing an Ion value. Use [IonValue::accept] to begin traversal.

    ToIon

    pub(open) trait ToIon {
    fn to_ion(Self) -> Ion
    }

    Types that can be converted to an Ion value. Implement this trait for application types, then construct with Ion(value).
    impl ToIon for Unit
    impl ToIon for Bool
    impl ToIon for Int
    impl ToIon for Int64
    impl ToIon for UInt
    impl ToIon for UInt64
    impl ToIon for Float
    impl ToIon for Double
    impl ToIon for String
    impl ToIon for Bytes
    impl ToIon for Array[T]
    impl ToIon for Json
    impl ToIon for Map[String, T]

    IonError

    pub(all) suberror IonError {
    Syntax(String)
    DataModel(String)
    Unsupported(String)
    FuelExhausted(budget~ : Budget, operation~ : FuelOperation, location~ : FuelLocation)
    } derive(
    Debug
    )

    An error raised while reading, writing, or validating Ion.

    IonError::message

    fn IonError::message(self : IonError) -> String

    The human-readable message carried by an [IonError].

    IonError::to_repr

    Budget

    pub struct Budget {
    // private fields
    } derive(Eq,
    Debug
    )

    A positive number of simultaneously open nested operations.

    Budget::Budget

    fn Budget::Budget(units : Int) -> Budget raise IonError

    Create a positive fuel budget, or raise DataModel for zero or less.

    Budget::equal

    fn Budget::equal(Budget, Budget) -> Bool

    Budget::not_equal

    fn Budget::not_equal(x : Budget, y : Budget) -> Bool

    Budget::to_repr

    Budget::units

    fn Budget::units(self : Budget) -> Int

    The number of units in this budget.

    Catalog

    type Catalog

    A catalog of shared symbol tables, by name and version.

    Catalog::add

    fn Catalog::add(self : Catalog, table : SharedSymbolTable) -> Unit

    Add a shared table, replacing one with the same name and version.

    Catalog::find

    fn Catalog::find(self : Catalog, name : String, version : Int) -> SharedSymbolTable?

    The shared table with exactly this name and version.

    Catalog::is_empty

    fn Catalog::is_empty(self : Catalog) -> Bool

    Whether the catalog holds no tables.

    Catalog::latest

    fn Catalog::latest(self : Catalog, name : String) -> SharedSymbolTable?

    The shared table with this name and the highest version.

    Catalog::load

    fn Catalog::load(values : Array[Ion]) -> Catalog raise IonError

    A catalog of the shared tables among values; other values are ignored.

    Catalog::new

    fn Catalog::new() -> Catalog

    An empty catalog.

    Decimal

    pub struct Decimal {
    coefficient :
    BigInt

    exponent : Int
    negative_zero : Bool
    } derive(
    Debug
    )

    An Ion decimal value, kept as coefficient * 10^exponent.
    impl ToIon for Decimal

    Decimal::Decimal

    #alias(new, deprecated="Use `@ion.decimal(...)` instead")
    #as_free_fn(decimal)
    fn Decimal::Decimal(coefficient :
    BigInt
    , exponent : Int) -> Decimal

    Build a decimal from an explicit coefficient and base-10 exponent.

    Decimal::add

    fn Decimal::add(self : Decimal, other : Decimal) -> Decimal?

    The sum, via moonbitlang/x's decimal arithmetic, or None when an operand or the result is out of that type's range. The result is normalized.

    Decimal::coefficient

    The unscaled coefficient (significand) of this decimal.

    Decimal::compare

    fn Decimal::compare(self : Decimal, other : Decimal) -> Int?

    Numeric comparison, via moonbitlang/x's decimal arithmetic: -1, 0, or 1. Unlike IonValue::equals, this ignores scale, so 1.5 and 1.50 compare equal. Returns None when an operand is out of range.

    Decimal::exponent

    fn Decimal::exponent(self : Decimal) -> Int

    The base-10 exponent of this decimal.

    Decimal::from_int

    fn Decimal::from_int(value : Int) -> Decimal

    Build a decimal from an Int, with exponent 0.

    Decimal::from_x

    Build an Ion decimal from a moonbitlang/x/decimal. This direction is exact: the scale becomes a negative exponent.

    Decimal::is_negative

    fn Decimal::is_negative(self : Decimal) -> Bool

    Whether this value is negative, including negative zero.

    Decimal::is_negative_zero

    fn Decimal::is_negative_zero(self : Decimal) -> Bool

    Whether this value is negative zero.

    Decimal::is_zero

    fn Decimal::is_zero(self : Decimal) -> Bool

    Whether the coefficient is zero.

    Decimal::multiply

    fn Decimal::multiply(self : Decimal, other : Decimal) -> Decimal?

    The product, via moonbitlang/x's decimal arithmetic, or None when an operand or the result is out of that type's range.

    Decimal::negative_zero

    fn Decimal::negative_zero() -> Decimal

    The decimal -0. (negative zero), which Ion keeps distinct from 0..

    Decimal::negative_zero_at

    fn Decimal::negative_zero_at(exponent : Int) -> Decimal

    The decimal -0dN: negative zero at a given exponent, which Ion keeps distinct from 0dN.

    Decimal::parse

    fn Decimal::parse(text : StringView) -> Decimal raise IonError

    Parse the body of an Ion decimal: an optional sign, a coefficient with an optional decimal point, and an optional d/D exponent. For example -1_234.56d-7, 0., or 123d0.

    Decimal::subtract

    fn Decimal::subtract(self : Decimal, other : Decimal) -> Decimal?

    The difference, via moonbitlang/x's decimal arithmetic, or None when an operand or the result is out of that type's range.

    Decimal::to_double

    fn Decimal::to_double(self : Decimal) -> Double?

    The nearest Double, or None when the value is out of range.

    Decimal::to_ion

    #deprecated("Use `Ion(value)` instead")
    fn Decimal::to_ion(self : Decimal) -> Ion

    Decimal::to_ion_string

    fn Decimal::to_ion_string(self : Decimal) -> String

    Render this decimal as Ion text, preserving coefficient, exponent, and signed zero. Use positional notation for non-positive exponents when the adjusted exponent is at least -6; otherwise retain d notation. Positive exponents require d notation to preserve precision.

    Decimal::to_repr

    Decimal::to_x

    This decimal as a moonbitlang/x/decimal, or None when it cannot be represented there.

    A non-negative exponent is folded into the coefficient, and a negative one becomes the scale. Two things are lost: x's decimal normalizes trailing zeros, and it has no signed zero, so -0. converts to plain 0. and, for example, 1.50 converts to 1.5. The conversion returns None when the required scale exceeds @decimal.max_scale.

    Decimal::zero

    fn Decimal::zero() -> Decimal

    The decimal 0..

    Field

    pub struct Field {
    name : SymbolToken
    value : Ion
    } derive(
    Debug
    )

    A field of an Ion struct: a symbol token name and a value.

    The name is a token, not a value, so it cannot carry annotations.

    Field::Field

    #alias(new, deprecated="Use `@ion.field(...)` instead")
    #as_free_fn(field)
    fn Field::Field(name : String, value : Ion) -> Field

    Build a struct field from a name literal.

    Field::from_token

    fn Field::from_token(name : SymbolToken, value : Ion) -> Field

    Build a struct field from a name symbol token.

    Field::name

    fn Field::name(self : Field) -> SymbolToken

    The field's name token.

    Field::to_repr

    Field::value

    fn Field::value(self : Field) -> Ion

    The field's value.

    FoldAction

    pub(all) enum FoldAction[A] {
    Continue(A)
    SkipChildren(A)
    Stop(A)
    } derive(Eq,
    Debug
    )

    Control flow returned by a fold callback.

    • Continue(a) — descend into the value's children with the new accumulator
    • SkipChildren(a) — do not descend, continue with siblings
    • Stop(a) — stop the whole traversal and return a

    FoldAction::equal

    fn[A : Eq] FoldAction::equal(FoldAction[A], FoldAction[A]) -> Bool

    FoldAction::is_stop

    fn[A] FoldAction::is_stop(self : FoldAction[A]) -> Bool

    Whether this action stops the traversal.

    FoldAction::not_equal

    fn[A : Eq] FoldAction::not_equal(x : FoldAction[A], y : FoldAction[A]) -> Bool

    FoldAction::value

    fn[A] FoldAction::value(self : FoldAction[A]) -> A

    The accumulator carried by a FoldAction.

    Fuel

    pub(all) enum Fuel {
    Unlimited
    Limited(Budget)
    } derive(Eq,
    Debug
    )

    Optional scoped fuel. Nesting is unrestricted unless the caller supplies Limited(Budget(...)). Siblings reuse their enclosing operation's fuel. Fuel bounds open operations, not total work, memory, or elapsed time.

    Fuel::equal

    fn Fuel::equal(Fuel, Fuel) -> Bool

    Fuel::not_equal

    fn Fuel::not_equal(x : Fuel, y : Fuel) -> Bool

    Fuel::to_repr

    FuelLocation

    pub(all) enum FuelLocation {
    TextOffset(Int)
    CharacterOffset(Int)
    RegexInstruction(Int)
    BinaryOffset(Int)
    ValuePath(String)
    SchemaId(String)
    } derive(Eq,
    Debug
    )

    Where fuel ran out. Text offsets count UTF-16 code units; binary offsets count octets. Value paths use the schema validator's $ notation.

    FuelLocation::equal

    FuelLocation::not_equal

    fn FuelLocation::not_equal(x : FuelLocation, y : FuelLocation) -> Bool

    FuelOperation

    pub(all) enum FuelOperation {
    TextRead
    BinaryRead
    CstRead
    JsonRead
    SchemaLoad
    SchemaImport
    SchemaValidate
    RegexParse
    RegexCompile
    } derive(Eq,
    Debug
    )

    The operation that tried to enter a nested scope after fuel ran out.

    FuelOperation::equal

    FuelOperation::not_equal

    fn FuelOperation::not_equal(x : FuelOperation, y : FuelOperation) -> Bool

    ImportLocation

    pub struct ImportLocation {
    name : String
    sid : Int
    } derive(Eq, Hash,
    Debug
    )

    The identity of an imported symbol with unknown text. sid is its one-based position in the shared table, not its ID in a stream.

    ImportLocation::equal

    ImportLocation::hash

    fn ImportLocation::hash(self : ImportLocation) -> Int

    ImportLocation::hash_combine

    fn ImportLocation::hash_combine(ImportLocation, Hasher) -> Unit

    ImportLocation::not_equal

    fn ImportLocation::not_equal(x : ImportLocation, y : ImportLocation) -> Bool

    Ion

    #alias(IonValue)
    pub struct Ion {
    annotations : Array[SymbolToken]
    kind : IonValueKind
    } derive(
    Debug
    )

    An Ion value: zero or more annotations followed by the core value.
    impl ToIon for Ion

    Ion::Ion

    fn[T : ToIon] Ion::Ion(value : T) -> Ion

    Convert a value to Ion through its ToIon implementation.

    Ion::accept

    fn Ion::accept(self : Ion, visitor : &IonVisitor) -> Unit

    Accept visitor on this value, traversing depth-first in document order: the value, then its annotations, then — for containers — the child values (struct field names precede their values). It works at any nesting depth.

    Ion::annotations

    fn Ion::annotations(self : Ion) -> Array[SymbolToken]

    The annotations on this value, in order.

    Ion::big_int

    An int value from an arbitrary-precision BigInt.

    Ion::blob

    fn Ion::blob(value : Bytes) -> Ion

    A blob value.

    Ion::bool

    fn Ion::bool(value : Bool) -> Ion

    A Bool value.

    Ion::clob

    fn Ion::clob(value : Bytes) -> Ion

    A clob value.

    Ion::decimal

    fn Ion::decimal(value : Decimal) -> Ion

    A decimal value.

    Ion::equals

    fn Ion::equals(self : Ion, other : Ion) -> Bool

    Structural equality following the Ion data model: annotations must match in order, all NaNs are equivalent, positive and negative zero are not, list and sexp elements are compared pairwise, and struct fields are compared as an unordered multiset. It works at any nesting depth.

    Ion::float

    fn Ion::float(value : Double) -> Ion

    A float value.

    Ion::fold

    fn[A] Ion::fold(self : Ion, acc : A, folder : IonFold[A]) -> A

    Fold over this value, depth-first in document order, threading acc and honoring [FoldAction] flow control.

    Ion::fold_up

    fn[R] Ion::fold_up(self : Ion, scalar~ : (Ion) -> R raise?, container~ : (Ion, Array[(SymbolToken?, R)]) -> R raise?) -> R raise?

    Fold self bottom-up with no recursion. scalar gives a result for each value with no children; container gives a result for each list, S-expression or struct from its children's results, in order, each with its field name (None outside a struct).

    container runs after all of that container's children. A walk that must act in document order (such as raising for the first bad symbol) uses [IonValue::walk] instead. The children's results are kept until their container finishes, so memory grows with the width of the open levels.

    Ion::from_fields

    fn Ion::from_fields(fields : Array[Field]) -> Ion

    A struct value.

    Ion::has_annotations

    fn Ion::has_annotations(self : Ion) -> Bool

    Whether this value carries at least one annotation.

    Ion::has_imported_symbols

    fn Ion::has_imported_symbols(self : Ion) -> Bool

    Whether any unknown symbol value, field name, or annotation retains a shared-table import location.

    Ion::int

    fn Ion::int(value : Int) -> Ion

    An int value from an Int.

    Ion::ion_type

    fn Ion::ion_type(self : Ion) -> IonType

    The Ion type of this value. For a typed null such as null.int this is the null's type (int), not null.

    Ion::is_null

    fn Ion::is_null(self : Ion) -> Bool

    Whether this value is a null of any type.

    Ion::is_untyped_null

    fn Ion::is_untyped_null(self : Ion) -> Bool

    Whether this value is the untyped null (null.null).

    Ion::kind

    fn Ion::kind(self : Ion) -> IonValueKind

    The core kind of this value.

    Ion::list

    fn Ion::list(values : Array[Ion]) -> Ion

    A list value.

    Ion::new

    #deprecated("Use `Ion(kind)` instead")
    fn Ion::new(kind : IonValueKind) -> Ion

    Build a value from a kind, with no annotations. Use Ion(kind) instead.

    Ion::sexp

    fn Ion::sexp(values : Array[Ion]) -> Ion

    An sexp value.

    Ion::string

    fn Ion::string(value : String) -> Ion

    A string value.

    Ion::symbol

    fn Ion::symbol(value : String) -> Ion

    A symbol value with known text.

    Ion::symbol_token

    fn Ion::symbol_token(value : SymbolToken) -> Ion

    A symbol value from a symbol token.

    Ion::timestamp

    fn Ion::timestamp(value : Timestamp) -> Ion

    A timestamp value.

    Ion::to_ion

    #deprecated("Use `Ion(value)` instead")
    fn Ion::to_ion(self : Ion) -> Ion

    Ion::to_repr

    Ion::typed_null

    fn Ion::typed_null(t : IonType) -> Ion

    A typed null such as null.int.

    Ion::untyped_null

    fn Ion::untyped_null() -> Ion

    The untyped null value.

    Ion::walk

    fn Ion::walk(self : Ion) -> Iter[WalkEvent]

    The events of a walk over self, as an iterator.

    Ion::walker

    fn Ion::walker(self : Ion) -> ValueWalker

    A walker positioned before self.

    Ion::with_annotation

    fn Ion::with_annotation(self : Ion, annotation : SymbolToken) -> Ion

    A copy of this value with annotation appended to its annotations.

    Ion::with_annotations

    fn Ion::with_annotations(self : Ion, annotations : Array[SymbolToken]) -> Ion

    A copy of this value with the given annotations, replacing any existing ones.

    IonFold

    pub(all) struct IonFold[A] {
    visit_value : (A, Ion) -> FoldAction[A]
    visit_annotation : (A, SymbolToken) -> FoldAction[A]
    visit_field_name : (A, SymbolToken) -> FoldAction[A]
    }

    The callbacks for a fold over an Ion value.

    Build one with [IonFold::default] and override the fields you need, or use [continuing] to lift a plain accumulator function.

    IonFold::default

    fn[A] IonFold::default() -> IonFold[A]

    A fold whose callbacks pass the accumulator through unchanged.

    IonType

    pub(all) enum IonType {
    Null
    Bool
    Int
    Float
    Decimal
    Timestamp
    Symbol
    String
    Clob
    Blob
    List
    Sexp
    Struct
    } derive(Eq, Hash,
    Debug
    )

    The thirteen core Ion types.

    Null is the untyped null; every other type also has a typed null value (null.int, null.list, ...), whose Ion type is the type itself.

    IonType::all

    fn IonType::all() -> Array[IonType]

    Every Ion type, in the order used throughout the specification.

    IonType::equal

    fn IonType::equal(IonType, IonType) -> Bool

    IonType::from_text

    fn IonType::from_text(name : StringView) -> IonType?

    Parse the textual name of an Ion type, as it appears after null. or in an Ion Schema type constraint. Returns None for an unknown name. Both null and $null (the Ion Schema spelling) name the untyped null type.

    IonType::has_typed_null

    fn IonType::has_typed_null(self : IonType) -> Bool

    Whether this type may be the target of a null.<type> literal.

    IonType::hash

    fn IonType::hash(self : IonType) -> Int

    IonType::hash_combine

    fn IonType::hash_combine(IonType, Hasher) -> Unit

    IonType::is_container

    fn IonType::is_container(self : IonType) -> Bool

    Whether this type is one of the three container types.

    IonType::not_equal

    fn IonType::not_equal(x : IonType, y : IonType) -> Bool

    IonType::to_repr

    IonType::to_text

    fn IonType::to_text(self : IonType) -> String

    The Ion text name of this type (null, int, sexp, ...).

    IonValueKind

    pub(all) enum IonValueKind {
    Null(IonType)
    Bool(Bool)
    Int(
    BigInt
    )
    Float(Double)
    Decimal(Decimal)
    Timestamp(Timestamp)
    Symbol(SymbolToken)
    String(String)
    Clob(Bytes)
    Blob(Bytes)
    List(Array[Ion])
    Sexp(Array[Ion])
    Struct(Array[Field])
    } derive(
    Debug
    )

    The core kind of an Ion value, without annotations.

    IonValueKind::to_ion

    #deprecated("Use `Ion(kind)` instead")
    fn IonValueKind::to_ion(self : IonValueKind) -> Ion

    SharedSymbolTable

    type SharedSymbolTable

    A shared symbol table: a name, a version, and its symbols in ID order. None is a slot with unknown text.

    SharedSymbolTable::from_value

    fn SharedSymbolTable::from_value(value : Ion) -> SharedSymbolTable? raise IonError

    The shared table a value declares: a struct annotated $ion_shared_symbol_table with a non-empty string name, an optional positive version (1 when missing), and a symbols list, where an element that is not a string is a slot with unknown text. None means the value is not a shared table. The imports field is informational metadata and is ignored; only symbols declares slots.

    SharedSymbolTable::name

    fn SharedSymbolTable::name(self : SharedSymbolTable) -> String

    The table's name.

    SharedSymbolTable::new

    fn SharedSymbolTable::new(name : String, version : Int, symbols : Array[String?]) -> SharedSymbolTable

    A shared symbol table named name, at version, declaring symbols.

    SharedSymbolTable::symbols

    fn SharedSymbolTable::symbols(self : SharedSymbolTable) -> Array[String?]

    The table's symbols in ID order; None is a slot with unknown text.

    SharedSymbolTable::version

    fn SharedSymbolTable::version(self : SharedSymbolTable) -> Int

    The table's version.

    SymbolTable

    type SymbolTable

    The symbols in effect at a point in an Ion stream: the system symbols, the symbols of the shared tables the local symbol table imports, and the local symbols.

    SymbolTable::apply_local_symbol_table

    fn SymbolTable::apply_local_symbol_table(self : SymbolTable, value : Ion) -> Bool raise IonError

    Apply value when it is a local symbol table: a struct whose first annotation is $ion_symbol_table. Returns whether it was one; any other value leaves the table unchanged.

    The symbols field lists the new local symbols; an element that is not a string declares a slot with unknown text, and a symbols field that is not a list declares none. imports: $ion_symbol_table appends the symbols to the current ones instead of replacing them. A list of imports names shared symbol tables, which resolve against the catalog as resolve_import describes.

    SymbolTable::find

    fn SymbolTable::find(self : SymbolTable, text : String) -> Int?

    The symbol ID this table already uses for text.

    SymbolTable::for_values

    fn SymbolTable::for_values(values : Array[Ion]) -> SymbolTable raise IonError

    Build a symbol table for serializing values. Unknown imported symbols retain their shared-table name, version, and slot; their stream-local IDs may change. Unknown local IDs are reserved after the imports. Conflicting versions of the same unknown shared table in one context are rejected because a catalog could resolve their null slots differently.

    SymbolTable::intern

    fn SymbolTable::intern(self : SymbolTable, text : String) -> Int

    The symbol ID for text, adding it to the local symbols when the table does not already define it.

    SymbolTable::intern_token

    fn SymbolTable::intern_token(self : SymbolTable, token : SymbolToken) -> Int raise IonError

    The ID a writer context assigns to token. Imported unknown symbols are located by table name and slot; local unknown IDs follow this context's imported IDs. Known text is interned as usual.

    SymbolTable::is_empty

    fn SymbolTable::is_empty(self : SymbolTable) -> Bool

    Whether the table defines no symbols beyond the system symbols.

    SymbolTable::localize_for_text

    fn SymbolTable::localize_for_text(self : SymbolTable, value : Ion) -> Ion raise IonError

    Replace unknown tokens with this context's IDs for text rendering. Emit this context's local symbol table before the result.

    SymbolTable::locals

    fn SymbolTable::locals(self : SymbolTable) -> Array[String?]

    The local symbols in ID order, after the imported IDs. None is a slot with unknown text.

    SymbolTable::lookup

    fn SymbolTable::lookup(self : SymbolTable, sid : Int) -> SymbolToken raise IonError

    The token for a symbol ID that a reader meets, as token gives it. An ID beyond the table is an error: the Ion specification requires a reader to reject it rather than keep a symbol no table defines.

    SymbolTable::max_id

    fn SymbolTable::max_id(self : SymbolTable) -> Int

    The highest symbol ID the table defines.

    SymbolTable::reserve

    fn SymbolTable::reserve(self : SymbolTable, count : Int) -> Unit

    Replace the imports with count local null slots from ID 10, ahead of the existing local symbols. These remain equivalent to symbol zero; declaring them through a shared import would give them a different identity. Known texts can be interned after them.

    SymbolTable::reset

    fn SymbolTable::reset(self : SymbolTable) -> Unit

    Drop the imported and local symbols, as a version marker does.

    SymbolTable::resolve

    fn SymbolTable::resolve(self : SymbolTable, value : Ion) -> Ion raise IonError

    value with text for every symbol ID the table defines, in its annotations, field names, and symbol values at any depth. A symbol ID the table leaves without text retains its import location when applicable, and an ID beyond the table raises, as lookup does: for the first such ID in document order.

    SymbolTable::system

    fn SymbolTable::system(catalog? : Catalog) -> SymbolTable

    A table with only the Ion 1.0 system symbols, resolving imports against catalog (empty by default).

    SymbolTable::text

    fn SymbolTable::text(self : SymbolTable, sid : Int) -> String?

    The text of a symbol ID: a system symbol, an imported or local symbol, or nothing for an ID with unknown text or beyond the table.

    SymbolTable::to_local_symbol_table

    fn SymbolTable::to_local_symbol_table(self : SymbolTable) -> Ion?

    The local symbol table that sets up this table in a stream, or None when the system symbols suffice: its imports, each with the max_id it takes, and the local symbols.

    SymbolTable::token

    fn SymbolTable::token(self : SymbolTable, sid : Int) -> SymbolToken

    The token for a symbol ID: known text, or an unknown symbol retaining its imported table name and slot when applicable.

    SymbolTable::writer_token_id

    fn SymbolTable::writer_token_id(self : SymbolTable, token : SymbolToken) -> Int raise IonError

    Assign a writer ID, rejecting unknown tokens that would alias known text or refer to an undeclared import or local slot.

    SymbolToken

    pub struct SymbolToken {
    text : String?
    sid : Int?
    import_location : ImportLocation?
    import_version : Int?
    } derive(Eq, Hash,
    Debug
    )

    A symbol token: known text, a stream-local symbol ID, and the shared-table location when an imported symbol's text is unknown.

    SymbolToken::SymbolToken

    #alias(new, deprecated="Use `@ion.symbol(...)` instead")
    #as_free_fn(symbol)
    fn SymbolToken::SymbolToken(text : String) -> SymbolToken

    A symbol token with known text.

    SymbolToken::equal

    fn SymbolToken::equal(SymbolToken, SymbolToken) -> Bool

    SymbolToken::equivalent

    fn SymbolToken::equivalent(self : SymbolToken, other : SymbolToken) -> Bool

    Ion symbol equivalence. Known text determines identity; otherwise imported symbols compare by table name and slot, and local unknown symbols equal $0.

    SymbolToken::from_import

    fn SymbolToken::from_import(sid : Int, name : String, version : Int, import_sid : Int) -> SymbolToken

    An imported symbol with unknown text. The local ID and table version are retained for serialization; equivalence uses only name and import_sid.

    SymbolToken::from_sid

    fn SymbolToken::from_sid(sid : Int) -> SymbolToken

    A symbol token known only by its symbol ID.

    SymbolToken::has_text

    fn SymbolToken::has_text(self : SymbolToken) -> Bool

    Whether the symbol's text is known.

    SymbolToken::hash

    fn SymbolToken::hash(self : SymbolToken) -> Int

    SymbolToken::hash_combine

    fn SymbolToken::hash_combine(SymbolToken, Hasher) -> Unit

    SymbolToken::not_equal

    fn SymbolToken::not_equal(x : SymbolToken, y : SymbolToken) -> Bool

    SymbolToken::require_text

    fn SymbolToken::require_text(self : SymbolToken) -> String raise IonError

    The symbol's text, raising [IonError::Unsupported] when the symbol is known only by ID.

    SymbolToken::sid

    fn SymbolToken::sid(self : SymbolToken) -> Int?

    The symbol's ID, when the context assigned one.

    SymbolToken::text

    fn SymbolToken::text(self : SymbolToken) -> String?

    The symbol's text, when known.

    SymbolToken::to_ion

    #deprecated("Use `Ion(value)` instead")
    fn SymbolToken::to_ion(self : SymbolToken) -> Ion

    Timestamp

    pub struct Timestamp {
    year : Int
    month : Int?
    day : Int?
    hour : Int?
    minute : Int?
    second : Int?
    fraction : Decimal?
    offset : TimestampOffset?
    } derive(
    Debug
    )

    An Ion timestamp value.
    impl ToIon for Timestamp

    Timestamp::Timestamp

    #alias(new, deprecated="Use `@ion.timestamp(...)` instead")
    #as_free_fn(timestamp)
    fn Timestamp::Timestamp(year~ : Int, month? : Int?, day? : Int?, hour? : Int?, minute? : Int?, second? : Int?, fraction? : Decimal?, offset? : TimestampOffset?) -> Timestamp

    Build a timestamp from its components. Components left as None reduce the precision; fraction requires second, and offset is required for any value that carries a time.

    Timestamp::as_local

    #deprecated("build the timestamp in UTC and use `with_offset`")
    fn Timestamp::as_local(self : Timestamp) -> Timestamp

    Read the components as UTC and move them to the timestamp's own offset. This mixes UTC components with a local offset, so the input does not name the instant its text shows; build the UTC timestamp and use with_offset instead.

    Timestamp::as_utc

    fn Timestamp::as_utc(self : Timestamp) -> Timestamp

    The same instant in UTC, with the offset Z: the form whose components Ion binary stores and Ion Hash serializes.

    Timestamp::compare_instant

    fn Timestamp::compare_instant(self : Timestamp, other : Timestamp) -> Int

    Compare the instants two timestamps name, as -1, 0, or 1, ignoring their precision and offsets: a component a timestamp leaves out counts as its lowest value, and a timestamp with no time, or an unknown offset, is read in UTC. 2007T, 2007-01-01, and 2007-01-01T01:00+01:00 name the same instant, though they are different Ion values.

    Timestamp::day

    fn Timestamp::day(self : Timestamp) -> Int?

    The day component (1-31), when present.

    Timestamp::fraction

    fn Timestamp::fraction(self : Timestamp) -> Decimal?

    The fractional-second component, when present.

    Timestamp::hour

    fn Timestamp::hour(self : Timestamp) -> Int?

    The hour component (0-23), when present.

    Timestamp::minute

    fn Timestamp::minute(self : Timestamp) -> Int?

    The minute component (0-59), when present.

    Timestamp::month

    fn Timestamp::month(self : Timestamp) -> Int?

    The month component (1-12), when present.

    Timestamp::offset

    fn Timestamp::offset(self : Timestamp) -> TimestampOffset?

    The local offset, when present.

    Timestamp::parse

    fn Timestamp::parse(text : StringView) -> Timestamp raise IonError

    Parse an Ion timestamp. For example 2007T, 2007-02-23, 2007-02-23T12:14Z, or 2007-02-23T12:14:33.079-08:00.

    Timestamp::precision

    fn Timestamp::precision(self : Timestamp) -> TimestampPrecision

    The precision implied by the components that are present.

    Timestamp::second

    fn Timestamp::second(self : Timestamp) -> Int?

    The second component (0-59), when present.

    Timestamp::to_ion

    #deprecated("Use `Ion(value)` instead")
    fn Timestamp::to_ion(self : Timestamp) -> Ion

    Timestamp::to_ion_string

    fn Timestamp::to_ion_string(self : Timestamp) -> String

    Render this timestamp as Ion text.

    Timestamp::validate

    fn Timestamp::validate(self : Timestamp) -> Unit raise IonError

    Check that the components describe a real moment: the year is 1 to 9999, each component is in range and the day exists in its month, an hour comes with a minute, and a fractional second is at least zero and less than one.

    Timestamp::with_offset

    fn Timestamp::with_offset(self : Timestamp, offset : TimestampOffset) -> Timestamp

    The same instant expressed at offset: the date and time move by the difference between the offsets. An unknown offset reads and writes the components in UTC. A timestamp with no time of day has no offset, and is returned unchanged.

    Timestamp::year

    fn Timestamp::year(self : Timestamp) -> Int

    The year component.

    TimestampOffset

    pub(all) enum TimestampOffset {
    Known(Int)
    Unknown
    } derive(Eq, Hash,
    Debug
    )

    The local offset of an Ion timestamp.

    TimestampOffset::equal

    TimestampOffset::hash

    fn TimestampOffset::hash(self : TimestampOffset) -> Int

    TimestampOffset::hash_combine

    fn TimestampOffset::hash_combine(TimestampOffset, Hasher) -> Unit

    TimestampOffset::not_equal

    fn TimestampOffset::not_equal(x : TimestampOffset, y : TimestampOffset) -> Bool

    TimestampPrecision

    pub(all) enum TimestampPrecision {
    Year
    Month
    Day
    Minute
    Second
    } derive(Eq, Hash,
    Debug
    )

    The precision to which an Ion timestamp is specified.

    TimestampPrecision::equal

    TimestampPrecision::hash

    fn TimestampPrecision::hash(self : TimestampPrecision) -> Int

    TimestampPrecision::hash_combine

    fn TimestampPrecision::hash_combine(TimestampPrecision, Hasher) -> Unit

    TimestampPrecision::not_equal

    ValueWalker

    pub struct ValueWalker {
    // private fields
    }

    A pull-based, depth-first walk over an Ion value that uses no recursion. Build one with [IonValue::walker], then call [ValueWalker::next] until it returns None.

    ValueWalker::depth

    fn ValueWalker::depth(self : ValueWalker) -> Int

    The number of containers the walk is inside now: 1 after the Enter of a top-level container, 0 again after its Exit.

    ValueWalker::next

    fn ValueWalker::next(self : ValueWalker) -> WalkEvent?

    The next event of the walk, or None when the walk is complete.

    ValueWalker::skip_children

    fn ValueWalker::skip_children(self : ValueWalker) -> Unit

    Skip the children of the container that the last [ValueWalker::next] entered: the next event is that container's Exit. After any other event it does nothing.

    WalkEvent

    pub(all) enum WalkEvent {
    Scalar(Ion, field~ : SymbolToken?)
    Enter(Ion, field~ : SymbolToken?)
    Exit(Ion, field~ : SymbolToken?)
    } derive(
    Debug
    )

    One step of a depth-first walk over an Ion value, in document order.

    field is the struct field name the value has in its parent, or None outside a struct. A typed null of a container type, such as null.list, is a Scalar: it has no children.

    WritePosition

    pub struct WritePosition {
    depth : Int
    parent : IonType?
    index : Int
    field : SymbolToken?
    annotations : Array[SymbolToken]
    }

    The position of the next value in an incremental writer.

    WriterState

    type WriterState

    Container, field, and annotation validation shared by incremental codecs.

    WriterState::begin_output

    fn WriterState::begin_output(self : WriterState) -> Unit raise IonError

    WriterState::check

    fn WriterState::check(self : WriterState) -> Unit raise IonError

    WriterState::check_finished

    fn WriterState::check_finished(self : WriterState) -> Unit raise IonError

    WriterState::closing

    fn WriterState::closing(self : WriterState) -> (IonType, Int, Int) raise IonError

    WriterState::commit

    fn WriterState::commit(self : WriterState) -> Unit

    Consume the validated slot after successful encoding/output.

    WriterState::end_output

    fn WriterState::end_output(self : WriterState) -> Unit

    WriterState::enter

    fn WriterState::enter(self : WriterState, kind : IonType) -> Unit

    WriterState::fail

    fn WriterState::fail(self : WriterState) -> Unit

    WriterState::finish

    fn WriterState::finish(self : WriterState) -> Unit

    WriterState::leave

    fn WriterState::leave(self : WriterState) -> Unit

    WriterState::new

    WriterState::position

    fn WriterState::position(self : WriterState) -> WritePosition raise IonError

    Validate the next slot without consuming pending metadata.

    WriterState::set_annotations

    fn WriterState::set_annotations(self : WriterState, annotations : Array[SymbolToken]) -> Unit raise IonError

    WriterState::set_field_name

    fn WriterState::set_field_name(self : WriterState, name : SymbolToken) -> Unit raise IonError

    accept_values

    fn accept_values(values : Array[Ion], visitor : &IonVisitor) -> Unit

    Accept visitor on every value of a datagram, in order.

    check_container_kind

    fn check_container_kind(kind : IonType) -> Unit raise IonError

    Validate the container kind accepted by step-in.

    continuing

    fn[A, N] continuing(f : (A, N) -> A) -> ((A, N) -> FoldAction[A])

    Lift a plain (A, N) -> A function into a callback that always continues into children.

    days_in_month

    fn days_in_month(year : Int, month : Int) -> Int

    The number of days in month of year.

    fold_values

    fn[A] fold_values(values : Array[Ion], acc : A, folder : IonFold[A]) -> A

    Fold over every value of a datagram, in order.

    highest_symbol_id

    fn highest_symbol_id(values : Array[Ion]) -> Int

    The highest ID of a symbol known only by its ID in values, in annotations, field names, and symbol values at any depth, or 0 when there is none.

    is_leap_year

    fn is_leap_year(year : Int) -> Bool

    Whether year is a leap year in the proleptic Gregorian calendar.

    shift_date

    fn shift_date(year : Int, month : Int, day : Int, days : Int) -> (Int, Int, Int)

    The civil date days days after year-month-day, rolling over months and years. days may be negative.

    system_symbol_text

    fn system_symbol_text(sid : Int) -> String?

    The text of an Ion 1.0 system symbol ID (1..9), or None outside that range. The system symbol table is the initial symbol table shared by every Ion stream; IDs 0 and 10 and above have no text without a local symbol table.