Sign in

    jq

    A jq clone implemented in MoonBit

    jq
    json
    moonbit
    Download zip
    Author
    Version
    0.2.0
    License
    Apache-2.0
    Last updated
    7 months ago
    Downloads
    2K

    #mizchi/jq

    A jq clone implemented in MoonBit.

    #Install CLI

    moon install mizchi/jq/cmd/moonjq

    This installs moonjq to ~/.moon/bin/moonjq.

    echo '{"name":"alice","age":30}' | moonjq '.name' # "alice" echo '[1,2,3,4,5]' | moonjq 'map(. * 2) | add' # 30 echo '[{"a":1},{"a":2},{"a":3}]' | moonjq '[.[] | select(.a > 1)]' # [{"a":2},{"a":3}]

    #Use as Library

    moon add mizchi/jq

    let results = @jq.run(".foo", @json.parse("{\"foo\":42}"))
    // results = [Number(42)]

    // Pre-compile for repeated use
    let filter = @jq.compile("map(. + 1)")
    let r1 = @jq.run_compiled(filter, @json.parse("[1,2,3]"))
    // r1 = [Array([Number(2), Number(3), Number(4)])]

    See README.mbt.md for full doc-tested API examples.

    #Compatibility

    96.2% compatible with jq 1.8.1 (252/262 verified tests).

    429 of 514 jq.test cases ported. 496 MoonBit tests total, all passing.

    #Supported Features

    CategoryFeatures
    Basic., .foo, .[n], .[], .., .[m:n]
    Operators\|, ,, + - * / %, == != < > <= >=, and or not, //
    Assignment=, \|=, +=, -=, *=, /=, %=, //=
    Construction[expr], {k: v}, {foo} (shorthand), (expr), "\(.expr)"
    Controlif-then-elif-else-end, try-catch, .foo?, foreach, while, until, label/break
    Bindingas $x, as [$a,$b], as {a:$x}, ?//, reduce, def
    Pathpath(), setpath(), getpath(), delpaths(), del(), pick()
    Builtinslength, keys, values, type, select, map, map_values, empty, add, sort, sort_by, group_by, unique, unique_by, reverse, flatten, min, max, min_by, max_by, first, last, limit, range, has, contains, inside, any, all, tostring, tonumber, toboolean, tojson, fromjson, ascii_downcase, ascii_upcase, split, join, startswith, endswith, ltrimstr, rtrimstr, trim, ltrim, rtrim, trimstr, abs, fabs, floor, ceil, round, explode, implode, to_entries, from_entries, with_entries, walk, transpose, bsearch, indices, index, rindex, not, recurse, debug, IN, INDEX, error, paths, isempty, nth, skip, builtins, utf8bytelength, $__loc__
    Mathsqrt, sin, cos, atan, atan2, log, log2, exp, exp2, pow, infinite, nan, isnan, isinfinite, isfinite, isnormal
    Formats@base64, @base64d, @json, @text, @html, @uri
    Typesnumbers, strings, booleans, nulls, arrays, objects, iterables, scalars

    #Excluded Tests (85 of 514)

    FeatureTestsNotes
    import/include10Module system (out of scope)
    input1Multi-input I/O

    #Remaining Incompatibilities (10 of 262 tested)

    All caused by platform constraints (IEEE 754 double / JS string handling):

    IssueTestsDetail
    Large integers > 2^534Overflow to Infinity instead of exact representation
    -0 handling1abs on -0.0 outputs 0 instead of -0
    NUL byte (\u0000) in strings2contains and toboolean misbehave with embedded NUL
    path() error message1Missing result value in error message
    pick(last) negative index1Different error from jq
    fromjson error message1MoonBit JSON parser produces different error text

    #Quick Commands

    just # check + test just fmt # format code just check # type check just test # run tests just moonjq FILTER # run CLI

    #License

    Apache-2.0

    #mizchi/jq

    A jq clone implemented in MoonBit. Provides compile, run, and run_string for JSON query/transform.

    #Quick Start

    ///|
    test {
    inspect(
    @jq.run_string(".foo", "{\"foo\":42}"),
    content=(
    #|["42"]
    ),
    )
    }

    #API

    #run_string — One-shot string-based execution

    ///|
    test {
    inspect(
    @jq.run_string("[.[] | select(. > 3)]", "[1,2,3,4,5]"),
    content=(
    #|["[4,5]"]
    ),
    )
    }

    #run — Execute with Json value

    ///|
    test {
    let input = @json.parse("{\"a\":1,\"b\":2}")
    let results = @jq.run("{sum: (.a + .b)}", input)
    inspect(
    results[0].stringify(),
    content=(
    #|{"sum":3}
    ),
    )
    }

    #compile + run_compiled — Pre-compile for repeated use

    ///|
    test {
    let filter = @jq.compile("map(. * 2)")
    let r1 = @jq.run_compiled(filter, @json.parse("[1,2,3]"))
    let r2 = @jq.run_compiled(filter, @json.parse("[10,20]"))
    inspect(r1[0].stringify(), content="[2,4,6]")
    inspect(r2[0].stringify(), content="[20,40]")
    }

    #Error Handling

    ///|
    test {
    let result = @jq.run_string(".foo", "not json") catch {
    @jq.JqError::JqError(msg) => ["Error: " + msg]
    }
    inspect(result[0].has_prefix("Error:"), content="true")
    }

    #Examples

    #Field Access and Pipes

    ///|
    test {
    let input = "{\"user\":{\"name\":\"alice\",\"age\":30}}"
    inspect(
    @jq.run_string(".user.name", input),
    content=(
    #|["\"alice\""]
    ),
    )
    inspect(
    @jq.run_string(".user | keys", input),
    content=(
    #|["[\"age\",\"name\"]"]
    ),
    )
    }

    #Array Operations

    ///|
    test {
    let arr = "[3,1,4,1,5,9,2,6]"
    inspect(
    @jq.run_string("sort | unique", arr),
    content=(
    #|["[1,2,3,4,5,6,9]"]
    ),
    )
    inspect(
    @jq.run_string("map(. * 2) | add", arr),
    content=(
    #|["62"]
    ),
    )
    }

    #Reduce

    ///|
    test {
    inspect(
    @jq.run_string("reduce .[] as $x (0; . + $x)", "[1,2,3,4,5]"),
    content=(
    #|["15"]
    ),
    )
    }

    #Object Construction

    ///|
    test {
    let input = "[{\"name\":\"a\",\"val\":1},{\"name\":\"b\",\"val\":2}]"
    inspect(
    @jq.run_string("map({(.name): .val}) | add", input),
    content=(
    #|["{\"a\":1,\"b\":2}"]
    ),
    )
    }

    #Conditionals

    ///|
    test {
    inspect(
    @jq.run_string(
    "map(if . > 0 then \"pos\" elif . == 0 then \"zero\" else \"neg\" end)", "[-1,0,1]",
    ),
    content=(
    #|["[\"neg\",\"zero\",\"pos\"]"]
    ),
    )
    }

    #User-Defined Functions

    ///|
    test {
    inspect(
    @jq.run_string(
    "def double: . * 2; def inc: . + 1; map(double | inc)", "[1,2,3]",
    ),
    content=(
    #|["[3,5,7]"]
    ),
    )
    }

    #Format Strings

    ///|
    test {
    inspect(
    @jq.run_string("@base64", "\"hello\""),
    content=(
    #|["\"aGVsbG8=\""]
    ),
    )
    inspect(
    @jq.run_string("@base64d", "\"aGVsbG8=\""),
    content=(
    #|["\"hello\""]
    ),
    )
    }

    JqError

    pub(all) suberror JqError {
    JqError(String)
    }

    Error type for jq compilation and evaluation failures.
    impl Show for JqError

    ArithOp

    pub enum ArithOp {
    Add
    Sub
    Mul
    Div
    Mod
    }

    Arithmetic operation type used in filter expressions.
    impl Show for ArithOp

    CmpOp

    pub enum CmpOp {
    Eq
    Ne
    Lt
    Gt
    Le
    Ge
    }

    Comparison operation type used in filter expressions.
    impl Show for CmpOp

    Filter

    pub enum Filter {
    Identity
    RecurseOp
    Field(String)
    Index(Int)
    Slice(Int?, Int?)
    Iterate
    Pipe(Filter, Filter)
    Comma(Filter, Filter)
    Literal(Json)
    ArrayConstruct(Filter?)
    ObjectConstruct(Array[(ObjKey, Filter)])
    Paren(Filter)
    Try(Filter)
    TryCatch(Filter, Filter)
    IfThenElse(Filter, Filter, Filter)
    Binding(Pattern, Filter, Filter)
    Reduce(Filter, Pattern, Filter, Filter)
    FuncDef(String, Array[String], Filter, Filter)
    FuncCall(String, Array[Filter])
    DynIndex(Filter)
    PostfixDynIndex(Filter, Filter)
    DynSlice(Filter, Filter?, Filter?)
    Neg(Filter)
    Arith(ArithOp, Filter, Filter)
    Compare(CmpOp, Filter, Filter)
    LogicAnd(Filter, Filter)
    LogicOr(Filter, Filter)
    Alternative(Filter, Filter)
    Foreach(Filter, Pattern, Filter, Filter, Filter?)
    StringInterp(Array[(String, Filter?)])
    Assign(Filter, Filter)
    Update(Filter, Filter)
    UpdateAlt(Filter, Filter)
    Label(String, Filter)
    Break(String)
    BindingAlt(Array[Pattern], Filter, Filter)
    }

    A compiled jq filter AST node.
    impl Show for Filter

    ObjKey

    pub enum ObjKey {
    StrKey(String)
    ExprKey(Filter)
    }

    Object key type: either a static string or a dynamic expression.
    impl Show for ObjKey

    Pattern

    pub enum Pattern {
    PatVar(String)
    PatArray(Array[Pattern])
    PatObject(Array[(String, Pattern)])
    }

    Pattern for destructuring bind in 'as' expressions.
    impl Show for Pattern

    compile

    fn compile(filter_str : String) -> Filter raise JqError

    Compile a jq filter string into a reusable Filter AST.

    run

    fn run(filter_str : String, input : Json) -> Array[Json] raise JqError

    Compile and execute a jq filter string against JSON input.

    run_compiled

    fn run_compiled(filter : Filter, input : Json) -> Array[Json] raise JqError

    Execute a pre-compiled filter against JSON input.

    run_string

    fn run_string(filter_str : String, input_str : String) -> Array[String] raise JqError

    Compile and execute a jq filter, with JSON string input/output.