A small, unsafe language for writing unsafe things safely.
Hemlock is a systems scripting language that combines the power of C with the ergonomics of modern scripting languages. It embraces manual memory management and explicit control while providing structured async concurrency built-in.
For the complete language manual and documentation, visit hem-doc.
- Explicit over implicit - No hidden behavior, no magic
- Manual memory management - You allocate, you free
- Dynamic by default, typed by choice - Optional type annotations with runtime checking
- Unsafe is a feature - Full control when you need it, safety tools when you want them
print("Hello, World!");
// Raw pointer (dangerous but flexible)
let p: ptr = alloc(64);
memset(p, 0, 64);
free(p);
// Safe buffer (bounds checked)
let buf: buffer = buffer(64);
buf[0] = 65;
free(buf);
async fn compute(n: i32): i32 {
let sum = 0;
for (let i = 0; i < n; i = i + 1) {
sum = sum + i;
}
return sum;
}
// Spawn tasks on separate OS threads (real pthreads!)
let t1 = spawn(compute, 1000);
let t2 = spawn(compute, 2000);
// Wait for results (both computing in parallel)
let r1 = join(t1);
let r2 = join(t2);
import "libc.so.6";
extern fn strlen(s: string): i32;
extern fn getpid(): i32;
let len = strlen("Hello!");
let pid = getpid();
| Category | Highlights |
|---|---|
| Types | i8-i64, u8-u64, f32/f64, bool, string, rune, ptr, buffer, array, object |
| Memory | alloc, free, memset, memcpy, realloc, talloc, sizeof |
| Strings | UTF-8, mutable, 19 methods (substr, split, trim, replace, etc.) |
| Arrays | Dynamic, 23 methods (push, pop, map, filter, reduce, sort, fill, etc.) |
| Concurrency | async/await, real OS threads (pthreads), channels |
| FFI | Call C functions from shared libraries, export extern |
| Error Handling | try/catch/finally/throw, panic() |
| I/O | File API, signal handling, command execution |
| Stdlib | 52 modules (math, net, crypto, signal, atomic, ffi, and more) |
| Packages | hpm package manager with GitHub registry |
macOS:
brew install libffi openssl@3 libwebsocketsUbuntu/Debian:
sudo apt-get install libffi-dev libssl-dev libwebsockets-devmake # Build hemlock
make test # Run all testsQuick Install (recommended):
curl -fsSL https://raw.githubusercontent.com/hemlang/hemlock/main/install.sh | bashFrom source:
sudo make install # Install to /usr/local
make install PREFIX=~/.local # Install to custom prefix
sudo make uninstall # Remove installationHemlock can run in a web browser by compiling the interpreter to WebAssembly via Emscripten.
# Install Emscripten (one-time setup)
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk && ./emsdk install latest && ./emsdk activate latest && source ./emsdk_env.sh
cd ..
# Build the WASM interpreter
make wasm-interpreter
# Serve the browser example (builds WASM if needed)
make wasm-browser-example
# Open http://localhost:8080/examples/wasm-browser/index.htmlOr use it from JavaScript:
var Module = {
print: function(text) { console.log(text); },
onRuntimeInitialized: function() {
var hemlockEval = Module.cwrap('hemlock_eval', 'number', ['string']);
hemlockEval('print("Hello from Hemlock WASM!");');
},
noInitialRun: true
};See Installation - WASM Build for full details and examples/wasm-browser/ for a complete browser integration example.
./hemlock program.hml # Run a program
./hemlock program.hml arg1 arg2 # With arguments
./hemlock # Start REPLHemlock provides tools to bundle multi-file projects and create self-contained executables.
Resolve all imports and create a single distributable file:
./hemlock --bundle app.hml # Create app.hmlc
./hemlock --bundle app.hml --compress # Create app.hmlb (smaller)
./hemlock --bundle app.hml -o dist/app.hmlc # Custom output pathCreate a standalone executable that includes the interpreter:
./hemlock --package app.hml # Create ./app executable
./hemlock --package app.hml -o myapp # Custom name
./hemlock --package app.hml --no-compress # Faster startup, larger fileThe packaged executable runs anywhere without needing Hemlock installed.
See Bundling & Packaging for details.
Hemlock v2.0.1 is released with bugfixes since v2.0.0:
- BREAKING: Reduced builtin conflicts - Moved 63 builtins to
@stdlibmodules (math, signal, net, process, fs, atomic, debug, ffi) to reduce global namespace pollution - New stdlib modules -
@stdlib/signal,@stdlib/atomic,@stdlib/debug,@stdlib/ffi - C macro conflict prevention - Compiler sanitizes imported names that conflict with C system macros
- Full type system with 64-bit integers and Unicode support
- Pattern matching with destructuring, guards, and rest syntax
- Expression-bodied functions, type aliases, named arguments, null coalescing
- Manual memory management with safe and unsafe options
- Async/await with true pthread parallelism
- 52 stdlib modules
- FFI for C interop with
export extern fnfor reusable library wrappers - Compiler backend (C code generation) with 100% interpreter parity
- LSP server with go-to-definition and find-references
- hpm package manager with GitHub-based registry
- 900+ tests with 231 parity tests (100% pass rate)
"We give you the tools to be safe (
buffer, type annotations, bounds checking) but we don't force you to use them (ptr, manual memory, unsafe operations)."
Hemlock is NOT memory-safe. Dangling pointers, use-after-free, and buffer overflows are your responsibility. We provide the tools to help, but we don't force you to use them.
MIT License
Hemlock is experimental and evolving. If you're interested in contributing, please read CLAUDE.md first to understand the design philosophy.
