LM lmscript.io Language Guide
Complete Language Reference

LMScript Language & CLI Guide

This reference covers every language construct in LMScript (.lms). For the statically typed inner bytecode language inside _lmcode_ ... end _lmcode_ blocks, see the LMCode Tutorial and LMCode Spec.

1. Values, Truthiness, Strict Equality & Block Scope

values_and_scope.lms
const REGION = "us-east-1";
name  = "lmscript";
items = ["alpha", "beta", "gamma"];
meta  = {owner: "platform", retries: 3};

println(type(null), type(true), type(1.5), type(name), type(items), type(meta));
# null boolean number string array object

2. 0-Based Indexing, Negative Indices & Strict Access

Both outer LMScript and inner LMCode use 0-based indexing. Negative indices count from the end (xs[-1] is the last element). Accessing a missing map key or out-of-bounds index is a located error (never undefined); use .get(key, default) and .has(key) for optional fields:

strict_access.lms
langs = ["lmscript", "lmcode", "rust", "cpp"];
println(langs[0]);                 # lmscript (first element)
println(langs[-1]);                # cpp      (last element)
println(langs.get(99, "none"));    # none

cfg = {server: {port: 8080}};
println(cfg.server.get("port", 80));  # 8080
println(cfg.has("client"));           # false

3. Python-Style Strings, Regex & Stateful pos()

Form Syntax Behavior
Double / Single "hello\n", 'hello\n' Standard escapes (\n, \t, \r, \", \\).
Raw String r"^\d+\.\d+$", r'C:\work\bin' Backslashes are preserved verbatim. Ideal for regular expressions.
Format String f"host={host} list={[1, 2]}" Evaluates arbitrary expressions inside {...} (lists/maps render as JSON). Use {{ and }} for literal braces.
Triple-Quoted """...""", f"""...""", r"""...""" Multi-line strings preserving embedded newlines and quotes.
Path Literals path".", path"report.txt" Normalizes to the OS path form (".", "./report.txt" or ".\report.txt") with zero filesystem lookup.

Pair raw strings (r"...") with LMScript's regex helpers: check containment with regex_test(s, pat), extract one or all matched substrings with regex_find(s, pat) and regex_matches(s, pat), extract capture groups with regex_match(s, pat) and regex_find_all(s, pat), or write a Perl-style stateful lexer using pos() + regex_find_pos(s, pat):

regex_and_stateful_pos.lms (download)
log = "2026-10-17 ERROR disk 91% | 2026-10-18 WARN cpu 72%";
println(regex_test(log, r"ERROR"));        # true (contains pattern)
println(regex_find(log, r"\d+%"));         # "91%" (first matched substring)
println(regex_matches(log, r"\d+%"));      # ["91%", "72%"] (all matched substrings)

# Stateful Perl-like pos(): loop over token rules, advance pos() on first match
const RULES = [["WS", r"\s+"], ["NUM", r"\d+"], ["ID", r"[A-Za-z_]\w*"], ["OP", r"[=+*;-]"]];
src = "x = 42 + y;";
pos(src, 0);
while (pos() < len(src)) {
  let matched = false;
  for (rule in RULES) {
    let tok = regex_find_pos(src, rule[1]);
    if (tok != null) { matched = true; break; }
  }
  assert(matched, f"unexpected input at pos {pos()}");
}

4. Control Flow, Iteration & Functions

control_flow_and_fn.lms
fn classify(score) {
  if (score >= 90) {
    return "A";
  } else if (score >= 80) {
    return "B";
  } else {
    return "C";
  }
}

# Counted for loop & for-in over list values, map keys, or string code points
for (i = 0; i < 3; i = i + 1) {
  println(classify(75 + i * 10));
}
for (v in ["a", "b"]) { println(v); }
for (k in {host: "localhost", port: 8080}) { println(k); }

# Tuple destructuring with let
let (first, second) = ["lmscript", "lmcode"];
label = first == "lmscript" ? "outer" : "inner";
println(f"{first} ({label}) + {second}");

5. Embedded Data Blocks (_json_, _csv_, _toml_, _props_, _graphql_, _cmd_, _lmcode_)

Every embedded block opens with NAME = _KIND_ [options]; and closes with end _KIND_ at column 1. JSON, CSV, TOML, list, and XML blocks produce opaque Data whose .value() returns a fresh mutable copy; props, cmd, GraphQL, and LMCode blocks have their own result kinds. For parsed data blocks, ${expr} splices are substituted per value after parsing so spliced values cannot corrupt structure.

data_blocks.lms
bonus = 5;
users = _csv_;
name,score
Ada,${90 + bonus}
end _csv_
users = users.value();
assert(users[0].score == 95);

settings = _toml_;
[server]
host = "127.0.0.1"
port = 9000
end _toml_
println(settings.value().server.port);

6. Running External Commands (cmd, _cmd_ & system)

LMScript never passes strings through a shell by default: cmd PROGRAM ARGS... and _cmd_ blocks construct an explicit argv list, and system(argv, timeoutMs_or_opts) returns {stdout, stderr, code, signal, timed_out, duration_ms, pid}.

processes.lms
argv = cmd lmscript --version;
r = system(argv, 5000);
assert(r.code == 0 && !r.timed_out);
println("Version: " + trim(r.stdout));

7. Imports, Packages, ARGV & PROPS

Include reusable files with import "lib/helpers.lms"; (files starting with package NAME; expose their symbols as NAME.member). Read CLI arguments from ARGV (alias args), optionally parse them with args_parse(ARGV, flags), and read configuration properties from PROPS / --pref FILE.

8. Forward Templates (expand) & Reverse Templates (template_extract)

templates.lms
line = expand("[DEPLOY] site=${site} env=${env}", {site: "lmscript.io", env: "prod"});
parsed = template_extract_text("[DEPLOY] site=${site} env=${env}", line);
println(json_stringify(parsed));

9. Recovery (recover / resumepoint) & Concurrency (parallel / parallel_map)

concurrency.lms
fn task_a() { return 20; }
fn task_b() { return 22; }

# Run tasks concurrently and collect results in input order
results = parallel([task_a(), task_b()]);
assert(sum(results) == 42);

# Bounded fan-out over a runtime list (default concurrency 8, max 64)
fn double(x) { return x * 2; }
doubled = parallel_map([1, 2, 3, 4], double, 4);
println(json_stringify(doubled));