Skip to content

Kuzat/jslt-rs

Repository files navigation

JSLT-RS

A Rust implementation of the JSLT (JSON Selection and Transformation Language) with a small engine API and a browser WASM demo.

Current status: Full parser, binder, and evaluator with comprehensive stdlib support. All core JSLT features are implemented, including expressions, functions, comprehensions, and 45+ built-in functions. Includes a built-in code formatter and LSP server. A live WASM demo is available at GitHub Pages.

Building

This is a workspace-based Rust project. To build:

# Check all crates for compilation errors
cargo check --workspace

# Build all crates
cargo build --workspace

# Run tests for all crates
cargo test --workspace

# Build specific crate
cargo build -p engine

# Build CLI tool
cargo build -p cli --release

# Run CLI tool
cargo run -p cli -- <args>

CLI Usage

The CLI tool allows you to transform JSON data using JSLT programs and format JSLT code.

Commands

  • jslt run (or just jslt for backward compatibility) - Execute JSLT transformations
  • jslt format - Format JSLT source code

Basic Usage

# Transform JSON using a JSLT program file and JSON input file
cargo run -p cli -- -p <program.jslt> -i <input.json>

# Use inline expressions with --eval
cargo run -p cli -- -e '.name' -i <input.json>

# Read input from stdin
echo '{"name": "alice"}' | cargo run -p cli -- -e '.name'

# Pretty-print output
cargo run -p cli -- -p <program.jslt> -i <input.json> --pretty

CLI Options

  • -p, --program <FILE> - Path to a JSLT program file (use - to read from stdin)
  • -e, --eval <EXPR> - Inline JSLT expression to evaluate (conflicts with --program)
  • -i, --input <FILE> - JSON input file (use - to read from stdin, or omit to read from stdin)
  • --pretty - Pretty-print the output JSON

Examples with Sample Files

The repository includes example programs and inputs in the examples/files/ directory:

examples/files/
├── inputs/
│   ├── a-b-numbers.json    # {"a": 1, "b": 2}
│   └── empty.json          # {}
└── programs/
    ├── add-a-b.jslt        # {"sum": .a + .b}
    └── hello.jslt          # {"hello": "world"}

Example 1: Add two numbers

cargo run -p cli -- -p examples/files/programs/add-a-b.jslt -i examples/files/inputs/a-b-numbers.json
# Output: {"sum":3}

Example 2: Simple hello world transformation

cargo run -p cli -- -p examples/files/programs/hello.jslt -i examples/files/inputs/empty.json
# Output: {"hello":"world"}

Example 3: Using inline expressions

# Extract a field
echo '{"name": "alice", "age": 30}' | cargo run -p cli -- -e '.name'
# Output: "alice"

# Transform with object construction
echo '{"a": 2, "b": 3}' | cargo run -p cli -- -e '{"sum": .a + .b, "product": .a * .b}'
# Output: {"sum":5,"product":6}

Example 4: Pretty-printed output

cargo run -p cli -- -p examples/files/programs/add-a-b.jslt -i examples/files/inputs/a-b-numbers.json --pretty
# Output:
# {
#   "sum": 3
# }

Code Formatting

The CLI includes a built-in formatter for JSLT code. The formatter ensures consistent style and spacing across your JSLT files.

Formatter Usage

# Format to stdout
jslt format <file.jslt>

# Format from stdin
cat program.jslt | jslt format -

# Format in-place
jslt format --write <file.jslt>

# Check if files are formatted (CI mode)
jslt format --check <file.jslt>

# Use custom configuration file
jslt format --config .jsltfmt <file.jslt>

Configuration

Create a .jsltfmt file in your project root to customize formatting:

indent_width = 2
max_width = 100
indent_style = "spaces"  # or "tabs"
trailing_comma = "never"  # or "always"

The formatter will automatically search for .jsltfmt in the current directory and parent directories.

Features

  • Consistent spacing: Proper spacing around operators, commas, and keywords
  • Smart line breaking: Arrays and objects break to multi-line when exceeding max_width
  • Idempotent: Running the formatter multiple times produces the same output
  • Configurable indentation: Choose between spaces/tabs and set indent width

Examples

Before formatting:

def  add(x,y)    x+y
let   foo=1
{"result":.data}

After formatting:

def add(x, y) x + y

let foo = 1

{"result": .data}

Browser/WASM Quickstart

The repository includes a WASM wrapper crate and a minimal browser demo.

Prerequisites

  • Rust stable and wasm32 target:
rustup target add wasm32-unknown-unknown
  • wasm-pack (builds the web-friendly JS/WASM bundle):
cargo install wasm-pack
  • A static HTTP server to host the demo directory (choose one):
    • Node (no install): npx http-server
    • Python 3: python3 -m http.server

Build the WASM bundle

This produces a JS glue file and a .wasm module under examples/wasm/pkg/.

# From the repo root
  wasm-pack build crates/wasm --release --target web --out-dir ../../examples/wasm/pkg

Notes:

  • --target web outputs an ES module you can import from a <script type="module">.
  • Re-run this command any time you change Rust code in the WASM wrapper or engine.

Run the demo page

Serve the demo directory to avoid CORS issues and import the generated module.

# From the repo root, serve the examples/wasm directory:
  npx http-server examples/wasm -p 8080
  # or
  python3 -m http.server -d examples/wasm 8080

Open http://localhost:8080 in your browser.

Try this example:

  • Program:
{ "greet": .name }
  • Input:
{ "a": 2, "b": 3, "name": "alice" }

Expected output:

{
    "greet": "alice"
  }

Troubleshooting

  • Seeing {} instead of your object?
    • Hard refresh to bust the browser cache so the fresh pkg/ is used.
    • Ensure you built with --target web and are serving over HTTP, not file://.
  • “WASM not loaded” or module import errors:
    • Confirm the server is running from examples/wasm/ and pkg/ exists.
    • Check the browser console for network errors (404 for pkg/jslt_wasm.js or the .wasm file).

Project Structure

This project is organized as a Rust workspace with the following crates and directories:

Crates:

  • crates/ast/ - AST types with spans and pretty-printer
  • crates/lexer/ - Hand-rolled lexer, no dependencies
  • crates/parser/ - Pratt parser that produces AST
  • crates/value/ - JsltValue facade over serde_json::Value
  • crates/interp/ - Evaluator with environments and binding/linking
  • crates/stdlib/ - Built-in functions registry and implementations (45+ functions)
  • crates/engine/ - Public API for compile/apply operations
  • crates/cli/ - Command-line interface
  • crates/wasm/ - wasm-bindgen wrapper for browser/WASM deployment

Additional directories:

  • examples/wasm/ - Browser demo with HTML/JS frontend (deployed to GitHub Pages)
  • conformance/ - Test cases and reference test harness
  • docs/ - Language specification and stdlib documentation

Development Status

This project is in active development with most core features completed:

Completed:

  • Lexer, parser, and AST with full JSLT grammar support
  • Expression evaluator with proper null propagation and short-circuiting
  • Name resolution and binding for variables and functions
  • All 45+ standard library functions (string, numeric, array, object, boolean, time, regex, URL)
  • Conformance test suite with reference implementation comparison
  • WASM bindings with live browser demo deployed to GitHub Pages
  • CI/CD pipeline with automated builds and deployments
  • Module import system design
  • CLI improvements (pretty printing, eval mode)

🚧 In Progress:

  • Error message polish and code frame display
  • Language Server Protocol (LSP) support

📋 Planned:

  • Node.js, JVM, Python bindings
  • Extension function system

See TODO.md for the detailed implementation plan.

License

Apache License, Version 2.0 - see LICENSE file for details.

About

Experimental implementation of JSLT in rust

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages