This file provides guidance to AI agents (Claude Code, GitHub Copilot, Cursor, etc.) when working with code in this repository.
gnata-sqlite is a fork of RecoLabs/gnata — a full JSONata 2.x implementation in Go. This fork extends it with:
- SQLite C extension (
sqlite/) — registersjsonata(),jsonata_query(), andjsonata_eachas SQL functions/virtual tables via CGo - Query planner (
internal/planner/) — decomposes JSONata expressions for streaming SQL aggregation - Editor/LSP (
editor/) — language server and WASM entry point for browser-based editing - CodeMirror plugin (
editor/codemirror/) — TypeScript CodeMirror 6 language support
Module path: github.com/rbbydotdev/gnata-sqlite
| Package | Purpose |
|---|---|
Root (gnata) |
Core JSONata 2.x engine — lexer, parser, evaluator, streaming. Entry points: gnata.go, stream.go |
functions/ |
55+ built-in JSONata functions (string, array, numeric, datetime, etc.). Registered via functions.RegisterAll |
internal/evaluator/ |
AST evaluation dispatch — binary ops, functions, chains, transforms. One file per eval category (eval_binary.go, eval_function.go, eval_chain.go, etc.) |
internal/parser/ |
Pratt parser, AST nodes, fast-path analysis (parser.AnalyzeFastPath) |
internal/lexer/ |
Tokenizer for JSONata expression strings |
internal/planner/ |
Query planner — decomposes JSONata for streaming SQL aggregation. Extracts paths, predicates, accumulators |
sqlite/ |
SQLite C extension via CGo — registers jsonata(), jsonata_query(), jsonata_each virtual table. Routes aggregates through planner |
sqlite/tinygo/ |
TinyGo-compatible eval subset for WASM builds |
sqlite/benchmarks/ |
SQL benchmark files for SQLite extension performance testing |
editor/ |
LSP server (native) + TinyGo WASM entry point — completions, hover, diagnostics via JSON-RPC 2.0 over stdin/stdout |
editor/codemirror/ |
npm package — CodeMirror 6 language support (TypeScript) |
wasm/ |
WASM entry point for browser playground. Exports gnataEval, gnataCompile, gnataEvalHandle |
# Run all tests (includes CGo sqlite tests)
go test ./...
# Run tests excluding sqlite (no CGo needed)
go test $(go list ./... | grep -v sqlite)
# Lint
golangci-lint run
# Build sqlite extension (macOS)
go build -buildmode=c-shared -o gnata_jsonata.dylib ./sqlite
# Build sqlite extension (Linux)
go build -buildmode=c-shared -o gnata_jsonata.so ./sqlite
# Build WASM LSP
tinygo build -o gnata-lsp.wasm -no-debug -gc=conservative -scheduler=none -panic=trap -target wasm ./editor/
wasm-opt -Oz --enable-bulk-memory gnata-lsp.wasm -o gnata-lsp.wasm
# Build WASM playground
GOOS=js GOARCH=wasm go build -ldflags="-s -w" -trimpath -o gnata.wasm ./wasm
# Build CodeMirror package
cd editor/codemirror && npm install && npm run build
# Build native LSP server
go build -o gnata-lsp ./editor/
# Run benchmarks
go test -bench=. -benchmemGitHub Actions (.github/workflows/ci.yml) runs on push/PR to main:
go test -race -count=1 ./...golangci-lintviagolangci/golangci-lint-action@v9
Lexer -> Parser -> AST Processing -> Fast-Path Analysis -> Expression
- Lexer (
internal/lexer/) — Tokenizes JSONata expression strings - Parser (
internal/parser/) — Pratt (top-down operator precedence) parser producing AST nodes - AST Processing (
parser.ProcessAST) — Normalizes and optimizes the AST - Fast-Path Analysis (
parser.AnalyzeFastPath) — Classifies expressions into:- Pure-path fast path (e.g.,
Account.Name) — uses GJSON zero-copy - Comparison fast path (e.g.,
a.b = "x") — zero allocations - Function fast path (e.g.,
$exists(a.b)) — direct GJSON evaluation - Full AST evaluation required
- Pure-path fast path (e.g.,
Eval(ctx, any)— Evaluate against pre-parsed Go values via full AST walkEvalBytes(ctx, json.RawMessage)— Fast-path expressions use GJSON directly on raw JSON bytes; full-path falls back to unmarshal + Eval
Batch-evaluates multiple expressions against events. Schema-keyed GroupPlan caching deduplicates field extraction across expressions. Lock-free reads via atomic.Pointer snapshot; writes serialized by sync.Mutex. Single JSON scan per event via gjson.GetManyBytes.
Decomposes JSONata expressions into QueryPlan for SQL streaming aggregation. Each plan contains:
Accumulators— fed per row viaStepBatch(single GJSON scan per row)FinalExpr— evaluated once at finalizationPredicates— deduplicated, evaluated once per row; accumulators reference by index- Used by
jsonata_query()SQL aggregate function in the SQLite extension
CGo extension that registers SQL functions with SQLite:
jsonata(expr, json [, bindings])— scalar function, evaluates JSONata expression against JSONjsonata_query(expr, json)— aggregate function, routes through query planner for streaming aggregationjsonata_each— virtual table for iterating JSONata results as rows- Entry point:
extension.gowith C bridge inbridge.c/bridge.h
Shared Go code compiled to either:
- Native LSP server (
main_lsp.go, build tag!js) — JSON-RPC 2.0 over stdin/stdout - TinyGo WASM (
main_wasm.go, build tagjs) — for browser integration
Supports: textDocument/didOpen, textDocument/didChange (diagnostics), textDocument/completion (schema-aware)
evaluator.Eval(node, input, env) dispatches by node.Type. Each eval category is in its own file:
eval_binary.go— Binary operators, subscripts, filteringeval_function.go— Function calls, lambdas, partial applicationeval_chain.go— Path chaining, pipes, blocks, conditionseval_sort.go— Sorting with genericSortItemsErr[T any]helpereval_transform.go— JSONata transform operator (|obj|updates|deletes|syntax)eval_group.go— Group-by reduction ({key: val}syntax)eval_range.go— Range operator ([start..end])eval_regex.go— Regex compilation and matchingeval_unary.go— Unary operators (negation, array constructor)
Dispatch-map of funcFastHandlers maps each FuncFastKind to a standalone handler function (e.g., evalFuncContains, evalFuncString). Each handler operates directly on gjson.Result for zero-copy evaluation.
| Type | File | Description |
|---|---|---|
gnata.Expression |
gnata.go |
Compiled, goroutine-safe JSONata expression with fast-path metadata |
gnata.StreamEvaluator |
stream.go |
Batch evaluator with copy-on-write expression slice + BoundedCache for schema plans |
evaluator.Environment |
internal/evaluator/env.go |
Lexical scope chain for variable bindings and function registry |
parser.Node |
internal/parser/node.go |
AST node types |
planner.QueryPlan |
internal/planner/planner.go |
Compiled execution plan for jsonata_query aggregate |
BoundedCache |
bounded_cache.go |
Lock-free FIFO ring-buffer cache (atomic pointer reads) |
OrderedMap |
internal/evaluator/ordered_map.go |
Insertion-ordered map preserving JSON field order |
- Tests use separate
_testpackages (gnata_test,lexer_test,parser_test) testdata/groups/contains 100+ test case groups ported from the jsonata-js official suitetestdata/datasets/contains JSON test fixturessuite_test.goloads 1,200+ JSON test cases — each.jsonfile hasexpr,dataset,bindings, andresultfields- Key test files:
gnata_test.go,stream_test.go,func_fast_test.go,evaluator_test.go,suite_test.go,lexer_test.go,parser_test.go,analysis_test.go - SQLite benchmarks:
sqlite/benchmarks/*.sql - CI runs tests with
-raceflag
Only one direct dependency (pure Go, no CGo for core):
tidwall/gjson— Zero-copy JSON field extraction for fast-path byte-level evaluation
The sqlite/ package requires CGo (links against SQLite via sqlite3ext.h). Regex uses Go's standard regexp package.
Register custom functions via StreamEvaluator options or standalone CustomEnv:
customFuncs := map[string]gnata.CustomFunc{
"md5": func(args []any, focus any) (any, error) { ... },
}
se := gnata.NewStreamEvaluator(nil, gnata.WithCustomFunctions(customFuncs))