Docs Playground Changelog GitHub ↗

ClearScript Documentation

A type-safe scripting language with a Rust runtime, embeddable in Node.js and the browser. This page covers installation, the language tour, the standard library, and every embedding API.

tip Want to try it without installing anything? Open the browser playground — it runs the real Rust interpreter compiled to WASM.

Installation

Three ways to run ClearScript depending on what you're building:

1. The CLI (native Rust binary)

git clone https://github.com/clearscript/clearscript
cd clearscript
cargo build --release
./target/release/clear-cli run examples/hello.clear

2. Node.js embedding (npm)

npm install clearscript

Single platform-independent WASM bundle — no native compilation, no node-gyp. Works on macOS, Linux, Windows.

3. Browser (WASM)

import init, { run_clearscript } from './wasm/clear_wasm.js';
await init();
const result = JSON.parse(run_clearscript('fn main() { print("hi") }'));

Hello, World

Save the following as hello.clear:

fn main() {
  print("Hello, ClearScript!")
}

Run it:

clear-cli run hello.clear
# or via Node:
npx clear-node hello.clear

The main() function is invoked automatically if defined.

The CLI

CommandDescription
clear-cli run <file>Parse and execute a .clear file
clear-cli check <file>Validate syntax, print diagnostics, no execution
clear-cli replStart the interactive REPL
clear-cli fmt <file>Format source (placeholder — coming soon)
clear-cli build <file>Build to WASM or native (placeholder)
clear-cli versionPrint version

Syntax overview

ClearScript is expression-oriented. Almost everything returns a value. Statements end at newlines (no semicolons required).

// Single-line comment
/* Block
   comment */

let name = "world"          // immutable
let mut count = 0          // mutable

fn greet(n: String) {
  print("Hello, ", n)
}

fn main() {
  greet(name)
}

Types & values

TypeLiteral exampleNotes
Int42, 0xFF, 0b1010, 1_000_00064-bit signed
Float3.14, 1.5e-3IEEE 754 64-bit
String"hi", "Hello, ${name}!"UTF-8, supports interpolation
Booltrue, falseNo coercion to/from int
NilnilSingle null value (no undefined)
List[1, 2, 3]Heterogeneous allowed
Map{x: 1, y: 2}String keys
Functionfn(x) { x * 2 }First-class, captures lexically

Variables

let x = 5              // immutable, type inferred
let mut y = 10         // mutable
let z: Int = 15        // explicit type annotation
y = y + 1             // ok, mut
// x = x + 1            // compile error: x is immutable

Functions & lambdas

fn add(a: Int, b: Int) -> Int {
  a + b                    // last expression is the return value
}

// Lambdas — first-class functions
let double = |x| x * 2
let result = map([1, 2, 3], double)   // [2, 4, 6]

// Inline:
filter(nums, |n| n > 10)

Control flow

// if/else (expressions, return values)
let tier = if score >= 90 { "A" } else if score >= 75 { "B" } else { "C" }

// while
while count < 10 { count = count + 1 }

// for-in (works on lists, ranges, strings, maps)
for i in 0..10 { print(i) }
for ch in "hello" { print(ch) }

// loop / break / continue
loop {
  if done { break }
}

Pattern matching

match value {
  0           => "zero",
  1           => "one",
  n if n > 0  => "positive",
  _           => "other",
}

// Bind values:
match result {
  0 => print("empty"),
  n => print("got", n),    // `n` binds to value
}

Collections

let nums = [1, 2, 3]
nums[0]                  // 1 — index access
len(nums)                // 3

let user = {name: "Ada", age: 36}
user["name"]             // "Ada"

String interpolation

Any expression can be spliced into a string with ${...}. The hole is parsed and evaluated at runtime; the result is converted to a string and concatenated.

let name = "Ada"
let age = 36
print("Hello, ${name}! You are ${age} years old.")
// Hello, Ada! You are 36 years old.

// Arbitrary expressions — not just identifiers:
let nums = [1, 2, 3]
print("Sum = ${sum(nums)}, doubled = ${sum(nums) * 2}")
// Sum = 6, doubled = 12
Full expression support. Everything that works as a standalone expression works inside ${...}: arithmetic, function calls, method chains, conditionals, etc. Balanced braces inside the hole are tracked correctly.

Operators

CategoryOperators
Arithmetic+ - * / % **
Comparison== != < > <= >=
Logical&& || ! (short-circuit)
Bitwise& | ^ ~ << >>
Pipe|>data |> transform |> print
Null coalesce??x ?? default
Range0..10 (exclusive), 0..=10 (inclusive)
Assignment= += -= *= /= %= **=
note == is strict — there is no loose-equality == like JavaScript. 0 == false is a type error.

Standard Library — I/O

FunctionDescription
print(...args)Space-joins args, emits one line.
println(...args)Alias for print.

Standard Library — String

FunctionExample
len(s)len("hi") → 2
upper(s)upper("rust") → "RUST"
lower(s)lower("RUST") → "rust"
trim(s)trim(" x ") → "x"
split(s, sep)split("a,b,c", ",") → ["a","b","c"]
join(arr, sep)join(["a","b"], "-") → "a-b"
replace(s, from, to)String find/replace
contains(s, sub)Substring check
starts_with(s, p)Prefix check
ends_with(s, p)Suffix check
repeat(s, n)repeat("ab", 3) → "ababab"
chars(s)String → array of single-char strings
pad_left / pad_rightPad to length with char

Standard Library — Array

FunctionExample
len(arr)Number of items
push(arr, v)Append
pop(arr)Remove + return last
first(arr) / last(arr)Element access
reverse(arr)Reverse
sort(arr)Ascending sort
unique(arr)Deduplicate, preserving order
flatten(arr)One level of nested → flat
sum(arr) / product(arr)Numeric reduction
range(start, end)Generate [start..end)
enumerate(arr)Pairs of [index, value]
zip(a, b)Pair elements

Standard Library — Math

FunctionExample
abs(n)Absolute value
sqrt(n)Square root
pow(base, exp)Exponentiation
floor(n) / ceil(n) / round(n)Rounding
min(a, b) / max(a, b)Two-arg min/max
clamp(n, lo, hi)Restrict to range
random()Float in [0.0, 1.0)
sin(n) / cos(n) / tan(n)Trigonometry (radians)
log(n) / log2(n) / log10(n)Natural / base-2 / base-10 logarithm
exp(n)e^n

Standard Library — Functional

map([1,2,3], |x| x * 2)         // [2, 4, 6]
filter([1,2,3,4], |x| x % 2 == 0) // [2, 4]
reduce([1,2,3], |acc, x| acc + x, 0) // 6  — args: (list, fn, init)

Standard Library — Map

FunctionExample
keys(m)Sorted array of keys (deterministic)
values(m)Values in key-sorted order
has_key(m, k)Bool — key membership

Standard Library — JSON

FunctionExample
json_parse(s)json_parse("[1,2,3]") → [1, 2, 3]
json_stringify(v)json_stringify({"a": 1}) → "{\"a\":1}"

Round-trips through serde_json. Maps, arrays, strings, numbers, bools, and null are all supported.

Standard Library — Filesystem

FunctionDescription
fs_read(path)Read file → string
fs_write(path, contents)Write string → file
fs_exists(path)Bool — file exists

Native only. In WASM (browser playground) these throw a clear "filesystem not available" error.

Standard Library — Env & Time

FunctionDescription
env_get(name)Read environment variable, or empty string if unset
env_args()Array of CLI args passed after the script
time_now()Unix epoch seconds (float)

Standard Library — Type & assertion

FunctionDescription
type_of(v)Returns type name string
to_string(v) / to_int(v) / to_float(v) / to_bool(v)Explicit conversions
assert(cond)Throws on false
assert_eq(a, b)Throws if a != b

Embedding in Node.js

Install:

npm install clearscript

Run source

const cs = require('clearscript');

// Returns { ok, output, error } — never throws on script errors
const result = cs.run(`
  fn main() {
    let nums = [1, 2, 3, 4, 5]
    print("sum:", sum(nums))
  }
`);
console.log(result.output);  // ['sum: 15']

// execute() throws on failure
try {
  const out = cs.execute('fn main() { print("hi") }');
} catch (e) {
  if (e instanceof cs.ClearScriptError) {
    console.error(e.message, e.output);
  }
}

require() .clear files

require('clearscript/register');    // install loader hook
require('./hello.clear');            // runs the file, output → stdout

Reusable context (REPL / notebook)

const ctx = cs.createContext();
ctx.eval('fn greet(name: String) { print("hi", name) }');
ctx.eval('fn main() { greet("world") }');  // → ['hi world']
ctx.reset();

ESM

import { run, execute, createContext } from 'clearscript';

CLI

npx clear-node hello.clear
npx clear-node --eval 'fn main() { print("hi") }'
npx clear-node --repl
npx clear-node --version

Node API reference

APIReturnsThrows
run(source){ ok, output, error }Only on TypeError
execute(source)string[] (output lines)ClearScriptError
runFile(path){ ok, output, error, file }fs errors
executeFile(path)string[]ClearScriptError
createContext(){ eval, reset, source }
version()string
ClearScriptErrorclass extending Error with .output

Embedding in the browser (WASM)

<script type="module">
  import init, { run_clearscript } from './wasm/clear_wasm.js';
  await init();
  const { ok, output, error } = JSON.parse(
    run_clearscript('fn main() { print("hi from wasm") }')
  );
  document.body.textContent = output.join('\n');
</script>

Diagnostic codes

CodePhaseMeaning
E0001LexerUnexpected character
E0002LexerUnterminated string
E0003LexerUnterminated comment
E0004LexerInvalid number literal
E0010ParserUnexpected token
E0011ParserUnexpected end of input
E0012ParserMissing token
E0013ParserInvalid expression
E0020SemanticUndefined variable (with "did you mean?" suggestion)
E0021SemanticUndefined function
E0022SemanticUndefined type
E0023SemanticDuplicate definition
E0024Type checkerType mismatch (e.g. let x: Int = "s")
E0025Type checkerInvalid operation for types
E0026Type checkerArgument count / type mismatch in call

Current limitations

ClearScript is pre-1.0. The compiler frontend, tree-walking interpreter, type checker, and module resolver are complete. Still in progress:

✅ Shipped in v0.1.3: bidirectional type checker, module resolution with cycle detection, JSON / fs / env / time stdlib, "did you mean?" suggestions.
✅ Shipped in v0.1.5: full ${expr} string interpolation, O(n) unique(), zero clippy warnings.

FAQ

Why a new language?

JavaScript's footguns (coercion, hoisting, this, ==) are all fixable, but only if the compiler refuses the bad code. ClearScript bakes those refusals into the language as the 10 Laws.

How fast is the WASM interpreter?

The current tree-walking interpreter is suitable for scripts and config DSLs — roughly 5-20× slower than V8 for compute, comparable for I/O-bound work. A bytecode VM is on the roadmap.

Can I use it in production?

Not yet. v0.1 is for early experimentation. Open source launch on June 25, 2026.

Is there an N-API native addon?

Not yet — the npm package uses WASM for portability. N-API is on the roadmap for hot-path embedding scenarios.