In strict mode (.mld), directives are bare keywords. In markdown mode (.mld.md), prefix with `/`.
```mlld
>> Strict mode
var @greeting = `Hello @name!`
show @greeting
>> What NOT to do
Hello @name! Let me show something. >> This is an error in strict mode
```
Create with `var @name = value`. Reference with `@name` in templates/commands.
```mlld
var @name = "Alice"
show `Hello @name!`
run cmd {echo "User: @name"}
run js (@name) { console.log("Hi", name) }
```
Every command needs braces. Use language specifiers: `cmd {}` for simple commands, `sh {}` for shell scripts, `js {}` for code.
```mlld
>> Language specifiers
run cmd {echo "hello"} >> simple commands (pipes only)
run sh {npm test && build} >> shell scripts (&&, ||, multi-line)
run js {console.log("hi")} >> JavaScript code
>> Executables
exe @hi() = cmd {echo hi}
exe @script() = sh { echo "multi"; ls -la }
exe @calc(x) = js { return x * 2 }
```
Only these produce output: `show`, `run`, `output`. Everything else mutates state.
```mlld
var @secret = "hidden" >> no output
show `Visible`
run cmd {echo "Also visible"}
```
Default to `::...::` or backticks for templates. Use `@var` for interpolation.
| Syntax | Interpolation | Use For |
|--------|---------------|---------|
| `` `...` `` | `@var` `` | Default inline |
| `::...::` | `@var` `` | When backticks in text |
| `"..."` | `@var` `` | Single-line only |
| `'...'` | None (literal) | Literal text |
| `{...}` | `@var` `` | Commands/code |
```mlld
>> Backticks (default)
var @msg = `Hello @name!`
>> Double-colon (when text has backticks)
var @doc = ::Use `npm test` before @env::
>> Double quotes (single-line)
var @path = "@base/files/@filename"
>> Single quotes (literal - no interpolation)
var @literal = '@name stays literal'
```
Loops work inside templates:
```mlld
var @list = `
for @item in @items
- @item.name: @item.value
end
`
```
Objects/arrays use dot notation. Arrays support slicing. Methods available on values.
```mlld
var @user = {"name":"Alice","scores":[10,20,30]}
show @user.name >> Alice
show @user.scores.1 >> 20
>> Array slicing
var @arr = [1,2,3,4,5]
show @arr[0:3] >> [1,2,3]
show @arr[-2:] >> [4,5] - last 2
>> Builtin methods
var @list = ["apple", "banana"]
show @list.includes("banana") >> true
show @list.join(", ") >> "apple, banana"
var @text = "Hello World"
show @text.toLowerCase() >> "hello world"
show @text.split(" ") >> ["Hello", "World"]
show @text.trim().startsWith("H") >> true (chained)
```
Use `exe` for reusable templates, commands, or complex logic with blocks.
```mlld
>> Simple template
exe @greet(name) = `Hello @name!`
show @greet("Bob")
>> Command
exe @list(dir) = cmd {ls -la @dir | head -5}
>> Block syntax for multi-statement bodies
exe @process(data) = [
let @validated = @validate(@data)
let @transformed = @transform(@validated)
=> @transformed
]
```
Angle brackets load file contents. Works with globs and AST selectors.
```mlld
var @content = >> file contents
var @config = >> parsed as object
var @author = .author >> field access
>> Globs
var @docs =
show @docs.length >> number of files
for @doc in @docs => show @doc.mx.filename
>> AST selectors (code extraction)
var @funcs = >> all functions
var @handler = >> specific function
```
Detection rule: only `<…>` with `.`, `/`, `*`, or `@` are file refs. XML-like `` is plain text.
Module imports for code reuse. Namespace imports avoid collisions.
```mlld
>> Registry modules
import { @parallel, @retry } from @mlld/core
import @corp/utils as @corp
>> Local files
import { @helper } from "./utils.mld"
import { @config } from <@base/config.mld>
>> Import types
import module { @api } from @corp/tools >> cached
import static { @prompt } from "./prompt.md" >> embedded at parse
import live as @status >> always fresh
>> Directory imports
import "@agents" as @agentRegistry
show @agentRegistry.alice.tldr
```
`when` for conditionals. Use `first` for switch-style (stops at first match).
```mlld
>> Simple condition
when @isProd => show "Production mode"
>> Switch-style (first match wins)
when first [
@role == "admin" => show "Admin"
@role == "user" => show "User"
* => show "Guest"
]
>> Bare when (evaluates all matches)
when [
@score > 90 => show "Excellent!"
@hasBonus => show "Bonus earned!"
none => show "No matches"
]
```
`foreach` transforms collections. `for` executes or collects.
```mlld
>> foreach (transform array)
var @names = ["alice", "bob"]
exe @greet(name) = `Hi @name!`
var @greetings = foreach @greet(@names)
>> for (execute per item)
for @n in @names => show `Name: @n`
>> for (collect results)
var @doubled = for @x in [1,2,3] => @x * 2
>> for with block syntax
for @item in @items [
let @processed = @transform(@item)
show `Done: @processed`
]
>> Parallel execution
for parallel(3) @task in @tasks => @runTask(@task)
>> For with inline filter
var @valid = for @x in @items when @x != null => @x
```
Logical, comparison, and ternary operators.
```mlld
>> Comparison: <, >, <=, >=, ==, !=
>> Logical: &&, ||, !
>> Ternary: condition ? trueVal : falseVal
var @isValid = @score > 80 && @submitted
var @status = @isPro ? "premium" : "basic"
var @canEdit = @isOwner || (@role == "editor" && !@isLocked)
>> In conditions
when @tokens > 1000 && @mode == "production" => show "High usage"
```
State updates via `state://` protocol. SDK captures writes without filesystem I/O.
```mlld
var @count = @state.count + 1
output @count to "state://count"
```
```typescript
// SDK provides state, captures writes
execute(file, payload, { state: { count: 0 } });
// Returns: { stateWrites: [{ path: 'count', value: 1 }] }
```