docs | ||
examples | ||
lib | ||
src | ||
tests | ||
.gitignore | ||
go.mod | ||
go.sum | ||
main.go |
The Q Programming Language
Q is a minimal, dependency-free programming language and compiler targeting x86-64 and arm64 with ultra-fast builds and tiny binaries.
Features
- High performance (
ssa
andasm
optimizations) - Fast compilation (<100 μs for simple programs)
- Tiny executables ("Hello World" is ~600 bytes)
- Multiple platforms (Linux, Mac and Windows)
- Zero dependencies (no llvm, no libc)
Installation
Warning
Q is under heavy development and not ready for production yet.
Please read the comment on the status of the project.
Feel free to contact me if you are interested in helping out.
Build from source:
git clone https://git.urbach.dev/cli/q
cd q
go build
Install via symlink:
ln -s $PWD/q ~/.local/bin/q
Usage
Run:
q examples/hello
Build:
q build examples/hello
Cross-compile:
q build examples/hello --os [linux|mac|windows] --arch [x86|arm]
News
- 2025-08-18: Slices for strings.
- 2025-08-17: Struct allocation by value/reference.
- 2025-08-16: Multiple return values.
- 2025-08-15: Data structures.
- 2025-08-14: Memory load and store instructions.
- 2025-08-13: Naive memory allocations.
- 2025-08-12: Support for Windows on arm64.
- 2025-08-11: Support for Mac on arm64.
Examples
The syntax is still highly unstable because I'm focusing my work on the correct machine code generation for all platforms and architectures. However, you can take a look at the examples and the tests to get a perspective on the current status.
Source
The source code structure uses a flat layout without nesting:
- arm - arm64 architecture
- asm - Generic assembler
- ast - Abstract syntax tree
- cli - Command line interface
- codegen - SSA to assembly code generation
- compiler - Compiler frontend
- config - Build configuration
- core - Defines
Function
and compiles tokens to SSA - cpu - Types to represent a generic CPU
- data - Data container that can re-use existing data
- dll - DLL support for Windows systems
- elf - ELF format for Linux executables
- errors - Error handling that reports lines and columns
- exe - Generic executable format to calculate section offsets
- expression - Expression parser generating trees
- fs - File system access
- global - Global variables like the working directory
- linker - Frontend for generating executable files
- macho - Mach-O format for Mac executables
- memfile - Memory backed file descriptors
- pe - PE format for Windows executables
- scanner - Scanner that parses top-level instructions
- set - Generic set implementation
- sizeof - Calculates the byte size of numbers
- ssa - Static single assignment types
- token - Tokenizer
- types - Type system
- verbose - Verbose output
- x86 - x86-64 architecture
The typical flow for a build command is the following:
FAQ
Which platforms are supported?
arm64 | x86-64 | |
---|---|---|
🐧 Linux | ✔️ | ✔️ |
🍏 Mac | ✔️ | ✔️ |
🪟 Windows | ✔️ | ✔️ |
How tiny is a Hello World?
arm64 | x86-64 | |
---|---|---|
🐧 Linux | 646 bytes | 582 bytes |
🍏 Mac | 16.3 KiB | 4.2 KiB |
🪟 Windows | 1.7 KiB | 1.7 KiB |
Does the compiler generate efficient machine code?
The backend is built on a Static Single Assignment (SSA) intermediate representation, the same approach used by mature compilers such as gcc
, go
, and llvm
. SSA greatly simplifies the implementation of common optimization passes, allowing the compiler to produce relatively high-quality assembly code despite the project's early stage of development.
Can I use it in scripts?
Yes. The compiler can build an entire script within a few microseconds.
#!/usr/bin/env q
import io
main() {
io.write("Hello\n")
}
You need to create a file with the contents above and add execution permissions via chmod +x
. Now you can run the script without an explicit compiler build. The generated machine code runs directly from RAM if the OS supports it.
Any security features?
PIE: All executables are built as position independent executables supporting a dynamic base address.
W^X: All memory pages are loaded with either execute or write permissions but never with both. Constant data is read-only.
Read | Execute | Write | |
---|---|---|---|
Code | ✔️ | ✔️ | ❌ |
Data | ✔️ | ❌ | ❌ |
Any editor extensions?
Neovim: Planned.
VS Code: Clone the vscode-q repository into your extensions folder (it enables syntax highlighting).
Why is it written in Go and not language X?
Because of readability and great tools for concurrency. The implementation will be replaced by a self-hosted compiler in the future.
I can't contribute but can I donate to the project?
If I donate, what will my money be used for?
Testing infrastructure and support for existing and new architectures.
How do I pronounce the name?
/ˈkjuː/ just like Q
in the English alphabet.
FAQ: Contributors
How do I run the tests?
# Run all tests:
go run gotest.tools/gotestsum@latest
# Generate coverage:
go test -coverpkg=./... -coverprofile=cover.out ./...
# View coverage:
go tool cover -func cover.out
go tool cover -html cover.out
How do I run the benchmarks?
# Run compiler benchmarks:
go test ./tests -run '^$' -bench . -benchmem
# Run compiler benchmarks in single-threaded mode:
GOMAXPROCS=1 go test ./tests -run '^$' -bench . -benchmem
# Generate profiling data:
go test ./tests -run '^$' -bench . -benchmem -cpuprofile cpu.out -memprofile mem.out
# View profiling data:
go tool pprof --nodefraction=0.1 -http=:8080 ./cpu.out
go tool pprof --nodefraction=0.1 -http=:8080 ./mem.out
Any debugging tools?
I recently added a preliminary io.writeInt
to have some basic output for numeric values during compiler development.
You can also use the excellent blinkenlights from Justine Tunney to step through the x86-64 executables.
Is there an IRC channel?
#q on irc.urbach.dev.
License
Please see the license documentation.
Copyright
© 2025 Eduard Urbach