Unfish Learn Documentation & Tutorials
Ctrl+K
🐡 Unfish Play 📥 Downloads 💻 Studio IDE Portal GitHub

🐡 Welcome to Unfish

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.
  • Level 4: Computer Systems & Runtimes — Contiguous byte buffers, little-endian access, memory layout inspection, mark-and-sweep garbage collection, and custom bytecode virtual machines.
  • Level 5: Native & WebAssembly Compilation — Compiling directly into standalone, dependency-free C99 machine code and WebAssembly.

The Five-Engine Differential Architecture

Unfish features five distinct execution tiers kept in 100% lockstep by an automated differential conformance suite:

TierEngineDescriptionBest For
1 AST Interpreter Direct syntax tree walking interpreter Interactive REPL, instant startup, visual blocks
2 Stack Bytecode VM 36-opcode stack virtual machine with upvalues 2x–6x execution speedup, instruction tracing
3 Register Bytecode VM 256-register 3-address VM with computed-goto Fast interpreted execution, compiler education
4 Native C99 Compiler AOT transpilation into freestanding C99 30x–200x native execution speedup, binary deployment
5 WebAssembly Target Portable WebAssembly binary output In-browser execution, serverless workers
examples/hello.uf

⚡ Installation & Setup

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:

CommandPurpose
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 studioLaunch the browser IDE & visual blocks canvas
unfish lspStart Language Server Protocol server for VS Code
unfish replLaunch 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:

  • Numbers — 64-bit IEEE 754 floating-point numbers (42, 3.14159, 0xFF, 1e6).
  • 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.
  • repeat <count> times: — Dedicated beginner-friendly fixed count loop.
tut06_loops.uf

07. Functions & Default Parameters

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.

FunctionSignatureDescription
say(val)(Any) -> nullPrints value followed by a newline to standard output
print(val)(Any) -> nullPrints value without a trailing newline
len(coll)(Any) -> NumberReturns length of string, array, or byte buffer
type_of(val)(Any) -> StringReturns type name: "number", "string", etc.
push(arr, val)(Array, Any) -> nullAppends value to dynamic array
pop(arr)(Array) -> AnyRemoves and returns last element
range(a, [b, step])(Number, ...) -> ArrayGenerates range of numbers
concat(a, b)(Array, Array) -> ArrayNon-destructively merges two arrays
flatten(arr)(Array) -> ArrayFlattens one level of nested arrays
zip(a, b)(Array, Array) -> ArrayCombines two arrays element-wise into pairs
pad_start(s, len, [p])(String, Number, [String]) -> StringPads string on the left to target length
pad_end(s, len, [p])(String, Number, [String]) -> StringPads string on the right to target length
trim_start(s)(String) -> StringRemoves leading whitespace
trim_end(s)(String) -> StringRemoves trailing whitespace
chars(s)(String) -> ArrayReturns array of single-character strings
count(s, sub)(String, String) -> NumberCount of non-overlapping occurrences
clock()() -> NumberHigh-resolution wall-clock timestamp in seconds
assert(cond, [msg])(Any, [String]) -> nullRaises 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.

FunctionSignatureDescription
sys.platform()() -> StringReturns host OS name: "linux", "darwin", "windows"
sys.exit(code)(Number) -> neverTerminates process with integer exit code
sys.args()() -> ArrayArray of CLI argument strings passed to script
sys.cwd()() -> StringCurrent working directory path string
sys.env(key)(String) -> String|nullReturns environment variable value, or null
sys.set_env(k, v)(String, String) -> BooleanSets host environment variable
sys.exec(cmd)(String) -> NumberExecutes shell command via system()

Module: fs (Sandboxed Filesystem)

File I/O and directory operations. Import with import fs.

FunctionSignatureDescription
fs.read_text(path)(String) -> String|nullReads entire file contents as UTF-8 string
fs.write_text(path, text)(String, String) -> BooleanOverwrites file with text
fs.append_text(path, text)(String, String) -> BooleanAppends text to end of file
fs.exists(path)(String) -> BooleanReturns true if file or directory exists
fs.delete_file(path)(String) -> BooleanDeletes file at path
fs.list_dir(path)(String) -> Array|nullReturns array of entry filenames in directory
fs.mkdir(path)(String) -> BooleanCreates new directory
fs.remove_dir(path)(String) -> BooleanRemoves empty directory
fs.is_file(path)(String) -> BooleanReturns true if path is a regular file
fs.is_dir(path)(String) -> BooleanReturns true if path is a directory
fs.file_size(path)(String) -> Number|nullReturns file size in bytes