The Tawla programming language and its tawlac compiler
Project description
Tawla
Tawla is a small programming language with its own compiler, tawlac, built
from scratch. It looks a lot like C#: you write classes with fields and methods,
you get inheritance and interfaces, and everything is statically typed. Under the
hood tawlac turns your code into real machine code using LLVM and runs it on
the spot, so there's no separate "compile then run" dance.
It started as a learning project to understand how compilers actually work, and
it grew into a genuinely usable little language. Source files end in .twl.
Documentation: https://haddajidev.github.io/tawla-lang-docs/
Here's the whole "hello world":
class Main {
void main() {
print("Hello, Tawla!");
}
}
$ tawlac run hello.twl
Hello, Tawla!
Getting it running
You need Python 3.11 or newer. Install it from PyPI with pip:
pip install tawla
That pulls in everything it needs (including LLVM, via llvmlite) and gives you the
tawlac command. Works the same on Windows, macOS, and Linux.
No Python? Download a standalone binary
Each release also ships a self-contained tawlac that bundles everything it
needs — no Python install required. Grab the one for your OS from the
Releases page and put it on
your PATH:
- Windows: download
tawlac-windows.exe, rename it totawlac.exe, move it into a folder such asC:\tools\tawla, then add that folder to yourPathenvironment variable (Settings → "Edit the system environment variables" → Environment Variables → editPath). Open a new terminal and runtawlac. - macOS: download
tawlac-macos, then:
First launch may be blocked because the binary is unsigned — allow it under System Settings → Privacy & Security, or runmv tawlac-macos tawlac chmod +x tawlac sudo mv tawlac /usr/local/bin/xattr -d com.apple.quarantine /usr/local/bin/tawlac. - Linux: download
tawlac-linux, then:mv tawlac-linux tawlac chmod +x tawlac sudo mv tawlac /usr/local/bin/
Then tawlac run app.twl works from anywhere, exactly like the pip version.
Hacking on it from a clone of this repo instead? That works too:
pip install llvmlite
python -m tawla run examples/hello.twl
Either way you get the tawlac command (or python -m tawla) with a few
subcommands:
tawlac run app.twl # compile and run a file
tawlac new myapp # scaffold a new project (like cargo new)
tawlac init # scaffold one in the current folder
tawlac run # run the current project (reads Tawla.toml)
tawlac version # what version is this
tawlac help # or: tawlac help run
What the language can do
- The basics:
int,float/double,bool, andstring; arithmetic and comparisons;if/else,while,for, and functions (recursion works fine). - Numbers: integer math with
int, and 64-bit floating point withfloat(ordouble— same thing). Ints widen to floats automatically when you mix them, so7.0 / 2is3.5while7 / 2stays3. forloops: the C-stylefor (int i = 0; i < n; i++) { ... }, with the loop variable scoped to the loop.- Increment/decrement:
i++,++i,i--,--ias shorthand fori = i + 1/i = i - 1(statement form — works on variables, fields, and array elements). - Logical operators:
&&,||, and!on bools, with short-circuit evaluation (sou != null && u.alive()is safe). - Ternary:
cond ? a : bpicks a value inline (lazy — only the taken branch runs). var: skip the type and let it be inferred —var x = 5;.null& defaults: reference types (objects, strings, arrays) can benull, and a declaration can skip the initializer —int x;is0,bool b;isfalse,User u;isnull. Using anull(calling a method, reading a field, indexing) gives a clean "null reference" error instead of a crash. Value types (int,float,bool) can't be null.- Classes: fields, methods, constructors,
this, andnew. Objects live on the heap. - Inheritance:
class Dog : Animal { ... }, with method overriding andsuper(...)to call the base constructor. - Encapsulation: members are
privateby default; mark thempublicto expose them orprotectedto share with subclasses. Constructors arepublicby default. Access is checked at compile time. (Heads up: code written for Tawla 0.x needspublicadded to anything used across class boundaries.) - Polymorphism: methods are virtual, so the right one gets picked at runtime based on the actual object type (this is done with vtables).
- Interfaces:
interface Shape { int area(); }, and any class can implement it, even classes that share no common parent. - Abstract classes: mark a class
abstractand leave some methods without a body for subclasses to fill in. - Generics:
class Box<T> { ... }, used asBox<int>orBox<string>. - Strings: literals with escapes,
s.length,==, and+to join them, pluscharAt(s, i)(character code),substring(s, a, b),toInt(s)/toFloat(s), andtoString(n)(number to string). - Arrays:
new int[n], indexing witha[i](bounds-checked, so you get a clear error instead of a crash), anda.length. - Comments:
// like this. - Imports: split code across files with
import "other.twl";— the path is relative to the file importing it, and the.twlis optional. - IO library:
import "IO.twl";(a small module that ships with the compiler) gives youreadLine(),readInt(),readFloat(), andwrite(s)(print with no trailing newline) for reading input and prompting. - Collections:
import "Collections.twl";gives you a growableList<T>(add,get,set,size) and aMap<K,V>(put,get,has,size).Map.getreturnsnullfor a missing object value. (No nested generics yet, e.g.Map<string, List<int>>.) - JSON:
import "Json.twl";thenparseJson(text)returns aJsonvalue you navigate withget/at/sizeand read withasInt/asFloat/asBool/asString(plusisNull/isArray/... checks). Build withjsonObject()/jsonArray()+setString/setInt/set/push…andtoString(); in a handler,req.respondJson(200, out.toString())sendsapplication/json. The whole parser/serializer is written in Tawla itself. panic(s): print a message and abort, for unrecoverable errors.- Exceptions:
fuck_around { ... } find_out (e) { ... }is try/catch —eis the error message string.throw "msg";raises one, and built-in errors (panic, null dereference, array-out-of-bounds) are catchable too. Use barefind_out { ... }to ignore the message. - HTTP server:
import "Http.twl";gives you aServer, aRequest, and an Express-styleRouterwithHandlerclasses. Routes take path params —router.get("/users/:id", new GetUser())— and inside a handlerreq.param("id"),req.query("page"), andreq.header("Authorization")read the path param, query string, and request header (eachnullwhen absent).req.method()/path()/body()/respond()/respondJson()round it out. Single-threaded, minimal HTTP/1.1. - HTTP client (
fetch):fetch(url)(GET) orhttpRequest(method, url, body)returns aResponsewithstatus()andbody()— call other services. Network failures come back asstatus() == 0. - SQLite:
import "Sql.twl";gives youDb, preparedStmts, and aRowscursor —Db db = new Db("app.db"); Stmt q = db.prepare("SELECT name FROM users WHERE age > ?"); q.bindInt(0, 18); Rows r = q.query();thenr.next()/r.getString("name"). Parameters bind by index (injection-safe); SQL errors throw (catch withfuck_around/find_out). - Backend essentials:
import "Sys.twl";(getenv,now/nowMillis/sleepMillis,uuid),import "Fs.twl";(readFile/writeFile/appendFile— throwing — andexists), andimport "Crypto.twl";(sha256,hmacSha256). The basics for config, logging, IDs, and signing. - String interpolation:
"hi ${user.name}, ${n + 1} items"inside any string literal (a bare$stays literal). Embedded expressions are stringified withtoString, which now also handlesboolandstring. - JSON serialization: every class has an auto-generated
toJson()returning a JSON string over its fields (primitives, nested objects, arrays) —req.respondJson(200, user.toJson()). (JSON → object parsing stays inJson.twl.) - Built-in functions: a handful of predefined functions you can call without
declaring anything —
sqrt,pow,abs,min,max,floor,ceilfor math, pluscollect()/__live()for the GC. - Garbage collection: you don't free memory by hand. Call
collect()when you want a cleanup pass;__live()tells you how many objects are still around.
There's a runnable example for pretty much every feature in examples/ — poke
around in there to see how things look in practice.
How it works, roughly
tawlac runs your code through the usual compiler stages:
source.twl -> lexer -> parser -> sema -> codegen -> LLVM -> JIT -> runs
- the lexer chops the text into tokens
- the parser builds a tree out of those tokens
- sema checks the types and catches mistakes before any code is generated
- codegen turns the checked tree into LLVM instructions
- LLVM compiles that to machine code and the JIT runs it immediately
Each piece lives in its own file under tawla/, so it's not hard to follow if
you want to read along.
Running the tests
./venv/Scripts/python -m pytest
There are around 200 tests. Programs that print output are checked by running
tawlac as a separate process and looking at what it prints (this sidesteps a
Windows quirk where output from JIT-compiled code is hard to capture in-process).
What's not done yet
It's a real language, but it's still a young one. A few honest gaps:
tawlac build(making a standalone.exefrom your Tawla program) isn't done — for now everything runs throughtawlac run.- Generics only cover classes, not standalone functions, and you can't nest them
like
Box<Box<int>>. - Garbage collection has to be triggered with
collect(); it doesn't kick in on its own yet.
The full design and the step-by-step history of how it was built are in
docs/superpowers/specs/2026-05-29-tawla-language-design.md if you're curious.
License
MIT — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tawla-1.9.0.tar.gz.
File metadata
- Download URL: tawla-1.9.0.tar.gz
- Upload date:
- Size: 89.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5a27eff7b9d8cf246d1ad9528b39b08dfef7eee6dc8c2d6bb9c61ceb288623b9
|
|
| MD5 |
1e352cee431e1d423e86f046b4d7ddf8
|
|
| BLAKE2b-256 |
23f8584eee277ef7e57b5606e2d2521dd48a75de5660039d4ffa3468f6b8ccd2
|
File details
Details for the file tawla-1.9.0-py3-none-any.whl.
File metadata
- Download URL: tawla-1.9.0-py3-none-any.whl
- Upload date:
- Size: 65.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2afe07f385096099840ebab85fcd7045d627e7e766a4e50b079409aabae7ffbb
|
|
| MD5 |
70e300c65baa3dccbe0e70c1d20a2085
|
|
| BLAKE2b-256 |
dfcad1b574ec548f77fc98ceba2a3da676d08e73571161b3adb53f0492ccfd6f
|