Sign in

    jmespath

    JMESPath expressions, parsing and JSON evaluation for MoonBit

    jmespath
    json
    query
    parser
    Download zip
    Author
    Version
    0.7.0
    License
    Apache-2.0
    Last updated
    5 hours ago
    Downloads
    92

    No dependencies

    #moonrockz/jmespath

    A standalone JMESPath parser and JSON evaluator for MoonBit. The module uses only the core library and runs on Wasm, Wasm GC, JavaScript and native targets. It has no dependency on Smithy.

    #Query JSON

    ///|
    test {
    let input = @json.parse(
    (
    #|{"items":[{"name":"Zoe","active":true},{"name":"Ada","active":true},{"name":"Bob","active":false}]}
    ),
    )
    let result = @jmespath.search("items[?active].name | sort(@)", input)
    assert_eq(result, @json.parse("[\"Ada\",\"Zoe\"]"))
    }

    Install with moon add moonrockz/jmespath and import the module in moon.pkg. search(source, input) parses and evaluates once. Parse an expression separately to reuse it with different inputs:

    ///|
    test {
    let expression = @jmespath.parse("length(@)")
    assert_eq(expression.evaluate(@json.parse("[1,2,3]")), Json::number(3))
    assert_eq(expression.evaluate(Json::string("hello")), Json::number(5))
    }

    Neither parsing nor evaluation changes caller-owned JSON. Function arguments evaluate eagerly; logical && and || short-circuit. Projections omit null results; map retains them. Strings use Unicode code points for length and reverse. sort, max, min and the _by functions order strings by UTF-16 code unit, as Java's String.compareTo in the Smithy reference does, so U+10000 comes before U+FFFF. Object iteration order is unspecified, as in JMESPath.

    #Numbers

    Comparisons, equality, contains, sort, max and min treat numbers as the JMESPath runtime of the Smithy reference does. A number token without ., e or E is a 64-bit integer, or a big integer beyond 64 bits. A decimal token is a double when a double holds its exact value, and a big decimal otherwise. Two integers, or a big integer or big decimal on either side, compare exactly. A double on either side makes the comparison use doubles, so 9007199254740993 equals `9007199254740992.0` but not `9007199254740992` .

    The class and the exact value come from the token in the repr of a JSON number. Number literals keep their token, and to_number reads a string as a 64-bit integer or a double, as in Smithy. @json.parse keeps the token only of an integer that a double cannot hold, so give a decimal input its token in repr when its exact value matters. A number without its token is an integer when it is a whole number in the 64-bit range, and a double otherwise.

    For a string that contains ., e or E, to_number follows Java's Double.parseDouble syntax. It accepts surrounding U+0000 through U+0020, one d, D, f or F suffix, and hexadecimal floats such as 0x1.8p1. It rejects underscores. Without ., e or E, the string must be a signed 64-bit integer, so 0x1p3 and 12 return null. Overflow returns an infinite double, as in Smithy. Core Json can hold this value and comparisons can use it, but its text Infinity or -Infinity is outside the finite JSON number syntax.

    ///|
    test {
    let input = @json.parse("9007199254740993")
    assert_eq(
    @jmespath.search("@ > `9007199254740992`", input),
    Json::boolean(true),
    )
    assert_eq(
    @jmespath.search("@ == `9007199254740992.0`", input),
    Json::boolean(true),
    )
    }

    #Behaviour changes

    This release changes these results to match the Smithy reference:

    • to_number of a string gives a double when the string has ., e or E, and a 64-bit integer otherwise. A string that this rule does not parse, such as " 12 " or "9223372036854775808", gives null.
    • -0.0 == `0` is false, because the token -0.0 is a double and Java's Double.compare puts it before 0. The token -0 is an integer and equals 0.
    • A number literal keeps its token, so `1e2` prints as 1e2, not 100. The value does not change.
    • Strings order by UTF-16 code unit, not by code point.
    • sum, avg, abs, ceil and floor still compute with doubles. Their results have no token, so they compare by the rule for a number without a token.

    #Inspect expressions and errors

    Expression retains the original source and an inspectable root: Node. Each node has a kind: NodeKind and a half-open UTF-16 Span. Node kinds keep projection inputs, predicates, function arguments and expression references separate so downstream tools can analyze them without reparsing source text.

    parse, search and Expression::evaluate raise JmespathError(Diagnostic). Diagnostics contain an ErrorCode, a message and a source span. ErrorCode::to_string() returns the standard names syntax, invalid-type, invalid-value, invalid-arity and unknown-function. Diagnostic::line_column(source) returns one-based line and UTF-16 column.

    ///|
    test {
    let source = "items["
    try {
    @jmespath.parse(source) |> ignore
    fail("expected an incomplete expression")
    } catch {
    @jmespath.JmespathError(diagnostic) => {
    assert_eq(diagnostic.code, @jmespath.Syntax)
    assert_eq(diagnostic.line_column(source), (1, 7))
    }
    error => raise error
    }
    }

    Parsing allows up to 256 nested expression calls. Evaluation uses its own stack and allows up to 256 nested AST levels, including function callbacks. It also limits expression visits to 1,000,000 by default; max_steps overrides that limit on search or evaluate. Exceeding an evaluation limit raises invalid-value. Exceeding the parser nesting limit raises syntax. JSON literals must be valid JSON; use single quotes for raw string literals. Raw strings reject U+0000 through U+001F, including controls after a backslash. Textual escapes such as \n remain literal backslash-plus-letter sequences.

    #Conformance

    The module implements the JMESPath specification and its standard functions. Tests run the vendored official compliance suite on every supported target. Benchmark-only cases are excluded from correctness checks. Two literal.json cases, 'newline\n' and '\n' with actual newline characters, contradict the specification's raw-string ABNF. The runner explicitly expects syntax for those cases and checks that exactly two overrides are applied. All 892 cases still run; the other 890 retain their upstream expectations. The vendored JSON and generated embedded corpus remain unchanged. COMPLIANCE_SHA records the revision; mise run jmespath:vendor -- <git-ref> refreshes the JSON corpus and its embedded test data for offline test runs.

    Smithy contract analysis will consume this public module in a separate change.

    JmespathError

    pub(all) suberror JmespathError {
    JmespathError(Diagnostic)
    } derive(Eq,
    Debug
    )

    Diagnostic

    pub(all) struct Diagnostic {
    code : ErrorCode
    message : String
    span : Span
    } derive(Eq,
    Debug
    )

    Diagnostic::line_column

    fn Diagnostic::line_column(self : Diagnostic, source : String) -> (Int, Int)

    One-based line and UTF-16 column in the supplied expression source.

    ErrorCode

    pub(all) enum ErrorCode {
    Syntax
    InvalidType
    InvalidValue
    InvalidArity
    UnknownFunction
    } derive(Eq,
    Debug
    )

    ErrorCode::to_string

    fn ErrorCode::to_string(self : ErrorCode) -> String

    Expression

    pub(all) struct Expression {
    source : String
    root : Node
    } derive(Eq,
    Debug
    )

    A reusable parsed expression. The AST is public for downstream analyzers.

    Expression::evaluate

    fn Expression::evaluate(self : Expression, input : Json, max_steps? : Int) -> Json raise JmespathError

    Evaluate with an explicit continuation stack, including function callbacks.

    Node

    pub(all) struct Node {
    kind : NodeKind
    span : Span
    } derive(Eq,
    Debug
    )

    NodeKind

    pub(all) enum NodeKind {
    Identity
    Field(String)
    Literal(Json)
    Subexpression(Node, Node)
    Index(Node, Int)
    Slice(Node, Int?, Int?, Int?)
    Projection(Node, Node)
    ObjectProjection(Node, Node)
    Flatten(Node)
    Filter(Node, Node, Node)
    MultiList(Array[Node])
    MultiHash(Array[(String, Node)])
    Function(String, Array[Node])
    ExpressionRef(Node)
    Binary(Operator, Node, Node)
    Not(Node)
    } derive(Eq,
    Debug
    )

    Projections retain their input and per-element expression separately. ExpressionRef nodes represent late-bound expressions such as &name, including function arguments in sort_by(items, &name).

    Operator

    pub(all) enum Operator {
    Pipe
    Or
    And
    Equal
    NotEqual
    Less
    LessEqual
    Greater
    GreaterEqual
    } derive(Eq,
    Debug
    )

    Span

    pub(all) struct Span {
    start : Int
    end : Int
    } derive(Eq,
    Debug
    )

    A half-open range of UTF-16 offsets in the original expression.

    parse

    fn parse(source : String) -> Expression raise JmespathError

    Parse a standard JMESPath expression. Errors carry offsets into source.
    fn search(source : String, input : Json, max_steps? : Int) -> Json raise JmespathError

    Parse and evaluate a JMESPath expression against a JSON value.

    Powered by MoonBit

    Site sourceReport issuePackagesBuild queueSkillsStatistics

    © 2026 mooncakes.io