ALICE-LOL
English | 日本語
A text language for signed distance fields. The lol! macro (compile time) and
the runtime parser turn the same text into an
ALICE-SDF SdfNode tree, which can
be evaluated on the CPU, transpiled to GLSL / WGSL / HLSL, exported as a mesh
for 3D printing, and checked against geometric constraints whose verdict is
three-valued (satisfied / violated / undecided). A separate module,
research_law, holds unit-checked formulas with valid ranges, residuals and
provenance.
What it is for, and what it is not for
It is for writing geometry as short text that both people and language models
can produce: the grammar shipped with the crate (alice-lol/lol.gbnf)
can constrain a model's decoding so that only parseable text comes out, and the
parser turns that text into a field you can evaluate, render or print.
It is not a CAD kernel: there is no boundary representation, no assembly mating and no STEP output. Distance evaluation, compiled backends, shader transpilers and meshing belong to ALICE-SDF; this crate is the language on top of it. The geometric law checker answers questions about a field on a sampling grid and reports what it could not decide instead of passing it; it is not a finite-element solver.
Installation
The path dependencies of this repository (alice-sdf, alice-zip, and the
optional alice-physics / alice-llm) are expected as sibling checkouts when
building from source; see Building and testing.
Example
use lol;
use parse_lol;
use Vec3;
// Compile time: the macro builds the tree
let scene = lol! ;
// Run time: the same text through the parser (from a file or a language model)
let parsed = parse_lol
.unwrap;
// Both are the same field; the origin is inside the sphere
let p = new;
assert_eq!;
assert!;
// GLSL source for a shader (default feature `glsl`)
let glsl = to_glsl;
assert!;
The two front ends differ in one convention: SdfNode::box3d takes full
extents, the DSL's box3d takes half extents. Both notations of the same
shape are compared in alice-lol/tests/readme_parity.rs.
Where it sits
| Crate | Owns |
|---|---|
| ALICE-LOL | the language (macro, runtime parser, grammar, SdfNode → text emitter) and the law checkers |
| ALICE-SDF | distance functions, compiled backends (scalar / SIMD / BVH), shader transpilers, meshing |
| ALICE-Physics | fixed-point rigid bodies and the heat solver behind Constraint::ThermalField (feature physics) |
| ALICE-DetMath | sin / cos / exp / ln … with a bit-exact contract, shared instead of platform libm |
| ALICE-Zip | the law vocabulary (valid range, residual statistics, provenance, ingest policy) that research_law shares |
Keep alice-det-math unified across the resolved dependency graph: two
versions in one tree are two implementations of the same function. Check with
cargo tree -i alice-det-math.
Syntax
Every name below is accepted by alice_lol::runtime_parser::parse_lol. The
lol! macro accepts the same names except the stdlib group and
capsule_ab. Arguments are numbers (or, in the macro, {rust_expr} / bare
identifiers) followed by child expressions; the argument order of each name is
in LLM_REFERENCE.md and in the grammar. Text may be wrapped
as field Name { ... }. The runtime parser accepts // comments; the grammar,
written for model decoding, does not.
Primitives:
sphere box3d rounded_box cylinder torus cone capsule capsule_ab ellipsoid
plane octahedron rounded_cone pyramid hex_prism link capped_cone capped_torus
rounded_cylinder tube barrel heart egg helix tetrahedron box_frame diamond
star_polygon cross_shape triangle bezier triangular_prism cut_sphere
cut_hollow_sphere death_star solid_angle rhombus horseshoe vesica
infinite_cylinder infinite_cone gyroid chamfered_cube schwarz_p superellipsoid
rounded_x pie trapezoid parallelogram tunnel uneven_capsule arc_shape moon
blobby_cross parabola_segment regular_polygon stairs_prim dodecahedron
icosahedron truncated_octahedron truncated_icosahedron diamond_surface neovius
lidinoid iwp frd fischer_koch_s pmy circle_2d rect_2d segment_2d
rounded_rect_2d annular_2d terrain
CSG operations (the variadic ones fold left):
union smooth_union intersection smooth_intersection subtract smooth_subtract
chamfer_union chamfer_intersection chamfer_subtraction stairs_union
stairs_intersection stairs_subtraction xor pipe engrave groove tongue
columns_union columns_intersection columns_subtraction exp_smooth_union
exp_smooth_intersection exp_smooth_subtraction
Transforms:
translate rotate rotate_q scale scale_non_uniform
Modifiers:
round onion twist bend mirror repeat elongate revolution extrude taper
displacement polar_repeat shear noise repeat_finite octant_mirror
icosahedral_symmetry with_material surface_roughness sweep_bezier
3D-print infill (shell plus lattice):
lattice_infill diamond_infill schwarz_infill
Time:
animate morph
shopping_cart_coin skadis_panel skadis_hook_l skadis_hook_j skadis_hook_s
skadis_container skadis_clip skadis_shelf skadis_elastic_cord mug
gridfinity_bin gridfinity_bin_ex wall_hook drawer_organizer shelf_divider
sticky_note_holder business_card_holder pen_cup phone_stand headphone_holder
under_desk_mount desk_shelf monitor_riser coaster tissue_box_cover storage_box
cable_clip led_channel card_tray token_well wrench_holder socket_rail
hex_bit_holder raspi_case esp32_enclosure battery_18650_holder
toothbrush_holder drill_bit_holder pliers_rack spice_rack egg_tray
utensil_caddy filament_spool_holder nozzle_holder build_plate_rack
cutlery_tray pill_organizer magnetic_strip hairdryer_holder kcup_holder
hex_key_holder wrap_holder sock_divider soap_tray razor_holder
chopstick_holder swatch_holder tp_holder sd_card_holder driver_rack
cotton_dispenser sink_caddy clamp_rack dry_box outdoor_enclosure jewelry_stand
phone_dock cutting_board_rack tape_dispenser shower_caddy caliper_holder
bag_clip_org can_rack led_hub_box makeup_organizer vesa_mount l_bracket
t_slot_bracket_2020 raspi_mount_plate heat_set_array flange_mount
dovetail_pair profile_extrusion snap_fit_pair boss_array screw_hole tap_hole
counterbore countersink heat_set_hole bolt bracket_l flange_circular
t_slot_2020 profile_2020 profile_3030 dovetail slot snap_fit_annular
pin_hinge_knuckle boss rib bearing_seat rack_shelf cable_grommet
curtain_rod_bracket dowel_hole wood_screw_pilot arduino_mount_plate
pixhawk_mount servo_mount jst_ph_slot
runtime_parser::parse_program also reads
program(<sdf>, entities(<sdf>, ...), <intent>), where the intent is built
from these verbs (turn is the text form of the rotate verb, since rotate
is the SDF transform):
grasp release catch walk gaze point throw push pull turn align follow avoid
rest latent seq par music
Audit laws (audit_law)
An audit law states what a check must find, never how to measure it. One clause
per line; parse_law reads them and AuditLaw::evaluate answers with a
six-valued verdict (Supports / NoEvidence / Breaks / OutOfRange /
ParameterUpdate / Undecided). Whoever runs the check supplies the numbers,
so the same law can be fed from Rust or from another language.
audit evidence expect range
evidence <metric> requires that the metric was measured at all — a check that
compared nothing answers NoEvidence instead of passing. Only a finite number
greater than 0 is evidence; 0, a negative number, NaN and infinity are not.
expect <metric> == <value> [within <tol>] is the expectation, and
range <key> <value>... records a known violation: it is allowed while the
measurement matches, becomes OutOfRange once the row no longer applies, and
ParameterUpdate when the values move. The values are compared as a set (order
and duplicates are ignored). Evidence is judged before expectations, so 0 == 0 cannot pass by
accident on an empty measurement.
Law identity (law_id)
A stored law and an evaluated law are the same law only if they carry the same
name. AuditLaw::law_id derives a 32-byte identifier from the domain, the kind
of law, the arithmetic generation and the claim itself. Every field is written
with its type and its length, so ["ab", "c"] and ["a", "bc"], u32 5 and
u64 5, and an empty field and a missing field all get different identifiers.
The arithmetic generation is part of the name: the same text evaluated with
different arithmetic can answer differently, so it is a different law.
LOL_SEMANTICS_ID is the fold of LOL_SEMANTICS_PINS, and the pins are
computed from behaviour (audit_verdict_order_fingerprint,
law_input::input_reading_fingerprint,
research_law::expression_functions_fingerprint,
law_input::derivation_fingerprint) rather than written down, so changing
the order in which verdicts are reached, or how a request's inputs are read,
makes a pin disagree. An audit law's identifier also covers the type of each
x-input line (law_input::audit_law_from_file). ResearchLaw::law_id names a
research law by its claim: the name, the expression's tree (spacing and redundant
parentheses do not matter; operand order, grouping and literal bits do), the
output and inputs with their unit texts, the parameter values and each input's
valid range. Observations, the residual, provenance, oracle cases and a
parameter's uncertainty only support the claim and are left out (evaluation
does not read an uncertainty). Unit texts are compared as written, so m^3 and
m*m*m are different laws until unit parsing is pinned. SdfNode evaluation
has its own, separate identifier (below).
Example: law_id_demo; oracle:
alice-lol/tests/law_id_oracle.rs.
Geometry identity (geometry_id)
law_id above is scoped to audit law and research law: it does not cover
SdfNode's own numeric evaluation. LOL_GEOMETRY_SEMANTICS_ID is the
separate identifier for that — the upstream alice-det-math arithmetic
generation, canon_angle_deg's degree-range reduction, the arithmetic a
construct's evaluation uses (e.g. rotate/rotate_q's quaternion
construction), the accept/reject boundary of a construct's inputs (e.g.
rotate_q's unit-length tolerance, TPMS's scale/thickness validity
range), and emit's canonical text form (e.g. fmt_f32's round-trip
fidelity class). The two identifiers are independent constants: a change to
one never moves the other.
Like LOL_SEMANTICS_ID, each pin in LOL_GEOMETRY_SEMANTICS_PINS is computed
from behaviour (a fixed probe set run through the real function), never
written down by hand.
Example: geometry_id_demo; oracle:
alice-lol/tests/geometry_id_oracle.rs.
Geometric laws (law)
law::Constraint declares a property of a field; LawSet collects them with
a hard or soft priority and checks them on a grid. Each sample point ends as
satisfied, violated or undecided, and undecided points are reported in
LawReport::unresolved instead of being counted as passes. LawReport::hard_verdict
returns Proven / Violated / Undecided for the hard constraints.
The distance-dependent constraints do not read the field value as a distance (that is wrong for TPMS surfaces and inside unions): a box is proved free of surface by interval arithmetic, and an upper bound on the distance to the surface comes from a sign change between two evaluated points.
Each constraint has an evidence class (Constraint::evidence_class).
Modelled constraints rest on a model (grid resolution, a threshold, a
proxy) and cannot be added with Priority::Hard.
| Constraint | Checks | Evidence |
|---|---|---|
NonOverlap |
two fields do not overlap | Witnessed |
Containment |
one field lies inside another | Witnessed |
MinThickness |
the wall is at least a given thickness | Witnessed |
Stress |
wall thickness near load points scales with the load (geometric proxy) | Modelled |
Thermal |
surface-to-volume ratio near heat sources (geometric proxy) | Modelled |
Contact |
the gap between two fields lies in a range | Witnessed |
Continuity |
the interior is one connected region (flood fill) | Modelled |
GradientBound |
the field gradient stays under a bound | Witnessed |
Reachable |
two interior points are connected through the interior | Proved |
VolumeConservation |
volume before and after a change agrees within a tolerance | Modelled |
ThermalField (feature physics) |
the peak temperature of a solved heat field stays under a limit, bracketed between an insulated and an isothermal surface | Modelled |
Closed-form oracles: alice-lol/tests/analytic_law.rs,
alice-lol/tests/test_field_law_oracle.rs;
the grammar corpus against a brute-force refuter:
alice-lol/tests/law_corpus_oracle.rs.
Research laws (research_law)
research_law is unrelated to the geometric law module. A ResearchLaw is
a claim about data, output = f(inputs; parameters), written as text
(n*R*T/V) together with:
- units and dimensions: inputs, output and parameters carry units
(
Pa,kPa,J/(mol*K),m^3,L, …); the dimension of the expression is checked when the law is built (+/-need equal dimensions,exp/ln/sin/cosneed a dimensionless argument, the result must have the output's dimension) - valid ranges: evaluation outside the range of an input is refused
(
OutOfRange); the law does not extrapolate, and a non-finite intermediate value is an error rather than a NaN - residuals: measured from the stored observations, not reported numbers
- provenance and oracles: where the law comes from, and reference values
it has to reproduce within a tolerance (
check_oracles) - comparison:
compareevaluates two laws written in different units under the same conditions through aBridge(input-name mapping with unit conversion) - new evidence:
ingestjudges new observations, in the rule order ofalice_zip::law::SignalLaw::ingest; a parameter update is a least-squares refit of all parameters (Gauss–Newton with a fixed iteration limit and tolerance)
| Verdict | Meaning |
|---|---|
NoEvidence |
no observations were given |
OutOfRange |
some observations lie where the law cannot be evaluated; nothing was judged |
Supports |
the new observations agree with the law within the band |
ParameterUpdate |
the same expression fits stored and new evidence with refitted parameters |
ResidualGrew |
the deviation exceeds the band but not the break threshold |
Breaks |
the evidence is not described by this law |
The transcendental functions of an expression (exp, ln, sqrt, powers,
sin, cos) are evaluated through alice-det-math's f64 functions instead
of the platform math library, and the evaluation is a fixed sequence of f64
operations without fused multiply-add, so the same inputs give the same bits on
every platform. Range, residual, provenance and policy types are those of
alice_zip::law. Example: research_law_demo;
oracle: alice-lol/tests/analytic_research_law.rs.
Talking about verification state
This crate conjugates lol as a verb to name the stages a conformance claim
about it goes through (not yet checked, being checked, checked with
evidence) and who is doing the checking. See
docs/LOL_CONJUGATION_GLOSSARY.md for
the full vocabulary and worked examples against this crate's own
alice-law-conformance.toml.
Crates in this workspace
| Crate | Role |
|---|---|
alice-lol-macro |
the lol! proc-macro |
alice-lol |
runtime parser, emitter, transpile and export functions, law checkers, intent IR, stdlib shapes |
alice-lol-humanoid |
parametric humanoid template (joint FK, VRM / BVH import) |
alice-lol-robot |
intent verbs to humanoid FK and an 8-byte kinematics packet, with a safety law |
alice-lol-ui |
UI shapes (button, card, panel …), flex / grid / stack layout, contrast law |
alice-lol-datagen |
synthetic (caption, LOL) pair generator with self-check (not published) |
alice-world-auditor-types |
goal and verdict types shared by law checkers and planners |
alice-world-auditor |
planner over alice-physics worlds with three-valued verdicts (AGPL-3.0-or-later or commercial) |
Features
| Feature | Default | Description |
|---|---|---|
glsl |
yes | GLSL transpilation (to_glsl, to_glsl_dynamic, to_glsl_full) |
wgsl |
no | WGSL transpilation |
hlsl |
no | HLSL transpilation |
physics |
no | Constraint::ThermalField and material data from alice-physics (AGPL-3.0-or-later; enabling it applies that license downstream) |
roblox |
no | OBJ / FBX export within Roblox mesh limits (roblox_export) |
llm-bridge |
no | grammar-constrained generation through alice-llm (bridge; AGPL-3.0-or-later, applies downstream) |
Examples
| Example | Shows |
|---|---|
basic |
the macro and GLSL output |
showcase |
a tour of the syntax, variable capture, autodiff, CompiledSdf |
law_demo |
declaring and checking geometric laws |
research_law_demo |
the ideal-gas law in SI and in (kPa, L): re-evaluation, comparison, oracles, new evidence |
audit_law_demo |
writing a check as an audit law and reaching the 6 verdicts |
law_id_demo |
what a law's identifier is derived from, and what changes it |
geometry_id_demo |
what SdfNode evaluation's identifier is derived from, and how it differs from law_id |
audit_conformance |
the program contract of conformance/TASK.md for the audit laws, built on law_input (checked against conformance/probes.json) |
export_formats |
STL / 3MF / FBX written from the same mesh, and the resolution presets side by side |
pruning_demo |
interval-arithmetic pruning per grid cell |
autodiff_demo |
gradients, curvatures, Hessian |
compiled_demo |
compiled evaluation (single point, SIMD batch, normals) |
print_export |
STL / 3MF export |
print_verify |
numerical check of a lattice infill against the mesh |
roblox_accessory |
Roblox accessory export (feature roblox) |
prompt_to_sword |
prompt → grammar-constrained LOL → mesh (feature llm-bridge) |
alice_coaster |
a 10 cm round coaster built from an SDF pattern |
coin_dc_vs_mc |
marching cubes against dual contouring on a 1.7 mm coin |
complete_pipeline_output |
3MF export of every catalogue item in one run |
hardsurface_bolt_plate |
the fastener primitives |
hardsurface_snap_case |
the joint primitives |
hardsurface_ribbed_bracket |
the reinforcement primitives |
hardsurface_wall_bracket |
fasteners, joints, reinforcement and mounts in one part |
pattern_catalog |
the pattern registry as a catalogue |
print_demo |
structural intent for 3D printing |
royal_crown |
an ornate crown exported as OBJ |
skadis_panel_dc_vs_mc |
marching cubes against dual contouring on a perforated panel |
verify_customizers |
the customizer archetypes render the expected shape |
llm_bench |
pass rate of grammar-constrained decoding against a think-then-grammar prompt (feature llm-bridge) |
gridfinity_untrusted_input |
the fallible try_gridfinity_bin entry point refusing a degenerate dividers count instead of panicking |
parse_one_lol |
a one-shot parse_lol probe with a memory-capped allocator, used by tests/degenerate_grid.rs to run each generated case in its own process |
print_export_probe |
exports one scene's STL + 3MF to a given directory, used by tests/print_export_determinism.rs to run the same scene in two separate processes |
Run one with cargo run -p alice-lol --example <name>.
MSRV
Minimum supported Rust version: 1.90 . CI checks the whole workspace with that toolchain.
Building and testing
The crates depend on sibling repositories by path, so clone them next to this one:
Related crates
- ALICE-SDF: the field the language describes
- ALICE-Physics: deterministic physics
- ALICE-DetMath: bit-exact math functions
- ALICE-Zip: the
lawvocabulary shared withresearch_law - ALICE-Synth: defines the 8-byte payload of the
musicintent - ALICE-View: wgpu renderer
License
Apache-2.0 (LICENSE-APACHE, with NOTICE and
TRADEMARK_NOTICE) from 0.4.0; 0.3.x and earlier were released
under MIT OR Apache-2.0 and keep those terms. The exception is
alice-world-auditor (AGPL-3.0-or-later or commercial). The physics
and llm-bridge features pull in AGPL-3.0-or-later dependencies.
A redistribution (source or binary) must include LICENSE-APACHE and NOTICE; the notice can sit with the other licence texts and does not need to appear in a user interface. The names "ALICE" and "ALICE-*" are trademarks: see TRADEMARK_NOTICE, which applies whatever the licence.
The DSL exposes the ALICE-SDF primitive set, so that crate's attribution carries over: many distance-function forms follow Inigo Quilez's published articles, the stairs / columns / chamfer operators follow Mercury's hg_sdf, and the noise gradient table follows Ken Perlin. The implementations are ALICE-SDF's own Rust code; the full list is in ALICE-SDF/THIRD-PARTY-NOTICES.md.