# BQL — Bridge Query Language
Grammar-driven, multi-language parser **and** formatter libraries for BQL — a
small JQL-flavoured text syntax for the `FieldFilterGroup` boolean-filter tree.
One source-of-truth grammar (`spec/`) generates **native** libraries for
several languages, each exposing both directions:
- **`parse(bql: string) → Ast`** — SQL → Struct
- **`format(ast: Ast) → string`** — Struct → SQL
`parse(format(ast)) === ast` for every AST that `parse` produces (round-trip
invariant). Value / field payloads are carried **verbatim** — the lexer keeps
the raw slice between the quotes and the formatter re-wraps it without
escaping — so an AST built by hand must use the same raw form the lexer would
produce: pass string-literal bodies through the generated `escape` helper, keep
enum values plain (not number/date/participant shaped), and give participants a
recognised `type` with bare-word-safe `type`/`id`. A hand-built enum value that
would otherwise re-parse as a different type is quoted — and `escape`d inside the
quotes so it cannot inject — then re-parses as a string.
Alongside `escape`, each library exposes:
- **`unescape`** — the inverse of `escape`: decode a raw string-literal body
(the full escape set `\" \\ \/ \n \t \r \b \f \uXXXX`) back to logical text,
erroring on a malformed escape.
- canonical numeric value constructors from native numbers (`integer`, `float`);
`float` always carries a decimal point/exponent so it never re-lexes as an
integer. An integer-shaped literal always stays an integer — an out-of-i32
value is a parse error, never silently a float.
In the **Rust** library, text payloads (fields, string / enum / numeric / date
values, participant `type`/`id`) are carried as `Cow<'a, str>`: `parse` always
borrows out of the input (`Cow::Borrowed`), so parsing stays zero-copy, while an
AST can equally be built from owned `String`s (`Cow::Owned`) without an input
buffer. Each borrowing node has an inherent `into_owned(self) -> Node<'static>`
that deep-clones every borrowed string, turning a parsed tree into a fully
owned `'static` one that can outlive the input it was parsed from. Behind the
optional `chrono` feature it also gains typed `Value` date/datetime constructors
and accessors.
## Languages
| Rust | `bql` (`crates/bql/`) | generated (parser + formatter) |
| TypeScript | `bql-ts/` (`@mathandmagic/bql`) | generated (parser + formatter) |
| Swift | repository package (`BQL` module) | generated (parser + formatter) |
| Kotlin | `ai.mathandmagic:bql` (`ai.mathandmagic.bql`) | generated (parser + formatter) |
## Install
All language packages use the same release version.
### Rust
```bash
cargo add bql
```
### TypeScript
```bash
npm install @mathandmagic/bql
```
### Swift
```swift
.package(
url: "https://github.com/MathAndMagic/bql.git",
from: "0.1.0"
)
```
The Swift package supports macOS 13+, iOS/tvOS 16+, and watchOS 9+.
### Kotlin/JVM
```kotlin
dependencies {
implementation("ai.mathandmagic:bql:0.1.0")
}
```
## How it works
```
spec/bql.ungram ─┐
├─► bql-grammar ─► Grammar (IR) ─► bql-codegen ─►┬─► crates/bql (Rust)
spec/bql.rules ─┘ ├─► bql-ts (TypeScript)
├─► bql-swift (Swift)
└─► bql-kotlin (Kotlin)
▲
corpus/ ────────────────────────── conformance runner ───────────────────┘
(golden: bql → ast.json → pretty.bql)
```
- `spec/bql.ungram` — AST shape in [ungrammar](https://crates.io/crates/ungrammar)
notation (the same notation rust-analyzer uses to describe the Rust AST).
- `spec/bql.rules` — EBNF productions + annotations: the boolean-expression
layer (`@expr` / `@binary` / `@prefix` / `@group`), value-type inference,
participant prefixes, and round-trip rules. (The De Morgan / IN-BETWEEN
lowering entries document the *consumer's* reduction to `FieldFilterGroup`;
the generated parser keeps a compact `Not` node and scalar `IN` as leaves.)
- `crates/bql-codegen` — the generator. Each emitter walks the concrete-syntax
productions (`Node::syntax`) and annotations in the `Grammar` IR to emit the
AST types, lexer, recursive-descent parser, and pretty-printer; no grammar
shape is hard-coded. Rust output is canonicalised through `rustfmt`.
- `corpus/` — the golden corpus. Every language must parse each input to the
identical canonical `ast.json`, format back to the identical `pretty.bql`,
and report the same errors. This is the linchpin that keeps all language
implementations honest.
## Quickstart
```bash
make gen # regenerate Rust + TS + Swift + Kotlin libraries from spec/
make test # Rust + TS + Swift + Kotlin tests (each runs the corpus/ conformance suite)
make ci # full gate: fmt + clippy + build + test + gen-diff
```
The golden `corpus/` is the cross-language source of truth; each language
checks it from its own test suite, so `make test` already asserts conformance.
## Toolchain
- Rust 1.92.0+ (pinned via `rust-toolchain.toml`).
- Node 24+ for the TypeScript package.
- Swift 5.9+ for the Swift package (`make test-swift` runs it inside the
`swift:6.0` Docker image, so no host Swift toolchain is required).
- Kotlin/JVM for the Kotlin package (`make test-kotlin` runs it inside the
`gradle:8.14.3-jdk21` Docker image, so no host JDK/Gradle is required).
## License
Dual-licensed under [MIT](LICENSE-MIT) or [Apache-2.0](LICENSE-APACHE), at your
option (`MIT OR Apache-2.0`).