Goal: author compact annotations on signs/books (or any text source) that compile into a deterministic regions + metadata structure you can ship with schematics and tools.
- Regions = unions of axis-aligned boxes (AABBs) keyed by IDs like
cpu.core - Metadata = namespaced key/values attached to regions (plus
$globaland wildcards likecpu.*) - Authoring constraints: terse on signs, multi-line friendly, stable & deterministic
This repository currently contains the spec and examples. Implementation comes next.
- Portable context for builds: label sub-assemblies, bus lines, rooms—precisely and repeatably.
- Machine-readable: consumers (renderers, validators, pipelines) can query regions and inherited metadata.
- Sign-friendly: minimal punctuation, statement markers, multi-line allowed.
An ordered array of tuples: ([sx, sy, sz], text).
[
{
"pos": [10, 64, 10],
"text": "@rc([0,0,0],[3,2,1])\n#doc.label=\"Patch A\""
},
{
"pos": [0, 64, 0],
"text": "@cpu.core=ac([100,70,-20],[104,72,-18])\n#cpu.core:logic.clock_hz=4\n#cpu.*:power.budget=\"low\"\n#$global:io.bus_width=8"
}
]posis the source block’s world coords (sign/lectern).textis raw DSL; newlines allowed.- Order is used only to derive stable IDs for anonymous regions; it must not affect semantics.
{
"$global": { "metadata": { "io.bus_width": 8 } },
"cpu.*": { "metadata": { "power.budget": "low" } },
"__anon:0:0": {
"bounding_boxes": [[[10,64,10],[13,66,11]]],
"metadata": { "doc.label": "Patch A" }
},
"cpu.core": {
"bounding_boxes": [[[100,70,-20],[104,72,-18]]],
"metadata": { "logic.clock_hz": 4 }
}
}Notes
rcis relative topos;acis absolute.- Bounds are inclusive; each box is stored as a pair of vec3 corners, normalized per axis.
- Anonymous regions appear only if they received metadata (recommended).
- Geometry starts with
@ - Metadata starts with
#
A statement starts at @ or # and ends right before the next @/# (or end of text). Newlines are allowed inside a statement.
Named regions (unified form)
@<region>=rc([x1,y1,z1],[x2,y2,z2]) // relative to this tuple’s pos
@<region>=ac([x1,y1,z1],[x2,y2,z2]) // absolute world coords
@<region>=<expr> // boolean expression over regions
Anonymous regions (no =; sign-local only)
@rc([x1,y1,z1],[x2,y2,z2])
@ac([x1,y1,z1],[x2,y2,z2])
@def(<expr>)
Booleans (Phase 0 / MVP)
<expr> := term { + term }*
term := <region> | ( <expr> )
- Operators:
+union only in Phase 0. - Other ops (
-,&,^) are reserved for a later phase.
Current region .
- Within a single tuple, the most recent geometry statement (named or anonymous).
- No current region across tuples.
Attach to current region (this tuple):
#key=<json>
Attach to an explicit target (dense):
#<target>:<key>=<json>
Where <target> is:
- a region ID (
fooorfoo.bar) - a wildcard prefix (
prefix.*) $global
Values: strict JSON (string/number/bool/null/array/object). (No computed value functions in v0.1; reserved for future.)
-
Read-time precedence per key: Exact region > longest matching wildcard(s) > $global.
-
Conflicts: different values for the same
<named target, key>across tuples → compile error (identical duplicates allowed). -
A named region is either:
- Accumulator (one or more
rc/acappend boxes), or - Defined (
@<region>=<expr>). Mixing both modes is an error.
- Accumulator (one or more
-
Anonymous region IDs derive from
(tuple_index, statement_index)—never random UUIDs. -
Deterministic output ordering is recommended:
$global, then wildcards (lexicographic), then regions (lexicographic).
@rc([0,64,0],
[31,72,15])
#doc.label="Data Loop"
#logic.clock_hz=4
@dataloop=rc([0,64,0],[31,72,15])
@dataloop.alu=rc([8,64,8],[22,70,14])
@dataloop.registers=rc([2,64,2],[12,69,6])+rc([14,64,2],[24,69,6])
#dataloop:doc.label="Data Loop"
#dataloop.registers:logic.word_size=8
@core=dataloop.alu+dataloop.registers
#core:doc.note="ALU ∪ registers"
@cpu.bus=ac([100,70,-20],[132,78,-5])
#cpu.bus:io.direction="east-west"
#cpu.*:logic.clock_hz=4
#$global:io.bus_width=8
@dataloop.registers=rc([2,64,2],
[12,69,6])
+ rc([14,64,2],
[24,69,6])
#dataloop.registers:doc.note="two banks"
Lexical
digit = "0"…"9" ;
int = ["-"], digit, { digit } ;
region-id = 1*( ALNUM | "_" | "." ) ; // [A-Za-z0-9_.]+
key = region-id ;
vec3 = "[", int, ",", int, ",", int, "]" ;
box = vec3, ",", vec3 ;
Program
input = { stmt } ;
stmt = geom | meta ;
Geometry
geom = "@", ( named-geom | anon-geom ) ;
named-geom = region-id, "=", ( "rc(", box, ")"
| "ac(", box, ")"
| expr ) ;
anon-geom = "rc(", box, ")"
| "ac(", box, ")"
| "def(", expr, ")" ;
expr = term, { "+", term } ; // Phase 0: union only
term = region-id | "(", expr, ")" ;
Metadata
meta = "#", ( targeted-meta | current-meta ) ;
targeted-meta = meta-target, ":", key, "=", json ;
current-meta = key, "=", json ;
meta-target = "$global" | region-id | region-id, ".*" ;
json = RFC 8259 JSON literal ;
Add to your Cargo.toml:
[dependencies]
insign-core = "0.1.1"
# For FFI bindings (Kotlin/JVM integration)
insign-ffi = "0.1.1"
# For WASM bindings (Web/Node.js)
insign-wasm = "0.1.1"Basic usage:
use insign_core::compile;
let input = vec![
([10, 64, 10], "@rc([0,0,0],[3,2,1])\n#doc.label=\"Patch A\"".to_string()),
];
let result = compile(&input)?;
let json = serde_json::to_string_pretty(&result)?;
println!("{}", json);Install from crates.io:
cargo install insign-core --features=cliOr download pre-built binaries from the GitHub Releases page.
echo '{"pos": [0,0,0], "text": "@rc([0,0,0],[1,1,1])\\n#test=1"}' | insign-cli --prettyDownload the appropriate native library from GitHub Releases:
- Linux:
libinsign_ffi.so - macOS:
libinsign_ffi.dylib - Windows:
insign_ffi.dll
// Example Kotlin integration (requires JNA)
val library = Native.load("insign_ffi", InsignLibrary::class.java)
val json = "[{\"pos\":[0,0,0],\"text\":\"@rc([0,0,0],[1,1,1])\"}]"
val result = library.compile_json(json)
println(result)Node.js:
npm install @schem-at/insign-wasmconst { compile_json } = require('@schem-at/insign-wasm');
const input = JSON.stringify([
{ pos: [0,0,0], text: "@rc([0,0,0],[1,1,1])\n#test=1" }
]);
const result = compile_json(input);
console.log(JSON.parse(result));Browser:
<script type="module">
import init, { compile_json } from './pkg/insign.js';
async function run() {
await init();
const input = JSON.stringify([
{ pos: [0,0,0], text: "@rc([0,0,0],[1,1,1])\n#test=1" }
]);
const result = compile_json(input);
console.log(JSON.parse(result));
}
run();
</script># Clone the repository
git clone https://github.com/Schem-at/Insign.git
cd Insign
# Build all packages
cargo build --all --release
# Run tests
cargo test --all
# Build FFI library
cargo build -p insign-ffi --release
# Build WASM package
cd crates/insign-wasm
wasm-pack build --release --target nodejs --out-dir pkg --out-name insignTo verify that FFI and WASM produce identical outputs:
./tools/scripts/parity-simple.shMIT (see LICENSE in this repository).