TLang is a compiled template language for structural code generation. Write a template once — generate every file, across every entity, in milliseconds. Add a new entity: every template that touches your model picks it up automatically.
TLang requires Rust 1.85 or later. Build from source:
git clone https://github.com/joel-f/tlang
cd tlang
cargo build --release
cargo install --path .
Verify:
tlang --version
Create Main.tlang:
use TLang.Terminal
func main(): String {
Terminal.println("Hello, TLang!")
return "ok"
}
Run it:
tlang compile-run Main.tlang
TLang compiles to bytecode on the first run and caches it. Subsequent runs reuse the cache and skip compilation — only recompiling when source files change.
A lang template generates code in a target language. Each call to the template produces one rendered instance:
use TLang.Terminal
lang [kotlin] entity(pkg: String, name: String) {
pkg ${pkg}
impl[data class] ${name}(
val id: Long,
val name: String
)
}
func main(): String {
entity("com.example.model", "User")
entity("com.example.model", "Product")
Terminal.println("Generated 2 entities.")
return "ok"
}
The ${pkg} and ${name} placeholders are substituted with the argument values at call time. Call the template once per entity — TLang handles the rest.
Hard-coding calls to entity(...) does not scale. Use set declarations to define your model data, then iterate over all sets in a loop:
use TLang.File
use TLang.Generator
lang [kotlin] entity(pkg: String, name: String) {
pkg ${pkg}
impl[data class] ${name}(
val id: Long,
val name: String
)
}
set User {
pkg: "com.example.model",
name: "User"
}
set Product {
pkg: "com.example.model",
name: "Product"
}
set Order {
pkg: "com.example.model",
name: "Order"
}
func main(): String {
for (model in Generator.models()) {
let code = entity(model.get("pkg"), model.get("name"))
File.write("src/" + model.get("name") + ".kt", code, true)
}
return "ok"
}
Adding a new entity is one new set block. Every template that iterates Generator.models() automatically includes it — no other changes required.
The doc template generates structured documents in multiple output formats from a single definition. Format is selected at call time:
use TLang.File
doc [md, html] apiDoc(service: String) {
# ${service} API Reference
This document is generated from a single template.
Update the template — every output format updates together.
[section "endpoints"
## Endpoints
[list "unordered"
- GET /health — liveness probe
- GET /api/v1/items — list all items
- POST /api/v1/items — create a new item
- DELETE /api/v1/items/:id — remove an item
]
]
}
func main(): String {
File.write("docs/api.md", apiDoc("md", "Inventory"), true)
File.write("docs/api.html", apiDoc("html", "Inventory"), true)
return "ok"
}
The style template generates CSS rules. Property values that contain spaces must be quoted. Pseudo-classes use the [hover], [focus], [active] modifier syntax:
use TLang.File
style [css] Theme(accent: String) {
:root {
--accent: ${accent},
--bg: "#0d0d0d",
--text: "#e8e8e8"
}
body {
background: "var(--bg)",
color: "var(--text)",
font-family: "system-ui, sans-serif",
margin: 0
}
.btn {
background: "var(--accent)",
padding: "0.65em 1.4em",
border-radius: 8px
}
.btn [hover] {
opacity: "0.85"
}
}
func main(): String {
File.write("assets/theme.css", Theme("css", "#7c5cfc"), true)
return "ok"
}
Split your project into modules. Use expose to export a symbol; use to import it:
// models/User.tlang
expose userEntity
lang [kotlin] userEntity(pkg: String) {
pkg ${pkg}
impl[data class] User(
val id: Long,
val name: String,
val email: String
)
}
// Main.tlang
use TLang.File
use models.User
func main(): String {
File.write("src/User.kt", userEntity("com.example"), true)
return "ok"
}
For multi-file projects, add a manifest.yml at the project root:
name: MyProject
project: MyApp
organisation: MyOrg
version: 1.0.0
stability: stable
releaseNumber: 1
main: Main
Run from the project directory:
tlang compile-run Main.tlang