Version: 1.0
Last Updated: June 2026
- Getting Started
- Language Basics
- Type System
- Functions
- Annotations
- Working with Tensors
- GPU Kernels
- Control Flow
- Modules & Imports
- Built-in Operations
- IDE Setup
- Formatter & Linter
- Error Handling
- Best Practices
- Complete Examples
For cloning the repo, building the tpt CLI, and writing/compiling your first .tpts file, see the root README.md Quick Start — this guide picks up from there and focuses on the language itself. Prefer a live, no-install environment? Try the browser playground.
The playground is a WASM build of the layer7 compiler with a live editor, example kernels, and TPTIR/Rust/perf/error output tabs — no server round-trip required. To launch it locally:
cd tools/tpt-playground
# Linux/macOS
./build.sh
# Windows
./build.ps1
# Serve the built directory (must be HTTP, not file://)
python3 -m http.server 8080
# open http://localhost:8080See tools/tpt-playground/README.md for details.
Beyond the tpt CLI, layer7 also builds an LSP server, formatter, and linter:
cd layer7_tptb
cargo build -p tpt-gpu-script-lsp # LSP server (see §11 IDE Setup)
cargo build -p tpt-gpu-script-format # Formatter/linter (see §12)
cargo run -p tpt-gpu-script-format -- fmt hello.tpts
cargo run -p tpt-gpu-script-format -- lint hello.tptsTPT Script files use the .tpts extension.
Variables are declared with let and are immutable by default:
let x = 42 // i64 (inferred)
let y: f32 = 3.14 // explicit type
let name = "TPT" // string literal
let flag: bool = true // boolean
// This is a line comment
/*
This is a block comment
that spans multiple lines
*/
| Operator | Description | Example |
|---|---|---|
+ - * / % |
Arithmetic | a + b, x * 2 |
== != < > <= >= |
Comparison | x == y, a < b |
&& || |
Logical | a && b, !x |
! |
Logical not | !flag |
= |
Assignment (in let) | let x = 5 |
-> |
Return type / arrow | fn f() -> f32 |
.. |
Range | 0..10 |
..= |
Inclusive range | 0..=10 |
. |
Field/method access | tpt.zeros(...) |
:: |
Path separator | import tpt::nn |
| Type | Description | Example Literal |
|---|---|---|
i8, i16, i32, i64 |
Signed integers | 42, -10, 1_000_000 |
u8, u16, u32, u64 |
Unsigned integers | 42u |
f16, bf16, f32, f64 |
Floating point | 3.14, 1.0e-5 |
bool |
Boolean | true, false |
index |
Platform index type | (used internally) |
Tensors are the primary data type. They are parameterized by element dtype and shape:
Tensor[f32, 224, 224] // 224x224 f32 image
Tensor[f32, batch, seq, d] // Symbolic batch/seq dimensions
Tensor[i64, *] // Dynamic-shaped 1D tensor
Tensor[f32, 3, 224, 224] // CHW image
| Type | Description |
|---|---|
Model |
A trained model loaded from disk |
DataLoader |
Iterable yielding training batches |
ComputeStream |
GPU command queue |
Optimizer |
Training optimizer (SGD, Adam, etc.) |
Checkpoint |
Model checkpoint for saving/restoring |
TPT Script infers types whenever possible:
let x = 42 // inferred as i64
let y = 3.14 // inferred as f64
let z = x + 1 // inferred as i64
@doc("Add two numbers")
fn add(a: f32, b: f32) -> f32 {
return a + b
}
Parameters are name-type pairs separated by commas:
fn matmul(
a: Tensor[f32, m, k],
b: Tensor[f32, k, n],
) -> Tensor[f32, m, n] {
return tpt.matmul(a, b)
}
Use return to return a value. The last expression in a block can also be returned:
// Explicit return
fn add(a: f32, b: f32) -> f32 {
return a + b
}
// Expression return (no semicolon on last line)
fn add(a: f32, b: f32) -> f32 {
a + b
}
Annotations provide metadata for functions and types. They are prefixed with @:
Human-readable documentation (also used by AI agents):
@doc("Compute softmax along the last dimension")
fn softmax(x: Tensor[f32, batch, seq, d]) -> Tensor[f32, batch, seq, d] {
return tpt.softmax(x, dim=-1)
}
Document function parameters and return values:
fn matmul(
@input("Left matrix, shape [m, k]") a: Tensor[f32, m, k],
@input("Right matrix, shape [k, n]") b: Tensor[f32, k, n],
) -> @output("Result matrix, shape [m, n]") Tensor[f32, m, n] {
return tpt.matmul(a, b)
}
Provide usage examples:
@example("let y = relu(x)")
fn relu(x: Tensor[f32, n]) -> Tensor[f32, n] {
return tpt.relu(x)
}
Compile-time constraints:
@constraint("a.shape[1] == b.shape[0]", error="Inner dimensions must match")
fn matmul(a: Tensor[f32, m, k], b: Tensor[f32, k, n]) -> Tensor[f32, m, n] {
return tpt.matmul(a, b)
}
Performance metadata:
@complexity("O(m * n * k)")
@memory("O(m * n)")
@flops("2 * m * n * k")
fn matmul(a: Tensor[f32, m, k], b: Tensor[f32, k, n]) -> Tensor[f32, m, n] {
return tpt.matmul(a, b)
}
Hardware requirements:
@requires_gpu(true)
@requires_tensor_cores(true)
@min_vram_gb(16)
fn train_step(model: Model, batch: DataLoader) {
// ...
}
Distributed execution metadata:
@distributed(strategy="fsdp", devices=8)
@supports_distributed(true)
@max_batch_size(512)
fn train_epoch(model: Model, data: DataLoader) {
// ...
}
Deployment target:
@deploy(target="cloud", optimize=true)
fn infer(model: Model, x: Tensor[f32, batch, seq]) -> Tensor[i64, batch] {
tpt.no_grad {
let logits = model.forward(x)
return tpt.argmax(logits, dim=1)
}
}
Autodiff support:
@differentiable(true)
@gradient_checkpoint(enabled=true)
fn transformer_block(x: Tensor[f32, seq, d]) -> Tensor[f32, seq, d] {
// ...
}
let zeros = tpt.zeros([64, 32], dtype=f32)
let ones = tpt.ones([3, 224, 224], dtype=f32)
let random = tpt.randn([batch, seq, d])
let arr = tpt.from_list([1.0, 2.0, 3.0, 4.0])
let range = tpt.arange(0, 10)
let eye = tpt.eye(64, dtype=f32)
let reshaped = tpt.reshape(x, [batch, seq * d])
let transposed = tpt.transpose(x, 0, 1)
let sliced = tpt.slice(x, [0..32, ..])
let result = tpt.matmul(a, b)
let activated = tpt.relu(x)
let prob = tpt.softmax(x, dim=-1)
let row = x[0]
let val = x[0, 0]
let slice = x[0..32]
Functions annotated with @requires_gpu(true) are compiled to TPTIR and executed on the GPU:
@doc("Fused multiply-add")
@requires_gpu(true)
@complexity("O(n)")
fn fused_mul_add(
a: Tensor[f32, n],
b: Tensor[f32, n],
c: Tensor[f32, n],
) -> Tensor[f32, n] {
return a * b + c
}
if loss > threshold {
tpt.print("Loss too high!")
} else if loss < 0.01 {
tpt.print("Converged!")
} else {
tpt.print("Training...")
}
for i in 0..10 {
tpt.print(i)
}
for batch in data {
let loss = model.forward(batch)
loss.backward()
model.step()
}
while loss > target_loss {
let batch = data.next()
loss = train_step(model, batch)
}
for i in 0..100 {
if i == 42 { break }
if i % 2 == 0 { continue }
process(i)
}
import tpt
import tpt::nn
import model::transformer as tr
- Install the TPT Script extension
- Features: syntax highlighting, completion, hover, go-to-definition, formatting, diagnostics
- Configure LSP server path in settings:
{
"tptb-lsp.serverPath": "/path/to/tptb-lsp"
}cd v/tpt-vscode
npm install
npm run compile
# Press F5 in VSCode to launch Extension Development Hostcargo run -p tpt-gpu-script-format -- fmt input.tpts
cargo run -p tpt-gpu-script-format -- fmt --in-place input.tptscargo run -p tpt-gpu-script-format -- lint input.tpts| Rule | Severity | Description |
|---|---|---|
missing_doc |
Warning | Function missing @doc |
naming_convention |
Info | Should use snake_case |
line_too_long |
Info | Line exceeds 100 chars |
trailing_whitespace |
Info | Trailing whitespace |
missing_return |
Warning | Missing return statement |
unnecessary_semicolon |
Info | Semicolon after } |
TPT Script provides structured error objects with:
- Error code: Machine-readable (e.g.,
SHAPE_MISMATCH) - Message: Human-readable description
- Location: File, line, and column
- Suggestions: List of possible fixes
- Fix code: Auto-fix suggestion
| Code | Meaning |
|---|---|
SHAPE_MISMATCH |
Tensor dimensions incompatible |
DTYPE_MISMATCH |
Operand dtypes incompatible |
TYPE_ERROR |
Type checking failure |
CONSTRAINT_VIOLATION |
@constraint violated |
UNDEFINED_VARIABLE |
Variable not in scope |
PARSE_ERROR |
Syntax error |
LEX_ERROR |
Tokenization error |
- Always use @doc - Every public function should have documentation
- Use type annotations - Catch errors early with explicit types
- Add constraints - Use
@constraintfor shape validation - Add performance metadata -
@complexity,@memory,@flops - Organize into modules - Keep related code together
- Use no_grad for inference - Avoids unnecessary gradient computation
- Format your code - Use
tptb-formatfor consistent style - Lint early and often - Catch style issues before they become habits
import tpt
@doc("Single transformer attention head")
@requires_gpu(true)
@requires_tensor_cores(true)
@differentiable(true)
@complexity("O(seq^2 * d_k)")
fn attention_head(
q: Tensor[f32, batch, seq, d_k],
k: Tensor[f32, batch, seq, d_k],
v: Tensor[f32, batch, seq, d_v],
) -> Tensor[f32, batch, seq, d_v] {
let scale = tpt.sqrt(tpt.cast(d_k, dtype=f32))
return tpt.attention(q, k, v, 1.0 / scale)
}
import tpt
import tpt.optim
@doc("Train a model for one full epoch")
@requires_gpu(true)
@requires_tensor_cores(true)
@min_vram_gb(16)
@supports_distributed(true)
@max_batch_size(512)
@distributed(strategy="fsdp", devices=8)
fn train_epoch(
model: Model,
data: DataLoader,
lr: f32,
) -> f32 {
let optimizer = tpt.optim.Adam(lr=lr)
let mut total_loss = 0.0
let mut count = 0
for batch in data {
let logits = model.forward(batch.input)
let loss = tpt.cross_entropy(logits, batch.labels)
loss.backward()
optimizer.step(model)
optimizer.zero_grad(model)
total_loss = total_loss + loss
count = count + 1
}
return total_loss / tpt.cast(count, dtype=f32)
}
import tpt
@doc("Run inference on a single input")
@requires_gpu(true)
@deploy(target="cloud", optimize=true)
fn predict(model: Model, x: Tensor[f32, 1, seq]) -> Tensor[i64, 1] {
tpt.no_grad {
let logits = model.forward(x)
return tpt.argmax(logits, dim=-1)
}
}
For the complete language specification, see layer7_tptb/spec/tpts_spec.md.