Every measurement carries a validated physical unit — bit-width, numeric
base, and dimension travel inline, in the same byte stream, with no
external schema. A bare 9.81 is never ambiguous, and a
mismatched unit is a parse error. Built for instrumentation, telemetry,
and control systems where the wrong unit is a failure.
You saw the syntax — here's a spacecraft running on it. One format, the whole loop:
config in, telemetry out. Edit the orbit; the craft encodes each measurement into a
.bvnr frame and the ground station decodes it — the orbit you see is drawn only from those
decoded values, a full round-trip through the format. Units, bit-widths and types ride
inside the stream; no schema required. Hover a gauge to jump to the line it decoded.
In scientific and industrial systems, the costly failures are rarely bad syntax — they are unit confusion. A value sent in pounds-force and read as newtons. Feet read as meters. The number parsed fine; the dimension was wrong.
"A bare 9.81 could be meters per second, volts, or a dimensionless ratio. Bovnar makes that difference explicit — and a mismatched unit a parse error."
Bovnar is not a replacement for JSON in simple REST APIs, nor for Protobuf in performance-critical RPC. It is built for the place where dimensional correctness is a requirement: scientific instrumentation and metrology, industrial telemetry and control, IoT sensor networks, long-term measurement archival, and mixed text-binary log streams.
m/s and write an inline unit that doesn't match,
and the parser rejects it. Dimensional intent is checked against a built-in unit
table — not assumed, not left to the reader.
.bvnr file is UTF-8 prose you can read in any editor. The parser validates
type and unit annotations on the fly, with no external toolchain required.
uint:64, float:64).
Add them — including compound units like k~g·m/s² and Gi~B
written inline — and every value is validated. Both modes are unambiguous.
Every format is a set of trade-offs. Bovnar's are deliberate.
| Format | Human readable | Type-precise | Physical units | Schema-free | Binary embed | Multi-dim arrays |
|---|---|---|---|---|---|---|
| JSON | ✓ | ✗ | ✗ | ✓ | ✗ | ✗ |
| YAML | ✓ | partial | ✗ | ✓ | ✗ | ✗ |
| TOML | ✓ | partial | ✗ | ✓ | ✗ | ✗ |
| CBOR | ✗ | ✓ | ✗ | ✓ | ✓ | ✗ |
| Protobuf | ✗ | ✓ | ✗ | ✗ | ✓ | ✗ |
| BOVNAR | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Best forBovnar is the format to reach for when units must travel with the data and the receiver may not share a schema — scientific instrumentation & metrology, industrial telemetry & control, IoT sensor networks, and long-term measurement archival. Where a wrong unit is a failure, it is unit-safe by construction; for simple REST payloads reach for JSON, for minimal wire size reach for CBOR or Protobuf. | ||||||
Every assignment is a declaration. The type annotation is not metadata — it is part of the value itself.
on_unverified / on_verified callback pair.
No heap required for the lexer itself.
NumPy bridge that loads typed arrays
straight into an ndarray with the physical unit attached.
bvnr_read_flags_t. Both callbacks receive the event type and parsed data.loads / dumps interface. Pure-ctypes — no compiled extension required.loads(typed=True) wraps each typed value in a Quantity that preserves its exact text, bit width, and unit. Pass the dict directly back to dumps() for a lossless round-trip.numpy.ndarray — bovnar widths map to native dtypes (float:32 → float32) and the whole-array unit rides alongside. NumPy is an optional, lazily-imported extra (pip install "bovnar[numpy]").libm is the only mandatory dependency.
A unit-tagged Bovnar array is already the shape of a tensor. The optional
NumPy bridge reads it directly into an ndarray — native
dtype, original shape, and the physical unit carried alongside — so
the wire format and your analysis code never disagree about what a number
means.
ndarray in a single
call. /-rows and bracket nesting collapse to the same
shape automatically.
from_numpy /
array_to_bvnr — dtype and unit preserved. A pint
hand-off (to_pint_array) is one call further.
<uint:16> port number
cannot accidentally become a 64-bit float. Optional annotations; sensible defaults.
/ separator
creates new rows; the result is a 3×3 matrix or any rectangular array — and the
Python NumPy bridge loads it into an ndarray in one call.
Eight documents covering every layer of the stack — from a five-minute tutorial to the formal EBNF grammar and the independent conformance verification protocol.
loads/dumps and streaming Reader/Writer.Type any valid (or invalid) Bovnar below. The parser emits events in real time — every token, type annotation, and error is shown as it is recognised.