LM lmscript.io LMCode Spec
Normative Specification · DSL_VERSION = 6

LMCode Grammar, dsl.schema: 2 & DSLB v6 Bytecode

Synthesized from lmcode/spec/language.md, grammar.ebnf, schema.md, and bytecode.md. Where prose and dsl.ts / dslvm.hpp ever differ, the compiler and C++ reference verifier are authoritative.

1. EBNF Grammar Summary

grammar.ebnf
chunk          = { fn_decl | rule_decl | stmt } ;
fn_decl        = "fn" IDENT "(" [ param_list ] ")" "->" TYPE block ;
rule_decl      = "@" IDENT "fn" IDENT "(" [ param_list ] ")" "->" TYPE "implements" IDENT block ;
param_list     = param { "," param } ;
param          = IDENT ":" TYPE ;

block          = "{" { stmt } "}" ;
stmt           = let_stmt | assign_stmt | return_write | if_stmt | switch_stmt
               | for_range_stmt | for_each_stmt | while_stmt
               | "break" ";" | "continue" ";" | "return" [ expr ] ";" | expr ";" ;

let_stmt       = "let" IDENT "=" expr ";" ;
assign_stmt    = IDENT "=" expr ";" ;
return_write   = "$return" [ "." IDENT | "[" expr "]" ] "=" ( expr | "null" ) ";" ;
if_stmt        = "if" "(" expr ")" block [ "else" ( if_stmt | block ) ] ;
switch_stmt    = "switch" "(" expr ")" "{" { switch_case } [ "default" ":" block ] "}" ;
switch_case    = "case" case_const ":" block ;
for_range_stmt = "for" "(" IDENT "in" "range" "(" expr "," expr ")" ")" block ;
for_each_stmt  = "for" "(" IDENT "in" expr ")" block ;
while_stmt     = "while" "(" expr ")" block ;

2. Operator Precedence & Type Rules

LevelOperatorsStatic Type Rule & Semantics
1 (loosest)a || bBoth bool; short-circuit evaluation.
2a && bBoth bool; short-circuit evaluation.
3==, !=Same primitive type or same enum type; host objects cannot be compared.
4<, <=, >, >=Both int or both float; returns bool.
5+, -Both int, both float, two strings (+ concatenates), or schema-mapped object operator (+, -, r+, r-).
6*, /, %Both int or both float (% is two ints only), or schema-mapped * / r*.
7!a, -a! requires bool; unary - requires int, float, or schema-mapped neg.
8obj.method(args)Postfix method call chain; resolved against effective schema.
9 (tightest)PrimaryLiterals, locals, context objects, fn(args), built-ins (float, int, str, math), qw/qi/qf, %(...), json <<~TAG, (expr).

3. Host Schema Format (dsl.schema: 2)

Every host supplies a JSON document with "dsl.schema": 2 and a valid identifier "name". Before typechecking, parseDslSchema retains the exact raw bytes as schema.bytes (for the SHA-256 header hash) and merges the bundled lmcode_std_v3 catalog into the effective schema.

sample_shop_v1 (host.schema.json)
{
  "dsl.schema": 2,
  "name": "sample_shop_v1",
  "types": {
    "Money": {
      "operators": { "+": "plus", "-": "minus", "*": "times", "r*": "times", "neg": "neg" },
      "methods": {
        "cents": [{ "params": [], "returns": "int" }],
        "plus":  [{ "params": ["Money"], "returns": "Money" }],
        "minus": [{ "params": ["Money"], "returns": "Money" }],
        "times": [{ "params": ["int"], "returns": "Money" }],
        "neg":   [{ "params": [], "returns": "Money" }]
      }
    },
    "Shop": {
      "methods": {
        "add":   [{ "params": ["string", "Money"], "returns": "self" }],
        "total": [{ "params": [], "returns": "Money" }]
      }
    },
    "Ledger": {
      "methods": {
        "record": [{ "params": ["Money"], "returns": "void" }]
      }
    }
  },
  "functions": {
    "money": [{ "params": ["int"], "returns": "Money" }, { "params": ["float"], "returns": "Money" }]
  },
  "context": { "shop": "Shop", "ledger": "Ledger" },
  "enums": { "Currency": ["USD", "EUR", "JPY"] },
  "conversions": [{ "from": "int", "to": "Money", "via": "money" }],
  "interfaces": {
    "Adjuster": { "params": [{ "name": "m", "type": "Money" }, { "name": "qty", "type": "int" }], "returns": "Money" },
    "Score":    { "params": [{ "name": "m", "type": "Money" }], "returns": "int" }
  },
  "result": { "type": "Json", "nullable": true }
}

4. DSLB v6 Binary File Layout

All multi-byte integers are big-endian with zero padding. The file must end immediately after the last entry (trailing bytes or truncated reads are rejected at load time).

SectionBinary LayoutVerifier Invariant
1. Header (38 B)u8[4] "DSLB", u16 version (6), u8[32] sha256(schema.bytes)Magic must be DSLB, version must be 6, SHA-256 must match host's raw schema bytes.
2. Constantsu32 count, then per entry: u8 tag (1=string), u32 byte_len, u8[byte_len]Shared across body and all rule entries; only tag 1 is valid.
3. Importsu16 count, then u32 const_idx per importEach constant is a canonical signature (fn NAME(...) -> R or method Type.name(...) -> R) bound at load time.
4. Contextu16 count, then (u32 name_const, u32 type_const)Every context variable and object type must match the host registry.
5. Body Regionlocals + u16 max_stack + u32 code_len + code + line_tableCode must end with HALT (0x11) at loop depth 0; line table sorted by pc.
6. Rule Entriesu16 entry_count, then per entry: u32 name, u32 annotation, u32 interface, u16 nparams, regionUnique entry names; parameter locals 0..nparams-1 match interface types; HALT leaves 0 (void) or 1 value.

5. Complete DSLB v6 Instruction Set (0x01..0x36)

HexMnemonicOperandsStack DeltaSemantics
0x01PUSH_INTi64 (8B)+1Push 64-bit two's-complement integer constant.
0x02PUSH_STRu32 const (4B)+1Push string constant from pool.
0x03PUSH_BOOLu8 0|1 (1B)+1Push boolean constant.
0x04LOAD_LOCALu16 slot (2B)+1Push local slot value (error if read before store).
0x05STORE_LOCALu16 slot (2B)-1Pop top of stack into local slot (forbidden on hidden loop slots).
0x06LOAD_CTXu16 ctx (2B)+1Push host-bound context object handle.
0x07CALL_FNu16 imp, u8 argc (3B)-argc + RInvoke resolved host function import.
0x08CALL_METHODu16 imp, u8 argc (3B)-(argc+1) + RInvoke resolved method import on receiver below args.
0x09 / 0x0AEQ / NE0B-1Same-type primitive/enum equality or inequality.
0x0BNOT0B0Boolean negation.
0x0CJUMPi32 rel (4B)0Unconditional forward jump.
0x0D / 0x0EJUMP_IF_FALSE / TRUEi32 rel (4B)-1Pop bool and jump forward if false / true.
0x0F / 0x10DUP / POP0B+1 / -1Duplicate or discard top stack value.
0x11HALT0B0Terminate execution of body or entry region.
0x12PUSH_FLOATf64 (8B)+1Push IEEE-754 binary64 constant.
0x13..0x16ADD_I / SUB_I / MUL_I / DIV_I0B-1Wrapping int64 arithmetic; DIV_I truncates toward zero (0 divisor errors).
0x17..0x1AADD_F / SUB_F / MUL_F / DIV_F0B-1IEEE-754 binary64 arithmetic.
0x1B / 0x1CNEG_I / NEG_F0B0Negate integer or float.
0x1D..0x20LT_I / LE_I / GT_I / GE_I0B-1Signed int64 ordering comparisons.
0x21..0x24LT_F / LE_F / GT_F / GE_F0B-1IEEE binary64 ordering comparisons.
0x25I2F0B0Widen int to float (float(i)).
0x26CONCAT0B-1Concatenate two byte strings.
0x27..0x29I2S / F2S / B2S0B0Convert int, float (%.15g), or bool to string.
0x2FFOR_RANGEu16 cnt, u16 end, i32 exit (8B)-2Pop end & start into hidden int slots; jump forward to exit if start >= end.
0x31FOR_NEXTu16 end, i32 back (6B)0Increment paired counter and branch back while counter < end.
0x32MATHu8 fn_id (1B)1 - argcBuilt-in math operation (IDs 0..17 below).
0x33MOD_I0B-1Signed int64 remainder (dividend's sign).
0x34WHILE_BEGIN0B0Marks structured while-loop head before condition evaluation.
0x35WHILE_TESTi32 exit (4B)-1Pop bool and jump forward past paired LOOP_BACK when false.
0x36LOOP_BACKi32 back (4B)0The only general backward branch; targets paired WHILE_BEGIN.

6. Built-In MATH Opcode IDs (0x32)

0: sin(f), 1: cos(f), 2: sqrt(f), 3: abs(f), 4: floor(f), 5: pow(f,f), 6: min(f,f), 7: max(f,f), 8: clamp(f,f,f), 9: lerp(f,f,f), 10: ease(f), 11: ease_in(f), 12: ease_out(f), 13: int(f)->i, 14: abs(i), 15: min(i,i), 16: max(i,i), 17: clamp(i,i,i).

7. Loader Error Codes

Error CodeCondition
LOAD_BAD_BYTECODEMalformed header/section, unknown opcode, backward jump, unpaired loop, hidden-slot write, stack depth mismatch, or another verifier failure.
LOAD_SCHEMA_MISMATCHBytecode header SHA-256 does not match the SHA-256 of the host's raw schema bytes.
LOAD_UNBOUNDAn imported function/method signature or context variable is not registered by the host.