Lexical Structure

This page restructures §2 of the specification as a reference for implementers. Osmol's lexical layer is small on purpose, but it is opinionated: the token set itself encodes the language's physics of knowledge, and one class of tokens exists only to die loudly.

Source form

Osmol source is UTF-8. Comments run from -- to end of line:

hold eta(dinner) = 19:40 ±5m, decays 20:00   -- uncertainty and decay, inline

Identifiers are lowercase kebab-case or dotted paths:

banh-mi-house
atlas.budget
status(atlas.review)

There is no uppercase convention to learn because there are no types to name, no classes to export, and no constants to shout. A twin's vocabulary is facts about a life, and facts are lowercase.

Literal forms

The literal forms are chosen so that time, uncertainty, and decay (the physics of knowledge) are first-class tokens rather than library calls. A value in Osmol is a datum with a shelf life, an error bar, and a resolution.

FormExamplesMeaning
Instant19:40, fri, 2026-08-01, +45mA point in time; +45m is relative to now
Periodday, week, 30mA duration, used by every and budgets
Time range22:00-07:00An interval, used by quiet
Quantity with unit12km, 3 seatsNumbers carry their units
Uncertainty suffix±5m, ±0.2An explicit error bar on a value
Confidence suffix@0.8Assertion confidence; defaults to @1.0
Granularity graincoarse(30m), coarse(city), coarse(week)A resolution on the granularity lattice

A holding like hold eta(dinner) = 19:40 ±5m @0.9, decays 20:00 is therefore expressible without a single library import: the instant, its uncertainty, its confidence, and its expiry are all lexical.

Strings

Strings are double-quoted and legal only inside express. This is Axiom V doing lexical work: prose is a controlled substance. Everywhere else in the language, values must be typed literals a machine can reason about: an eta is an instant, not a sentence about an instant. Free text exists in exactly one construct, the human lane, where it crosses the mesh verbatim and provenance-stamped. A string outside express is not a style violation; it does not tokenize.

Reserved words

The reserved words, exactly as spec §2 lists them:

twin, hold, seek, owe, decide, among, express, to, membrane, attention,
bond, trust, assume, yield, role, stake, on, toward, by, else, when,
decays, until, every, interrupts, quiet, threshold, escalate, sync,
drop, exact, coarse, category, existence, deny, family, team, others, all

Thirty-nine words. This is the entire keyword surface of the language, and none of them is a verb of transmission.

Tombstone words

A second reserved list exists for one purpose only, producing a compile error:

send, notify, broadcast, blast, cc, bcc, forward, reply

These eight words are reserved solely to fire error[O-000]: there is no send. They have no production in the grammar, no semantics, and no future. They exist so that the old world's verbs die loudly rather than quietly. A newcomer who types send gets the whole philosophy of the language in one diagnostic instead of a confusing unexpected identifier.

Implementers: the tombstone check should be the fastest error your toolchain can produce. The engineering dissertation places it at the lexer, before parsing exists.

Implementation note: how v0.1 tokenizes

The reference interpreter (osmol.py, normative for v0.1 behavior) does not run a general tokenizer. It works line by line: each line is stripped of comments, then matched against a table of regular expressions (the RX table), one pattern per declaration form. A line that matches no pattern is an unrecognized declaration, and that is a hard error. There is no "unknown statement, skipping" in this language.

Two ordering rules in osmol.py are worth carrying into any implementation:

  1. Tombstones are checked before anything else. Every line passes tombstone_check before any declaration pattern is tried, so send dies with O-000 even inside an otherwise unparseable line.
  2. Strings are stripped first. The tombstone check removes double-quoted spans before scanning for tombstone words, because strings are payload: express to raj: "I will reply tomorrow" is legal. The human lane may say anything; only the grammar is forbidden the old verbs.

The line-oriented approach is a documented v0.1 pragma, not the language's final word. The Rust core specifies a handwritten lexer with proper spans (dissertation, Chapter 2). But the two rules above are semantics, not implementation detail: keep them.