Author: Percy Rojas Masgo — Condesi Perú / Qomni AI Lab Version: 2.2 · License: MIT · Date: April 2026
- Overview
- Lexical Structure
- Type System
- Grammar (Full EBNF)
- Constructs
- Expressions
- Built-In Functions
- Execution Model
- Error Handling
- Response Format
- Versioning
- Compiler Architecture
- Development Tools
- Changelog
QOMN (QOMN Language) is a declarative domain-specific language for expressing deterministic engineering calculations as self-contained plan programs.
A QOMN program:
- Accepts typed numeric parameters
- Computes intermediate values via arithmetic expressions
- Validates inputs via assertions
- Declares named outputs with labels and units
- Embeds metadata (standard, source, domain) for traceability
- Compiles to WASM and executes deterministically — no randomness, no LLM
A single .qomn file may contain multiple plan_* declarations.
You do not need to know Rust, Python, or any programming language to write a QOMN plan. If you can write an engineering formula in a spreadsheet, you can write a QOMN plan.
Minimal viable plan — 10 lines with annotations:
plan_pipe_velocity( // 1. Name your calculation Q_lps: f64, // 2. Declare inputs with type (f64 = decimal number) D_mm: f64 // 3. One input per line; add a comment explaining units ) { meta { // 4. Required: cite the standard you are using standard: "IS.010 Peru — Instalaciones Sanitarias", source: "IS.010:2006 §3", domain: "hidraulica", version: "2.2", } let D_m = D_mm / 1000.0; // 5. Convert units (mm → m) let A = 3.14159 * D_m * D_m / 4.0; // 6. Compute intermediate values let V = Q_lps / (A * 1000.0); // 7. Final result (m/s) formula "Velocity": "V = Q / A"; // 8. Document the formula (required for PR) assert Q_lps > 0.0 msg "flow must be positive"; // 9. Validate each input assert D_mm > 0.0 msg "diameter must be positive"; output V label "Flow velocity" unit "m/s"; // 10. Declare what to return }That is a complete, runnable QOMN plan. Send it to the API and receive an exact answer with the standard cited — no spreadsheet errors, no approximations.
Single-line only:
// This is a comment
Block comments are not supported.
Reserved words that cannot be used as identifiers:
plan_ const let formula assert msg output label unit
meta true false version
Note:
label,unit,msg, andversionare reserved to prevent ambiguity with the built-in keywords used inoutputandmetablocks.
identifier ::= [a-zA-Z][a-zA-Z0-9_]*- Must start with a letter (uppercase or lowercase)
- May contain letters (any case), digits, and underscores
- Case-sensitive (
HP_reqandhp_reqare distinct identifiers) - Plan names must begin with
plan_
Valid: Q_gpm, h_loss, v_min, HP_req, plan_pump_sizing, RHO_CU
Invalid: 2x (digit start), _var (underscore start)
String literals support standard escape sequences:
\"— double quote\\— backslash\n— newline\t— tab
Other escape sequences are not supported and will produce a parse error.
Negative number literals are not supported directly. Use unary minus:
let x = -5.0; where - is a unary operator applied to the literal 5.0.
number ::= [0-9]+ ('.' [0-9]+)?
string_lit ::= '"' [^"]* '"'
bool_lit ::= 'true' | 'false'Numbers are always parsed as f64 unless context requires i64.
Scientific notation is not supported in v2 — use decimal form.
Valid: 0.06309, 1000.0, 76.04, 0.0
Invalid: 6.309e-2, 1_000
+ - * / ^ %
Operator ^ is exponentiation (equivalent to pow(x, n)).
All operators are left-associative except ^ (right-associative).
> < >= <= == !=
Comparison operators are valid only inside assert expressions.
&& || !
QOMN v2 is statically typed with five value types:
| Type | Size | Range | Use |
|---|---|---|---|
f64 |
64-bit float | ±1.8×10³⁰⁸ | Default numeric type |
f32 |
32-bit float | ±3.4×10³⁸ | Reduced precision |
i64 |
64-bit integer | ±9.2×10¹⁸ | Integer counts |
bool |
1-bit | true/false | Flags only |
str |
UTF-8 string | — | Labels, messages |
Rules:
- All arithmetic is performed in
f64precision - Integer parameters are automatically promoted to
f64in expressions strvalues cannot participate in arithmeticboolvalues resolve to1.0(true) or0.0(false) in arithmetic context
QOMN performs automatic (implicit) type coercion in the following cases:
Automatic (safe) coercions:
| From | To | Context |
|---|---|---|
i64 |
f64 |
Always safe; happens automatically in arithmetic |
bool |
f64 |
true = 1.0, false = 0.0 — enables branchless arithmetic |
f32 |
f64 |
Automatic when mixing f32 and f64 in expressions |
Mixed arithmetic promotion rules:
i64 + f64 → f64
bool + f64 → f64
bool + i64 → f64 (both operands promoted)
str + any → TypeError (forbidden)
Forbidden conversions (TypeError):
strcannot participate in any arithmetic expression- There is no explicit casting syntax; use constants for unit conversions
- No implicit narrowing (f64 → i64 is never automatic)
The str type is used exclusively for metadata, labels, messages, and formula documentation strings. It is not a general-purpose string type.
Supported string operations:
- Comparison:
==and!=are supported forstrinassertexpressions - Concatenation: NOT supported — use
labelandformulastrings for composition - Arithmetic: TypeError —
strcannot participate in numeric expressions
Where str appears:
metablock field valuesformuladeclaration name and expression stringsoutputstatementlabelandunitstringsassertmessage aftermsgstr-typed input parameters (for mode/type flags passed as metadata)
Example:
plan_check_mode(mode: str = "strict") {
meta { standard: "Internal", source: "§1", domain: "util", version: "2.2", }
assert mode == "strict" || mode == "lenient" msg "mode must be strict or lenient";
output mode label "Validation mode";
}
program ::= plan_decl+
plan_decl ::= 'plan_' identifier '(' param_list? ')' '{' meta body_item* '}'
body_item ::= const_decl | let_decl | formula | assert | output
param_list ::= param (',' param)*
param ::= identifier ':' type ('=' default)?
type ::= 'f64' | 'f32' | 'i64' | 'bool' | 'str'
default ::= literal
meta ::= 'meta' '{' meta_field+ '}'
meta_field ::= identifier ':' string_lit ','
const_decl ::= 'const' identifier '=' expr ';'
let_decl ::= 'let' identifier '=' expr ';'
formula ::= 'formula' string_lit ':' string_lit ';'
assert ::= 'assert' assert_expr 'msg' string_lit ';'
assert_expr ::= or_expr
or_expr ::= and_expr ('||' and_expr)*
and_expr ::= not_expr ('&&' not_expr)*
not_expr ::= '!' comp_expr | comp_expr
comp_expr ::= arith_expr (cmp_op arith_expr)? | '(' assert_expr ')'
output ::= 'output' identifier 'label' string_lit ('unit' string_lit)? ';'
expr ::= term (arith_op term)*
term ::= literal | identifier | call | '(' expr ')'
call ::= identifier '(' expr_list? ')'
expr_list ::= expr (',' expr)*
arith_op ::= '+' | '-' | '*' | '/' | '^' | '%'
cmp_op ::= '>' | '<' | '>=' | '<=' | '==' | '!='
log_op ::= '&&' | '||'
literal ::= number | string_lit | bool_lit
number ::= [0-9]+ ('.' [0-9]+)?
string_lit ::= '"' [^"]* '"'
bool_lit ::= 'true' | 'false'
identifier ::= [a-zA-Z][a-zA-Z0-9_]*plan_<name>(<params>) {
<body>
}
A plan is the top-level compilation unit. Each plan is independently callable. Plans cannot call other plans (no plan-to-plan invocation in v2).
Naming convention: plan_{domain}_{calculation}
Examples: plan_pump_sizing, plan_hazen_williams, plan_beam_deflection
Parameters are positional. When calling a plan, parameters may be passed by position or by name (runtime-dependent).
Default values are evaluated at parse time. Defaults must be literals — expressions are not allowed as defaults in v2.
Parameter Ordering Rule: Parameters with default values must appear after all required (non-default) parameters. This is enforced at parse time.
// VALID — required parameters first, then optional with defaults
plan_example(x: f64, y: f64, z: f64 = 1.0) { ... }
// INVALID — required parameter after optional parameter (parse error)
plan_bad(x: f64 = 1.0, y: f64) { ... }
The meta block is REQUIRED and must appear as the first item in the plan
body. A plan without a meta block will not parse. Only one meta block is
allowed per plan.
meta {
standard: "Full standard name and year",
source: "Section / table reference",
domain: "domain_name",
version: "2.0",
note: "Optional note",
}
Required fields:
| Field | Required | Description |
|---|---|---|
standard |
Yes | Full name of the applicable standard |
source |
Yes | Specific section, table, or clause |
domain |
Yes | One of: hidraulica, nfpa_electrico, civil, mecanica, termica, sanitaria, electrical, financial, medical, statistics, transport |
version |
Yes | QOMN spec version string, e.g. "2.2" |
note |
No | Optional clarification |
The meta block is REQUIRED and must appear as the first item in the
plan body. A plan without a meta block is a parse error. Only one meta
block is allowed per plan. This constraint is enforced by the grammar:
plan_decl ::= 'plan_' identifier '(' param_list? ')' '{' meta body_item* '}'
body_item ::= const_decl | let_decl | formula | assert | outputThe meta keyword is separate from body_item to make its required-and-first
position explicit in the grammar.
const IDENTIFIER = expr;
Declares a compile-time constant. By convention, constant names use
UPPER_SNAKE_CASE. Constants are evaluated before any let expression
and cannot reference let variables.
const GPM_TO_LPS = 0.06309;
const PI = 3.14159265358979;
const KPA_TO_M = 0.10197;
Variable Shadowing: Names must be unique across parameters, const
declarations, and let declarations within a plan. A const or let
binding cannot shadow an input parameter or any prior declaration in the
same plan. Violation produces a parse error.
// INVALID — shadowing parameter with let
plan_bad(x: f64) {
let x = 2.0; // parse error: 'x' already defined as parameter
...
}
// INVALID — shadowing const with let
plan_bad2(y: f64) {
const A = 1.0;
let A = 2.0; // parse error: 'A' already defined as const
...
}
let identifier = expr;
Declares a computed intermediate value. let variables are evaluated in
declaration order. A let may reference previously declared let and
const values, and all input parameters.
let Q_lps = Q_gpm * GPM_TO_LPS;
let H_m = P_psi * PSI_TO_M;
let HP_req = (Q_lps * H_m) / (eff * 76.04);
Important: let declarations are not reassignable. Each name may appear
only once on the left side of a let expression.
formula "Human-readable name": "Mathematical expression as string";
Declares a named formula string for documentation and audit trail purposes. The formula string is not evaluated — it is stored as metadata and returned in the response alongside computed values.
formula "Pump power": "HP = (Q[L/s] × H[m]) / (η × 76.04)";
formula "NFPA shutoff": "HP_shutoff ≤ 1.40 × HP_rated";
formula "Voltage drop": "ΔV = 2·ρ·L·I / S";
At least one formula declaration is required per plan (stdlib requirement;
not enforced by the parser but required for PR acceptance).
assert <condition> msg "<message>";
Asserts that an input or computed value satisfies a condition. If the assertion fails at runtime, execution halts and an error is returned with the message string. No partial outputs are emitted.
assert Q_gpm > 0.0 msg "flow must be positive (GPM)";
assert eff > 0.0 msg "efficiency must be > 0";
assert eff <= 1.0 msg "efficiency must be ≤ 1.0";
assert Q_gpm <= 5000.0 msg "flow exceeds NFPA 20 Table 4.26 max";
Rules:
- At least one
assertper input parameter is required (stdlib requirement) - Assertions may reference parameters,
const, andletvalues - Assertions are evaluated after all
letvalues are computed - Multiple assertions are evaluated in declaration order; first failure stops
Compound assertions:
assert fp > 0.0 && fp <= 1.0 msg "power factor must be in (0, 1]";
Logical operator precedence in assert expressions (high to low):
| Priority | Operator | Description |
|---|---|---|
| 3 (highest) | ! |
Logical NOT (prefix unary) |
| 2 | && |
Logical AND |
| 1 (lowest) | || |
Logical OR |
Comparison operators (>, <, >=, <=, ==, !=) are always evaluated
before any logical operator. Parentheses may be used to override precedence.
// a > 0 is evaluated first, then negated, then ANDed with b < 10
assert !a > 0.0 && b < 10.0 msg "...";
// Explicit grouping for clarity:
assert !(a > 0.0) && (b < 10.0 || c == 0.0) msg "...";
output <identifier> label "<label>" unit "<unit>";
output <identifier> label "<label>";
Declares a value to be included in the plan response. The identifier must
reference a previously declared let or const value, or an input parameter.
output HP_req label "Required HP" unit "HP";
output HP_max label "Max shutoff HP (NFPA 20)" unit "HP";
output Q_lps label "Flow rate" unit "L/s";
output H_m label "Total Dynamic Head" unit "m";
output drop_pct label "Voltage drop percentage" unit "%";
Unit conventions:
| Quantity | Preferred unit |
|---|---|
| Power | HP, kW, W |
| Flow | L/s, L/min, GPM, m³/h |
| Pressure | m, PSI, kPa, bar |
| Length | m, mm |
| Area | m², mm², cm² |
| Current | A |
| Voltage | V |
| Resistance | Ω, mm² (cross section) |
| Dimensionless | % |
Standard precedence (high to low):
| Precedence | Operators | Associativity |
|---|---|---|
| 5 (highest) | Unary - |
Prefix (right) |
| 4 | ^ |
Right |
| 3 | *, /, % |
Left |
| 2 | +, - (binary) |
Left |
| 1 | Comparisons | Non-assoc |
Unary minus and exponentiation: Unary minus has higher precedence than exponentiation. This follows the mathematical convention:
-2.0 ^ 2.0 = -(2.0 ^ 2.0) = -4.0
This differs from some languages (e.g., Python, where -2**2 = -4 by same
convention). To square a negative number, use parentheses: (-2.0) ^ 2.0 = 4.0.
let x = 2.0 + 3.0 * 4.0; // 14.0
let y = pow(2.0, 3.0); // 8.0
let z = (2.0 + 3.0) * 4.0; // 20.0
Integer division is not supported. All division produces f64.
Division by zero produces a runtime error, not NaN or Inf.
let r = 10.0 % 3.0; // 1.0
| Function | Signature | Description |
|---|---|---|
sqrt(x) |
f64 → f64 | Square root. Error if x < 0 |
pow(x, n) |
f64, f64 → f64 | x raised to power n |
abs(x) |
f64 → f64 | Absolute value |
min(a, b) |
f64, f64 → f64 | Minimum of two values |
max(a, b) |
f64, f64 → f64 | Maximum of two values |
clamp(x, min_val, max_val) |
f64, f64, f64 → f64 | Clamp x to [min_val, max_val]. Equivalent to max(min_val, min(x, max_val)) |
log(x) |
f64 → f64 | Natural logarithm. Error if x ≤ 0 |
log10(x) |
f64 → f64 | Base-10 logarithm. Error if x ≤ 0 |
round(x, n) |
f64, i64 → f64 | Round x to n decimal places |
floor(x) |
f64 → f64 | Floor (round down) |
ceil(x) |
f64 → f64 | Ceiling (round up) |
sin(x) |
f64 → f64 | Sine (radians) |
cos(x) |
f64 → f64 | Cosine (radians) |
tan(x) |
f64 → f64 | Tangent (radians) |
asin(x) |
f64 → f64 | Arcsine (radians). Error if |x| > 1 |
acos(x) |
f64 → f64 | Arccosine (radians). Error if |x| > 1 |
atan(x) |
f64 → f64 | Arctangent (radians) |
atan2(y, x) |
f64, f64 → f64 | Four-quadrant arctangent of y/x (radians) |
pi() |
→ f64 | π = 3.14159265358979 |
e() |
→ f64 | e = 2.71828182845905 |
Common engineering constants used in the stdlib. Using these as named const
declarations (rather than bare literals) improves readability and traceability.
| Constant | Value | Meaning |
|---|---|---|
76.04 |
kgf·m/s | HP conversion: 1 metric HP = 76.04 kgf·m/s (fluid power) |
0.70307 |
m/PSI | PSI to meters water column: 1 psi = 0.70307 m.c.a. |
0.06309 |
L/s per GPM | GPM to L/s: 1 gpm = 0.06309 L/s |
0.0172 |
Ω·mm²/m | Copper resistivity ρ at 20°C (IEC 60228) |
Example usage:
const GPM_TO_LPS = 0.06309; // 1 gpm = 0.06309 L/s
const PSI_TO_M = 0.70307; // 1 psi = 0.70307 m.c.a.
formula "HP conversion": "76.04 kgf·m/s = 1 HP (metric)";
For each plan invocation:
1. Bind input parameters (with defaults for omitted optional params)
2. Evaluate const declarations (in order)
3. Evaluate let declarations (in order, each may reference prior lets)
4. Evaluate assert conditions (in order; halt on first failure)
5. Collect output values
6. Return response
Each plan compiles to an independent WASM function. The compiler:
- Inlines all constants at compile time
- Generates typed local variables for each
let - Emits assertion checks as conditional traps
- Serializes outputs as a structured record
Compiled WASM modules are cached after the first call. Subsequent calls with different inputs execute the cached module — no recompilation.
QOMN execution is strictly deterministic:
- Same inputs always produce identical outputs (bit-for-bit)
- No random number generation
- No I/O, network calls, or filesystem access from within a plan
- No floating-point non-determinism: IEEE 754 double, round-to-nearest
{
"error": "assertion_failed",
"plan": "plan_pump_sizing",
"message": "flow exceeds NFPA 20 Table 4.26 max",
"input": "Q_gpm",
"value": 6000.0
}{
"error": "missing_input",
"plan": "plan_pump_sizing",
"parameter": "Q_gpm"
}{
"error": "type_error",
"plan": "plan_pump_sizing",
"parameter": "eff",
"expected": "f64",
"received": "string"
}{
"error": "math_error",
"plan": "plan_hazen_williams",
"expression": "sqrt(area)",
"reason": "sqrt of negative number",
"value": -0.5
}{
"error": "plan_not_found",
"plan": "plan_nonexistent"
}A successful execution returns:
{
"plan": "plan_pump_sizing",
"status": "ok",
"outputs": {
"HP_req": {
"value": 18.04,
"label": "Required HP",
"unit": "HP"
},
"HP_max": {
"value": 21.65,
"label": "Max shutoff HP (NFPA 20)",
"unit": "HP"
}
},
"formulas": {
"Pump power": "HP = (Q[L/s] × H[m]) / (η × 76.04)",
"NFPA shutoff": "HP_shutoff ≤ 1.40 × HP_rated"
},
"meta": {
"standard": "NFPA 20:2022 — Standard for Stationary Pumps",
"source": "Section 4.26, Chapter 6, Annex A",
"domain": "nfpa_electrico",
"version": "2.0"
},
"execution_ms": 0.18,
"assertions_passed": 5
}| Version | Key changes |
|---|---|
| v1.0 | Initial: plan_, let, output |
| v2.0 | Added: meta{}, assert...msg, formula, const, default params, unit |
| v2.2 | Identifier grammar mixed-case; reserved words expanded; unary minus precedence; assert precedence formalized; clamp/atan/asin/acos added; meta required; type coercion rules; string type rules; shadowing forbidden; domain constants table; compiler architecture |
Future versions maintain backward compatibility — v1 plans are valid v2 plans.
QOMN is compiled through a multi-stage pipeline:
- Strategy: recursive descent, single-pass
- Input: UTF-8
.qomnsource text - Output: HIR (High-level Intermediate Representation) — a typed AST
- HIR → type-checked, named AST with resolved identifiers
- Bytecode → lowered, flattened instruction stream
- Cranelift JIT → native machine code via the Cranelift code generator
- Target: Cranelift native ISA
- Optimizations: AVX2/FMA3 detected at startup; auto-vectorization for batch calls
- Memory: zero-allocation hot path (all stack-allocated per plan call)
- Hash function: FNV-1a (64-bit) over all input parameter values
- Cache hit cost: 0 ns (single memory read from cache table)
- Cache miss: full JIT execution + cache store
- Cache is per-process; cleared on plan reload
Planned tooling for the QOMN ecosystem:
| Tool | Command | Status |
|---|---|---|
| Type/syntax checker | qomn check <file.qomn> |
Planned |
| Code formatter | qomn fmt <file.qomn> |
Planned |
| Interactive shell | qomn repl |
Planned |
| Plan runner | qomn run <plan> [args] |
Available (via REST API) |
| Benchmark harness | qomn bench <plan> |
Available |
The qomn check tool will report:
- Parse errors (syntax)
- Type errors (e.g.,
strin arithmetic) - Shadowing violations
- Missing
metablock - Optional: default-before-required parameter ordering
- Fixed: Identifier grammar now allows mixed case
[a-zA-Z][a-zA-Z0-9_]* - Fixed: Added
label,unit,msg,versionto reserved words list - Fixed: Defined unary minus precedence (5) above exponentiation (4)
- Fixed: Formalized assert expression precedence (
!>&&>||); added EBNF foror_expr,and_expr,not_expr,comp_expr - Added:
clamp(x, min_val, max_val)built-in function - Added:
atan2(y, x),asin(x),acos(x),atan(x)inverse trig built-ins - Added:
mecanica,termica,sanitariastandard library domains - Clarified:
metablock is required and must be first in plan body; EBNF updated - Clarified: Type coercion rules (
i64→f64,bool→f64,strforbidden in arithmetic) - Clarified: String type usage (metadata only;
==and!=comparisons supported) - Clarified: Variable shadowing is forbidden (parse error)
- Clarified: Required parameters must precede optional (defaulted) parameters
- Added: Domain constants table (76.04, 0.70307, 0.06309, 0.0172) in §7.1
- Added: Compiler architecture documentation (§12)
- Added: Development tools roadmap (§13)
- Added: Escape sequence documentation in §2.3
- Added: Negative literal clarification in §2.3
- Added:
meta{},assert...msg,formula,const, default params,unit
plan_,let,output
QOMN v2.2 Specification — Copyright (c) 2026 Percy Rojas Masgo — MIT License