MGSL – Multi Grid Scripting Language
VS Code support for MGSL, a domain-specific language for procedural
grid-based level generation. MGSL programs describe how to transform a stack
of correlated grids through pattern-rewrite rules.
Features
Real-time diagnostics
Errors appear as you type — undeclared grids, unknown tag values, shape
mismatches, duplicate declarations, and more.

Tag value coloring
Every tag value gets a stable background color. Pattern bodies light up to
show which cells carry which values — the same color you see in the tag
declaration appears wherever that value is used.

Colors automatically adapt to light and dark themes.
Hover over any tag value, rule name, or grid name to see its declaration
inline — including the full tagset, rule attributes, and pair count.
Go to definition
F12 (or Ctrl+click) on a tag value, rule name, or grid reference
jumps to where it is declared.
Completion in pattern bodies
Inside [ … ] pattern cells, Ctrl+Space (or type [ to trigger
automatically) offers all tag values declared for that grid, plus the
wildcards * and ..
Syntax highlighting
Keywords, pattern wildcards (* and .), comments, and numeric literals
are highlighted via a TextMate grammar.
Language quick reference
tag terrain { wall, floor }
layers {
level: grid of terrain
}
rule fill(symmetry=all) {
level[.]
=>
level[floor]
}
program {
resize(60, 40)
all fill
trim()
}
| Token |
Meaning in pattern |
* |
match any value (including empty) |
. |
match or write the empty cell |
| name |
a specific tag value |
| integer |
a numeric value (grid of number) |
( … ) |
a parenthesized expression (§5.8) — e.g. (n + 1), (tiles > 5), (if(difficulty > 3, wall, floor)) |
Guards and runtime inputs:
tag terrain { water, grass, rock, blocker = rock | water } // named union
params {
difficulty: number = 3 // input param with a (mandatory) default
budget = difficulty * 3 // derived param, computed once at startup
}
rule flood {
{ all
map[.]
where[ (random(1, 100) <= difficulty) ] // per-cell guard, may use random()
}
=> map[water]
}
program {
resize(40, 30)
all flood
all place_bosses when (budget > 6) // statement runs only if true
}
Getting started
- Create a file with the
.mgsl extension
- Declare tags and layers
- Write rules and a program block
- Diagnostics appear in the Problems panel as you edit
Resources
Changelog
0.6.4
- Synced to language v0.6.4 — the write side is now recursive:
{ all } / { any } blocks may nest to any depth (independent per-layer choice, correlated combos). Match side unchanged
- Recursive shape-checking through the whole write tree, and a new check for a same-grid simultaneous write inside
{ all } ({ all g[wall] g[floor] }); weights still rejected in { all } at any depth
0.6.3
- Synced to language v0.6.3 — two new program statements:
mirror(horizontal | vertical) — fold the grid across its centre axis; the axis is validated
pad(N) — add a uniform empty border on all sides; a negative margin is reported
- New keywords
mirror / pad highlighted
- (v0.6.2 needed no extension change — it only altered
symmetry=all variant expansion, a runtime/compile concern the extension doesn't model.)
0.6.1
- Synced to language v0.6.1 — the
. empty literal is now usable inside expressions: (g == .) (is-empty), (g != .) (is-non-empty), if(cond, floor, .) (conditional clear). It's polymorphic (typed from context); an untyped . (e.g. (. == .)) is reported
- The structural cells
. (empty) and * (any/preserve) are now colored in patterns — each gets its own neutral, so the most frequent pattern characters read at a glance
- Also implemented bare tag-mask literals
!tag (match-side complement, F1) and a|b (union) in pattern cells, now that examples use them
0.6.0
- Synced to language v0.6 (execution-model rework). Strategies now separate a count (
one/all/some) from an execution policy:
all(policy=stabilize) smooth, some(max=200, policy=incremental) walk, one(policy=ranked) collapse, some(percent=50) carve
- Validated: unknown policy,
percent with a non-snapshot policy, one with policy=stabilize, max/percent misuse
first body-level sub-rule combinator ({ first … }) — first matching sub-rule wins per anchor; flagged if used on a match/write side
- New keywords highlighted:
first, policy, percent, snapshot, incremental, stabilize, ranked
0.5.3
(Synced to language v0.5.3.1 — the Marketplace uses 3-part semver, so this covers v0.5.2 through v0.5.3.1.)
- Named tag unions (v0.5.2) —
blocker = wall | door inside a tag block; a union resolves as a mask anywhere a tag value can, and is validated (unknown/forward-referenced members, redeclaration, reserved/built-in collisions)
- Param defaults (v0.5.3) —
difficulty: number = 3; the default expression follows the derived-param discipline (earlier params only, no grids/positions, numeric)
- Mandatory defaults (v0.5.3.1) — a bare
name: number input is now reported as an error (every input must carry a default)
0.5.1
- Synced to language v0.5.1.
when (expr) statement guards — a boolean, params-only guard on any program statement (all place when (difficulty > 3)); flagged if it reads a grid, x/y, or width/height, or is non-boolean
- Derived params —
name = expr in the params block, computed from input params and earlier-declared derived params; forward references, grid/position reads, and non-numeric results are reported
random(lo, hi) built-in, usable anywhere expressions are (also reserved as a name)
0.4.0
- Synced to language v0.4. Built-in functions in expressions:
if(c, a, b), min, max, abs, clamp — with full type-checking (arity, argument kinds, and if's same-type branches / boolean condition)
- The names
if, min, max, abs, clamp are reported as errors when used as a tag value, grid, or param
- A built-in name not followed by
( is flagged as an unresolved identifier
- Built-in calls are highlighted as functions
0.3.0
- Synced to language v0.3.1. The extension now understands:
- Numeric expressions in pattern cells —
n[ (n + 1) ] (full §5.8 grammar: arithmetic, comparison, logical, tag-union)
- The
params { … } block for runtime numeric inputs
- The
where[ (…) ] pseudo-layer for boolean position/value guards
- Numeric and set rotation attributes —
rotation=180, rotation={90, 270}
- Spec §7.3 checks: pattern dimension-mismatch (#3), duplicate
layers/program and a missing program block (#9)
- Expression type-checking with precise diagnostics (arithmetic/relational/logical/tag-union operand rules, identifier resolution)
- Parser errors now name tokens by their literal spelling (
'}' instead of 'RBrace')
0.2.1
- Synced to language v0.2: dropped Unicode glyph aliases (names only)
- Pattern wildcards swapped —
* now matches any value, . is the empty cell (? is freed)
- New
trim() program statement (crops to the bounding box of non-empty content)
0.2.0
- Hover information for tag values, rule names, and grid references
- Go to definition (F12 / Ctrl+click)
- Completion inside pattern bodies — tag values and wildcards
- Coloring scoped to actual tag value references (no false positives on rule/layer names)
- Error squiggles now land on the correct token (rule name, tag name, etc.)
- Additional sema checks: inconsistent row widths
0.1.0
- Initial release: syntax highlighting, real-time diagnostics, tag-value coloring
| |