Writing LMCode Programs
This tutorial walks through everything you can write inside an _lmcode_ ... end _lmcode_ block
and how outer LMScript compiles, inspects, and runs the resulting program.
1. Opening an _lmcode_ Block: cli= vs schema=
Every block binds to a single host schema using either cli="PROGRAM [SUBCOMMAND...]" or
schema="path/to/schema.json". A real run may resolve the CLI with
PROGRAM ... --lmcode-schema --json; --check and --dry-run instead use explicit
schema files or cached schemas, never discovery subprocesses or downloads. In a real run, an optional
install="github:OWNER/REPO@TAG" downloads and SHA-256 verifies a missing CLI binary into
~/.cache/lmscript/tools/.
# Option A: Bind directly to an installed (or auto-installed) host CLI
p1 = _lmcode_ cli="lmaws" install="github:lmctl-ai/lmaws@v0.1.34";
aws.demo().zones();
end _lmcode_
# Option B: Bind to a schema file on disk (great for offline compilation & tests)
p2 = _lmcode_ schema="host.schema.json";
let base = money(500);
shop.add("book", base);
end _lmcode_
2. Executing Programs: run_lmcode, lmcode_build & load_schema
# 1. Single program execution
r = run_lmcode(p1);
if (r.ok) {
println("Success result: " + json_stringify(r.result));
} else {
error(f"Failure: {r.get('message', 'host execution failed')}");
}
# 2. Multi-slot builder execution (runs multiple compiled slots in one host invocation)
b = lmcode_build("lmaws");
b.add(p1);
b.add(p1);
batch = run_lmcode(b);
# batch.result is a list of per-slot results; batch.parts == 2
# 3. Inspect host schema metadata at runtime
schema = load_schema(cli="lmaws" install="github:lmctl-ai/lmaws@v0.1.34");
println(type(schema));
3. Static Types, Wrapping int64 & Explicit Conversions
LMCode has four primitive value types (int, float, string, bool),
void for non-returning functions, standard Json, plus schema-declared object and enum types.
- No implicit primitive widening: Adding
int + floatis a compile error with the hint(no implicit conversion: widen the int with float(i)). - Explicit conversions:
float(i)widens anint;int(f)truncates afloattoward zero (saturating atint64bounds,NaN -> 0);str(x)formats anint,float,bool, orenum. - Two's-complement 64-bit integers: Runtime
intarithmetic wraps on overflow without trapping; source integer literals outsideqi(...)/jsonare checked within±2^53.
4. Quote-Lists (qw, qi, qf), Map Literals (%(...)) & JSON Heredocs
prog = _lmcode_ cli="lmaws" install="github:lmctl-ai/lmaws@v0.1.34";
# 1. Quote-word list -> StrList (0-indexed!)
let regions = qw(
us-east-1
us-west-2 # comments inside qw/qi/qf are stripped
eu-west-1
);
# 2. Quote-int list -> IntList (accepts full signed int64 range!)
let limits = qi(-9223372036854775808 0 1024 9223372036854775807);
# 3. Quote-float list -> FloatList (accepts scientific notation & integers)
let weights = qf(1 0.5 2.5e-3);
# 4. Homogeneous map literals -> StrMap, IntMap, or FloatMap
let headers = %(
cache_control => "public, max-age=300",
content_type => "text/html; charset=utf-8",
);
let ports = %(http => 80, https => 443);
# 5. Dedented raw heredoc -> string
let policy_note = <<~TXT;
Managed by lmscript.io
Do not edit manually.
TXT
# 6. Validated JSONC heredoc -> immutable Json object (checked at compile time!)
let config = json <<~JSON;
{
// JSONC comments and trailing commas are allowed here!
"domain": "lmscript.io",
"ttl": [60, 300,],
}
JSON
lmcode_return_put("first_region", regions.at(0));
lmcode_return_put("https_port", ports.get("https"));
lmcode_return_put("domain", config.get("domain").as_string());
end _lmcode_
5. ${expr} Splices from Outer LMScript
Before compiling an _lmcode_ block, LMScript expands ${expr} splices using values from the
enclosing script. Splices occupy whole expression positions; do not place them inside quoted strings.
Numbers, booleans, strings, lists, and maps are captured as typed literals. (Raw heredocs <<TAG
and <<~TAG never interpolate ${...}.)
target_domain = "lmscript.io";
max_ttl_sec = 3600;
prog = _lmcode_ cli="lmaws" install="github:lmctl-ai/lmaws@v0.1.34";
let dom = ${target_domain};
let ttl = ${max_ttl_sec};
lmcode_return_put("domain", dom);
lmcode_return_put("ttl", ttl);
end _lmcode_
6. Control Flow, switch, Loops & Compile-Time Inlined Functions
LMCode supports if / else if / else, switch (expr) { case K: { ... } default: { ... } } (braced arms, no fallthrough),
while (cond) { ... }, for (i in range(start, end)), and for (x in collection)
over IntList, FloatList, StrList, and Json arrays.
Helper functions declared with fn name(a: T) -> R { ... } are inlined at compile time
(DSLB v6 has no runtime call stack for user functions, so direct or mutual recursion is rejected at compile time).
prog = _lmcode_ cli="lmaws" install="github:lmctl-ai/lmaws@v0.1.34";
fn clamp_ttl(sec: int) -> int {
let result = sec;
if (sec < 60) {
result = 60;
} else if (sec > 86400) {
result = 86400;
}
return result;
}
let env_name = "prod";
let ttl = 0;
switch (env_name) {
case "dev": { ttl = clamp_ttl(30); }
case "staging": { ttl = clamp_ttl(30); }
case "prod": { ttl = clamp_ttl(300); }
default: { ttl = 120; }
}
lmcode_return_put("effective_ttl", ttl);
end _lmcode_
7. Host Context Objects, Operator Mapping, Conversions & Enums
A host schema can expose pre-bound context objects (such as aws in lmaws or
shop and ledger in the shop samples), custom binary/unary operators
(+, -, *, neg, r+, r-, r*),
single-step conversions (e.g., int -> Money via money(int)), and
strongly typed enums (Currency.USD).
8. Annotated SAM Rule Entries (@name fn ... implements Interface)
When a host needs to invoke callbacks inside your compiled bytecode (such as pricing rules, CAD parametric constraints,
or HTTP route handlers in the rest module), declare a top-level function with an @annotation
and an implements InterfaceName clause:
prog = _lmcode_ schema="host.schema.json";
@seasonal
fn discount_rule(m: Money, qty: int) -> Money implements Adjuster {
let out = m;
if (qty >= 10) {
out = m - 100; # int converts to Money via the schema conversion
}
return out; # return is only allowed as the final top-level statement
}
@loyalty
fn score_order(m: Money) -> int implements Score {
return m.cents() / 100;
}
end _lmcode_
9. Error Phases & Located Diagnostics
| Phase | When It Happens | Behavior |
|---|---|---|
Compile (dsl.ts) | During lmscript --check or before script execution | Exits with code 2 and exact error:FILE:LINE:COL: _lmcode_: ... diagnostic. |
CLI (phase: "cli") | Invalid host flags or manifest mismatch | Host writes failure envelope with phase="cli" (exit 2); run_lmcode returns { ok: false, phase: "cli", ... }. |
Load (phase: "load") | Schema hash mismatch or unbound import | Host verifier catches before execution and returns LOAD_SCHEMA_MISMATCH or LOAD_UNBOUND (exit 3). |
Run (phase: "run") | Division by zero, index out of bounds, or host method failure | Host maps bytecode PC through the region's line table and returns exact 1-based line and column (exit 3). |
App (phase: "app") | Post-body domain validation failure in the host | Host reports domain error with phase="app" (exit 4). |