bql 0.1.0

BQL — Bridge Query Language parser and formatter (Rust, generated).
Documentation
# 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

| Target | Package | Status |
|---|---|---|
| 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`).