Skip to the content.

Language JSON

A language file is one JSON object. ReScripted reads it and then knows how to parse your scripts.

Every language uses the same keys. You change the values.

"declaration_keywords": ["let"]     →  let x = 1;
"declaration_keywords": ["var"]     →  var x = 1;

Do not add new top-level keys. If you need a word the template does not have (command, quest), put it under custom (below).

Start from a complete file

Copy examples/mini/mini.json and change the words. Or copy ../agents/language_template.json and fill it.

The short JSON in Getting started is only for the hello-world program. A real language keeps the full file, including keys you are not using (leave them disabled).

If a coding agent is filling the file, give it ../agents/AGENTS.md.

Keys you will actually edit

Each block below is: the JSON, then a script it allows.

Name and file suffix

"name": "Mini",
"extensions": [".mini"]

name is what C++ passes to eval and load. extensions is how load_file("ai.mini") picks the language. Use a real name, not the template placeholder.

Comments

"comments": {
  "line": { "enabled": true, "markers": ["//", "#"] },
  "block": { "enabled": true, "pairs": [["/*", "*/"]] }
}
// a line comment
print(1);  # also a line comment

If # starts a comment, do not also use # as an import keyword or decorator prefix.

Variables

"variables": {
  "enabled": true,
  "declaration_keywords": ["let"],
  "assignment_operators": ["="]
}
let hp = 10;
hp = 9;

You can list += -= *= /= in assignment_operators (and in operators.assignment) to allow hp += 1.

Assignment to a field or a list slot is not implemented: hero.hp = 9 and items[0] = 1 will not work. Reading hero.hp and items[0] does work.

Functions

"functions": {
  "enabled": true,
  "declaration_keywords": ["fn"],
  "return_keywords": ["return"]
}
fn add(a, b) {
    return a + b;
}

After load, C++ can engine.call("add", {Value(2.0), Value(3.0)}).

Type-first headers like int add(int a, int b) work when you set functions.header_style to "c_type_first".

Blocks and semicolons

"tokens": { "statement_terminators": [";"] },
"blocks": { "delimiter": { "enabled": true, "open": "{", "close": "}" } }
if hp > 0 {
    print("alive");
}

Braces around a single statement are optional. Semicolons are optional unless you set statement_terminator_required.

Control flow

Use the keyword lists under keywords / control_flow for if, else, while, for, break, continue.

if hp <= 0 {
    return;
}
while hp > 0 {
    hp -= 1;
}
for i 5 {          // i is 0, 1, 2, 3, 4
    print(i);
}
for item [1, 2, 3] {
    print(item);
}

There is no for (i = 0; i < n; ++i).

Import

"imports": {
  "enabled": true,
  "import_keywords": ["import"],
  "module_separator": ".",
  "bind_as": "namespace"
}
import math
print(math.twice(3));

import helpers with no C++ registration loads helpers next to the script (same language, same folder). A registered library needs add_library in C++. See Libraries.

Run the top of the file

"entry_point": { "top_level_execution": true }

With this, engine.run(id) executes the lets and bare calls at the top of the script. Functions are registered on load even without it.

Extra words: command, entity, @export

A game language often wants more than fn and let. Add them under custom. ReScripted will parse them. Your C++ decides what they do.

"custom": {
  "function_kinds": [
    {
      "name": "command",
      "declaration_keywords": ["command"]
    }
  ],
  "types": [
    {
      "name": "entity",
      "declaration_keywords": ["entity"]
    }
  ],
  "decorators": [
    {
      "name": "export",
      "prefix": "@"
    }
  ],
  "host_functions": ["print", "spawn"]
}
@export
command ping() {
    return "pong";
}

entity Hero {
    hp = 10;
}
JSON In a script After you load / run
function_kinds command ping() { ... } engine.call("ping", {}) works after load (same as fn)
types entity Hero { hp = 10; } a map named Hero exists after run
decorators @export above a function your bind_decorator("export", ...) runs at load
host_functions spawn("goblin") you still must bind("spawn", ...)

List host function names in custom.host_functions and builtins.functions so the file documents the C++ contract.

Classes

When classes.enabled is true, scripts can declare class or actor (whatever keywords you set), optional : Base, fields, and methods. engine.call("OnSpawn") calls that method on the instance created for the declaration.

What the engine ignores (for now)

The JSON has fields for async, generics, macros, Python-style indentation, and more. Leave them in the file. They do not run. Do not promise them to script authors.

You also cannot assign through . or []. Closures do not capture inner lets.