Vex is a small, dynamically-typed scripting language implemented in C, with
its own lexer, recursive-descent parser, and tree-walking interpreter — plus
a CLI to run source files, and a Windows installer (Inno Setup) that puts
vex on your PATH and associates its file types with the Vex CLI.
vex script.vex # run a file
vex --version # print version
vex --help # usage
| Extension | Name | Purpose |
|---|---|---|
.vex |
Vex Script | A runnable program: vex myprogram.vex |
.vh |
Vex Header File | Shared structs/functions, pulled into a script with import "file.vh"; |
There's no special syntax inside a .vh file — it's ordinary Vex source.
.vex vs .vh is purely a naming convention for "this file is meant to be
run" vs. "this file is meant to be imported," the same way .c vs .h works
in C.
- Dynamic typing:
nil,bool,number(double),string,array,struct,function - Variables:
let x = 10; - Control flow:
if/else,while,for x in <array>,break,continue - Functions with closures and recursion:
fn add(a, b) { return a + b; } - Anonymous functions:
let f = fn(x) { return x * 2; }; - Structs:
struct Point { x, y }and literalsPoint { x: 1, y: 2 }, field access/assignmentp.x,p.x = 5; - Arrays:
[1, 2, 3], indexinga[0], assignmenta[0] = 9;,push/pop - Operators:
+ - * / %, comparisons,== !=,&& ||,!, unary- - Strings: concatenation with
+, indexing,upper/lower/split/join - Imports:
import "mathutils.vh";pulls a header's top-levelstruct/fndeclarations directly into the importing scope. Paths are resolved relative to the importing file's own directory; a file is only ever imported once even ifimported from multiple places. - Standard library:
print,println,len,type_of,str,num,input, math (sqrt,floor,ceil,abs,pow,min,max,random), arrays (push,pop,range), and file I/O (read_file,write_file,append_file,file_exists)
See examples/demo.vex for a tour of the core language, examples/hello.vex
for a minimal starting point, and examples/mathutils.vh +
examples/uses_header.vex for the header/import feature.
src/ interpreter source (C11, no dependencies besides libc/libm)
vex.h shared types (tokens, AST, values, env)
lexer.c source text -> tokens
parser.c tokens -> AST (recursive descent)
value.c runtime value helpers
env.c variable scopes / closures
interpreter.c tree-walking evaluator + standard library
main.c CLI entry point
Makefile builds ./vex (or vex.exe on Windows/MinGW)
examples/ sample .vex scripts and .vh headers
installer/ Inno Setup script + prebuilt vex.exe for packaging
Linux / macOS:
make
./vex examples/demo.vex
Windows, cross-compiled from Linux/macOS with MinGW-w64:
x86_64-w64-mingw32-gcc -O2 -std=c11 -o vex.exe src/*.c -lm
(vex.exe in installer/ was built exactly this way.)
Windows, natively (MSVC or MinGW installed on Windows):
gcc -O2 -std=c11 -o vex.exe src\lexer.c src\parser.c src\value.c src\env.c src\interpreter.c src\main.c -lm
or open a Developer Command Prompt and use cl if you prefer MSVC — the code
is portable C11 with no POSIX-only calls besides strdup (already guarded).
Two installer implementations, functionally equivalent - pick whichever toolchain you'd rather use:
installer/vex.iss(Inno Setup) - the original. Windows-only to build.installer-nsis/vex.nsi(NSIS) - seeinstaller-nsis/README.md. NSIS has an official Linux build, so this one can (and does, in CI) get built without a Windows runner.
The installer lives in installer/vex.iss and already expects vex.exe to
sit right next to it (it's included, prebuilt via MinGW).
- Install Inno Setup (free) on Windows.
- If you rebuilt
vex.exe, drop the new one intoinstaller/, replacing the old one. - Open
installer/vex.issin the Inno Setup Compiler (or runISCC vex.issfrom a command prompt) and build. - The installer is written to
installer/output/VexSetup-1.0.0.exe.
The installer:
- Installs to
Program Files\Vex(or per-user if not run elevated). - Has an "Add Vex to the system PATH" task, checked by default, so after
installing you can open a new terminal and just run
vex myscript.vexfrom anywhere. - Has an "Associate .vex and .vh with Vex" task, checked by default. This
is driven by a real
.regfile,installer/vex_file_associations.reg: at install time, the[Code]section reads that template, substitutes the actual install path for theINSTALL_PATHplaceholder (and, for a per-user install, redirectsHKEY_CLASSES_ROOTtoHKEY_CURRENT_USER\Software\Classes), writes the patched file to a temp folder, and imports it withreg.exe import. Double-clicking a.vexfile then runs it withvex.exe; double-clicking a.vhfile opens it in Notepad (headers are meant to beimported, not run directly — you can still run one explicitly withvex file.vhif you want). The same template, with the placeholder left in, ships in{app}for reference or manual re-association. - Cleanly removes itself, its PATH entry, and its file associations on uninstall.
- Ships
examples/*.vex,examples/*.vh, and this README alongside the executable.
This is a from-scratch teaching/hobby-scale interpreter, not a production language:
- No garbage collector — memory used by a running script is freed when the process exits, not incrementally. Fine for scripts; not for long-running daemons.
- No module/import system — one file per run.
- Struct instances copy by reference (assigning
let b = a;for a struct or array shares the same underlying object, same as arrays/objects in most scripting languages). - Numbers are always doubles (no separate int type).