Creature is a small, statically-checked-where-possible, dynamically-typed programming language with a hand-written lexer, parser, semantic analyzer, and tree-walking interpreter — implemented from scratch in TypeScript, with no parser generator or runtime framework.
Its object model is built around organisms (isolated, actor-like instances with private state) and signals (typed, asynchronously delivered messages), coordinated by a deterministic cooperative scheduler. Every run of a Creature program produces the same output, the same signal ordering, and the same errors, regardless of host machine or timing.
The language's vocabulary is drawn from real animal behavior — territories, alarm calls, migration — sourced and documented in docs/ECOLOGICAL_BASIS.md. It is a genuine design constraint, not just naming: functions are behaviors, objects are organisms, modules are habitats, messages are signals.
This is a compiler/interpreter project, not a wrapper around an existing toolchain:
- Full pipeline, hand-written: lexer → recursive-descent parser → semantic analyzer → tree-walking interpreter, each a separate, independently testable stage with source-span-aware diagnostics.
- Actor-style concurrency without nondeterminism: organisms have private memory and communicate only through signals; a FIFO scheduler with snapshot-at-emission semantics keeps every run reproducible.
- A real type story for a dynamic language: optional annotations get best-effort static checking during semantic analysis, and full validation at runtime — no silent coercion.
- Structured, actionable diagnostics: every error has a stable code (
CR1xxx–CR5xxx), a source location with a caret, and recovery guidance (seedocs/DIAGNOSTICS.md). - Tested like production infrastructure: 225 tests across 32 files, 93%+ statement coverage, plus deterministic fuzz tests on the lexer and parser that assert "never throws, never produces an invalid span" over arbitrary UTF-8 input.
import { print } from "habitat.io"
signal Alarm {
location: Text
}
organism Sentinel {
behavior watch(place: Text) {
emit Alarm(location: place)
}
}
organism Forager {
listen Alarm as warning {
print("Retreat from " + warning.location)
}
}
begin {
mark sentinel = Sentinel()
mark forager = Forager()
awaken sentinel
awaken forager
sentinel.watch("north ridge")
}
Running it prints Retreat from north ridge — the Forager's memory is completely private to it, and the alarm is delivered deterministically because Forager was awakened before the signal was emitted. See examples/ for eleven more, covering conditionals, collections, error handling, modules, and stateful organism lifecycles.
| Creature | Conventional equivalent |
|---|---|
mark / memory |
immutable / mutable binding |
behavior |
function |
organism |
class instance with private state (actor) |
environment |
shared singleton state |
signal / emit / listen |
typed message / send / subscribe |
habitat.* |
standard library module |
stage / transform |
state-machine state / transition |
observe |
pattern matching (switch) |
Full semantics are normative in docs/LANGUAGE_SPEC.md; syntax is normative in docs/GRAMMAR.md.
Requires Node.js 22 (LTS) and npm.
git clone https://github.com/GuerraXe/creature-lang.git
cd creature-lang
npm ci
npm run build
node dist/cli/main.js run examples/01_hello.grrRun every command from inside the creature-lang directory — npm ci needs its package.json, and node dist/cli/main.js is a relative path resolved from wherever your shell currently is (Cannot find module '...dist/cli/main.js' almost always means you're one cd away from the project root).
Installed as a package, the CLI is available as creature instead of node dist/cli/main.js:
creature run den.grr # execute a program
creature check den.grr # validate without running
creature format-check den.grr # check source formatting
creature versionrun accepts --debug (show host stack traces) and --max-steps N (override the evaluator's step budget, default 1,000,000). Exit codes: 0 success, 1 source/runtime error, 2 CLI usage error.
Creature source files use the .grr extension. Windows users who want Explorer to show the project's claw icon for .grr files can run the optional, reversible tools/register-windows-file-association.ps1 script (see its -Remove flag to undo).
Create any file ending in .grr — there's no scaffolding command, it's a plain text file:
// hello.grr
import { print } from "habitat.io"
begin {
mark greeting = "hello, creature"
print(greeting)
}
Then run it the same way as any example, using the path to your file:
node dist/cli/main.js run hello.grrEvery program needs exactly one begin { ... } block — that's the entry point. See examples/ for more complete programs (organisms, signals, stages, error handling, files), and docs/LANGUAGE_SPEC.md / docs/GRAMMAR.md for the full syntax.
npm run format:check # prettier
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm test # vitest (225 tests)
npm run test:coverage # vitest with coverage (93%+ statements)
npm run examples # builds the CLI and runs all 12 examples end to endAll of the above run in CI on every push and pull request (see .github/workflows/ci.yml).
docs/LANGUAGE_SPEC.md— normative language semanticsdocs/GRAMMAR.md— EBNF grammardocs/RUNTIME_MODEL.md— evaluation, scheduling, and resource limitsdocs/ARCHITECTURE.md— compiler/interpreter pipelinedocs/STANDARD_LIBRARY.md—habitat.io,habitat.files,habitat.mathdocs/DIAGNOSTICS.md— full error code referencedocs/ECOLOGICAL_BASIS.md— sourced basis for the language's namingexamples/— twelve annotated, CI-verified programsdecisions/— architecture decision log (EDL-001 through EDL-014)CHANGELOG.md
Native group/colony/swarm syntax and a rewrite-mode formatter are deferred; format-check validates LF endings, space indentation, no trailing whitespace, and a final newline, but does not rewrite files. Filesystem access is not sandboxed by default — an embedding must supply a sandbox root for untrusted input (see docs/STANDARD_LIBRARY.md).
MIT — see LICENSE.