Unfish is a serious, general-purpose programming language, compiler, bytecode virtual machine, and educational computing environment implemented in pure ANSI C99 with zero external dependencies.
"Hide complexity until the learner is ready — never hide it forever."
The Unbroken Path of Intellectual Ascent
Most programming languages force learners into a compromise: either use an overly simplified toy environment (like Scratch or simple web editors) that conceals how computers actually work, or dive directly into complex industrial languages (like C++ or Rust) where boilerplate and cryptic toolchains overwhelm beginners.
Unfish resolves this dilemma through a clean, unified architecture:
Level 1: Visual Blocks — Drag, snap, and assemble visual logic blocks with real-time two-way synchronization into source code.
Level 2: Clean Scripting — Human-oriented, indentation-based syntax without semicolons or noisy braces.
Level 3: Modern Language Engineering — Structs, object methods, algebraic enums, pattern matching, closures, cooperative fibers, and destructuring.
Unfish requires only a standard C99 compiler (such as gcc or clang) and standard POSIX make. It has zero external dependencies beyond libc and -lm.
Building from Source
Terminal Shell (Bash)
git clone https://github.com/ashokwebs/unfish.git
cd unfish
make -j4
./bin/unfish version
Running the Test Suites
Verification Commands
# Run all 21 unit test suites and the 104-test differential conformance suite
make test
# Verify zero memory leaks under AddressSanitizer & UndefinedBehaviorSanitizer
make test-asan
# Run multi-tier performance benchmarks
make bench
🚀 Your First Program
Unfish source code uses the .uf or .unfish file extension. Let's write a program that calculates Fibonacci numbers and prints them using formatted strings.
fibonacci.uf
💻 REPL & Developer Toolchain
The unfish executable provides a unified interface across the development lifecycle:
Command
Purpose
unfish run <file>
Execute script using tree-walking AST interpreter
unfish run --vm <file>
Execute script using Stack Bytecode VM
unfish run --regvm <file>
Execute script using Register Bytecode VM
unfish build -o <bin> <file>
Compile program to native executable binary
unfish build --wasm <file>
Compile program to WebAssembly module
unfish format -i <file>
Format source code to canonical style in place
unfish debug <file>
Launch interactive step debugger with breakpoints
unfish studio
Launch the browser IDE & visual blocks canvas
unfish lsp
Start Language Server Protocol server for VS Code
unfish repl
Launch interactive Read-Eval-Print Loop
🔬 Learn the Computer through Unfish
A defining mission of Unfish is to teach computer systems by making every layer of language execution visible and tangible. Type any code below and click through the tabs to observe how human-readable text transforms into tokens, syntax trees, bytecode instructions, and runtime memory.
Interactive Architectural Pipeline Explorer
01. Hello World & Text Output
In Unfish, printing to standard output is done with say (appends a newline) or the print(value) function (no trailing newline). Unfish also features Python-style string interpolation via f"...".
tut01_output.uf
02. Values & Primitive Types
Unfish runtime values are represented internally by a 16-byte tagged union (UfValue). The primitive types are:
Strings — Contiguous UTF-8 encoded text with length tracking and string interning.
Booleans — true or false.
Null — Represents absence of value (null).
tut02_values.uf
03. Variables & Lexical Scope
Variables in Unfish are declared with the let keyword. Once declared, variables can be reassigned with =:
tut03_variables.uf
04. Expressions & Operators
Unfish parses expressions using Vaughan Pratt's top-down operator precedence table. It supports standard arithmetic (+, -, *, /, %, with pow(a, b) for exponents), comparisons, logical operators (and, or, not), and the forward pipe operator (|>).
tut04_operators.uf
05. Conditional Logic
Blocks in Unfish are delimited using off-side indentation (4 spaces) introduced with a colon :.
tut05_conditions.uf
06. Loops & Iteration
Unfish supports three loop constructs:
while <condition>: — Loop while condition remains truthy.
for item in <collection>: — Iterate over arrays, strings, or map keys.
Functions are declared with function or fn and can include default parameter values:
tut07_functions.uf
09. Closures & Functional Pipelines
Functions in Unfish are first-class values that capture variables from their enclosing lexical environments. Anonymous lambda functions can be written with fn(args): expr.
tut09_closures.uf
12. Structs & Object Methods
Unfish provides lightweight structs with instance fields and methods bound to self:
tut12_structs.uf
13. Enums & Pattern Matching
Enums in Unfish are tagged sum types whose variants can carry data, matched with match ... when ...:
tut13_enums.uf
17. Cooperative Fibers & CSP Channels
Concurrency in Unfish is based on lightweight green threads (fibers) and Communicating Sequential Processes (CSP) channels, operating with deterministic scheduling and zero race conditions.
tut17_concurrency.uf
Core Built-in Functions
All core functions are available globally in every Unfish program without needing an import statement.
Function
Signature
Description
say(val)
(Any) -> null
Prints value followed by a newline to standard output
print(val)
(Any) -> null
Prints value without a trailing newline
len(coll)
(Any) -> Number
Returns length of string, array, or byte buffer
type_of(val)
(Any) -> String
Returns type name: "number", "string", etc.
push(arr, val)
(Array, Any) -> null
Appends value to dynamic array
pop(arr)
(Array) -> Any
Removes and returns last element
range(a, [b, step])
(Number, ...) -> Array
Generates range of numbers
concat(a, b)
(Array, Array) -> Array
Non-destructively merges two arrays
flatten(arr)
(Array) -> Array
Flattens one level of nested arrays
zip(a, b)
(Array, Array) -> Array
Combines two arrays element-wise into pairs
pad_start(s, len, [p])
(String, Number, [String]) -> String
Pads string on the left to target length
pad_end(s, len, [p])
(String, Number, [String]) -> String
Pads string on the right to target length
trim_start(s)
(String) -> String
Removes leading whitespace
trim_end(s)
(String) -> String
Removes trailing whitespace
chars(s)
(String) -> Array
Returns array of single-character strings
count(s, sub)
(String, String) -> Number
Count of non-overlapping occurrences
clock()
() -> Number
High-resolution wall-clock timestamp in seconds
assert(cond, [msg])
(Any, [String]) -> null
Raises AssertionError if condition is falsy
Module: sys
Access operating system parameters, environment variables, and process control. Import with import sys or from sys import platform, exit.
Function
Signature
Description
sys.platform()
() -> String
Returns host OS name: "linux", "darwin", "windows"
sys.exit(code)
(Number) -> never
Terminates process with integer exit code
sys.args()
() -> Array
Array of CLI argument strings passed to script
sys.cwd()
() -> String
Current working directory path string
sys.env(key)
(String) -> String|null
Returns environment variable value, or null
sys.set_env(k, v)
(String, String) -> Boolean
Sets host environment variable
sys.exec(cmd)
(String) -> Number
Executes shell command via system()
Module: fs (Sandboxed Filesystem)
File I/O and directory operations. Import with import fs.