Skip to main content
Version: 1.0.0

Syntax Reference

Transform lists and compose text​

ExpressionResult
[x * 2 for x in values if x > 0]A new list containing doubled positive values
concat(left, right)A new list, left items followed by right items
join(labels, "; ")Text assembled from a List of Text

The collection is evaluated once. Each item's filter runs before its transformation. The named item is local to the comprehension; input lists are preserved. Element types must be compatible. Empty lists receive their type from context, for example bind labels as List of Text = [].

Comprehensions respect the host iteration limit, including filtered items, and cancellation and duration checks. See the pure functions tutorial.

This page is a practical reference for the visible Touba syntax.

Files and indentation​

.tb files accept both LF (Linux/macOS) and CRLF (Windows) line endings. Indent each level with four spaces, without tabs. Diagnostics refer to the lines and positions in the original file; no manual conversion to LF is needed. A lone carriage return (CR without LF) is not a line ending. Use # for a line comment, including after an instruction.

Compilation Front End​

Tree-sitter support is used for editor tooling and CST construction. The Touba compiler rejects ERROR and MISSING recovery nodes before AST lowering. A file that Tree-sitter can partially display in an editor is therefore not accepted as a Touba program when the grammar had to recover from an error.

Text, types and binding scope​

A Text literal is double-quoted on one source line. JSON escapes are decoded exactly once: \n (newline), \r, \t, \b, \f, \", \\, \/ and \uXXXX (Unicode, including valid surrogate pairs). "a\n\nb" contains two newlines; "\\n" retains a backslash followed by n. Invalid escapes and raw control characters are refused. Multiline source literals remain unsupported; no implicit interpolation or JavaScript evaluation.

Int / Int returns Float. Int // Int returns an Int truncated toward zero: (0 - 7) // 2 is -3. A zero divisor is refused. Currency formatting and locale conventions belong in a package.

Templates are explicit: f"Case {reference}, quantity {text(quantity)}". Every expression inside braces must produce Text. Use {{ and }} for literal braces. Ordinary JSON text escapes still apply; expressions are pure and no host code is evaluated.

A binding belongs to its block. Sibling branches may reuse a name; shadowing a visible name or using a name outside its scope is rejected. if, for and while can nest in functions, skills and strategies. List items retain their type, including List of List of Int.

Module​

source.tb
module examples.name

In a Touba project, the recommended path mirrors the logical module name:

Structure
src/examples/name.tb

Run limits​

module examples.bounded

limits run
max_run_ms as 30000
max_steps as 100
max_tool_calls as 20
max_agent_invocations as 5
max_provider_calls as 10

A single limits run block, after imports and before declarations, bounds the entire run. Each value is a positive Int literal no greater than 9007199254740991. The compiler retains its source location in AIR; no manual JSON editing is needed.

An authorized capability may be reused while authority and budget allow it. Subagents share the run counters; a human-pause resume preserves prior consumption. Linking combines the limits of the entry module and modules whose executable declarations are linked, taking the minimum. Importing only a type, constant or memory does not transfer its module's run limits.

Imports​

The documented imports cover two families:

  • pure imports (IM-1): type, non-sensitive define, and pure function;
  • effectful imports (IM-2): local tool, skill, strategy, agent, and memory from the same project.
source.tb
module examples.search

import type SearchRequest from examples.common
import define DefaultRows from examples.common
import function NormalizeQuery from examples.common
import tool SearchDatasets from examples.data_tools
import skill BuildBrief from examples.shared_flow
import strategy SharedRun from examples.shared_flow
import agent Reviewer from examples.agent_pool
import memory ProjectKnowledge from examples.shared_memory

Imports must appear after module and before other declarations.

Scope

Effectful imports (IM-2) make local effectful components available for composition, but they grant no execution authority. The manifest, grants, secrets, handlers, memory scopes, callable_by, constraints, supply-chain lock, and runtime refusals remain explicit.

Exports from an installed package use the same import syntax. Aliases, wildcard imports, and re-exports remain excluded. The compiler resolves these declarations during project AIR assembly.

Complete modules required by the import example

Save these files alongside the importing module. Imports resolve source contracts, not runtime permissions.

src/examples/common.tb
module examples.common

type SearchRequest
query is Text
rows is Int

define DefaultRows as Int = 5

function NormalizeQuery
takes query as Text
gives Text
do
give query
src/examples/data_tools.tb
module examples.data_tools

import type SearchRequest from examples.common

tool SearchDatasets
description "Find public datasets matching the request."
takes request as SearchRequest
gives Text
requires network
src/examples/shared_flow.tb
module examples.shared_flow

skill BuildBrief
description "Return a typed brief supplied by the caller."
takes brief as Text
gives Text
do
give brief

strategy SharedRun
takes brief as Text
gives Text
uses BuildBrief
do
BuildBrief
with brief as brief
into result
give result
src/examples/agent_pool.tb
module examples.agent_pool

agent Reviewer
responsibility "Review the supplied brief for completeness using only authorized capabilities."
callable_by strategy examples.shared_flow.SharedRun
src/examples/shared_memory.tb
module examples.shared_memory

memory ProjectKnowledge

Types​

Int represents integers exactly from −9,007,199,254,740,991 to 9,007,199,254,740,991. Inputs and results outside that range are refused. Text length counts Unicode code points: 😀 counts as 1, precomposed é as 1, and e followed by a combining accent as 2. Visible graphemes and UTF-8 bytes use different measures.

source.tb
type Request
query is Text
rows is Int

Primary builtins: Text, Boolean, Int, Float, List of <Type>.

Business enumerations​

An enumeration declares a closed set of values. Each declaration is a distinct nominal type: Priority.high is not a Text.

priority.tb
module priority

enum Priority
low
normal
high

define DefaultPriority as Priority = Priority.normal

function IsUrgent
takes priority as Priority
gives Boolean
do
give priority == Priority.high

Equality operators == and != require the same nominal type. Ordered comparisons and implicit conversion from text are rejected. Records, lists and signatures may use this type. Import it with import type Priority from priority.

At JSON boundaries (handlers, host inputs and agent responses), the value is the exact member name, such as "high", checked against the expected type. Unknown values are rejected, never replaced by a default member. Use a define constant for a static trigger argument. Variants have no associated payload. match value followed by indented case Enum.member blocks selects one exhaustive branch: every member must occur exactly once, with no default case. See Enumerations.

Explicit optional values​

Optional of T distinguishes absence from an empty value. some(value) constructs presence; none(T) constructs typed absence.

optional_contact.tb
module optional_contact

function ContactLabel
takes contact as Optional of Text
gives Text
do
if present contact as address
give address
else
give "No contact"

function EmptyContact
gives Optional of Text
do
give none(Text)

function KnownContact
takes address as Text
gives Optional of Text
do
give some(address)

address exists only inside the if present branch. Direct access through contact.value, use as Text, and comparison of optional envelopes are rejected: handle presence, then compare the extracted value. Presence guards compose with else if and loops. List of Optional of Contact and Optional of List of Contact express different, supported types.

At JSON boundaries use {"present":false} or {"present":true,"value":"person@example.test"}, without extra fields. null is not Touba absence; an Optional field remains required in a record. {"present":true,"value":""} is a present, empty text. Handlers and providers must preserve the same envelope. some and none are expressions, not module define constants.

Module Constants​

define names an immutable, non-sensitive module value.

source.tb
define Name as Type = literal
source.tb
define DefaultTopic as Text = "transport"
define ScoreLimit as Float = 0.75
define MaxRows as Int = 10
define ReviewsEnabled as Boolean = true

A constant can be used by functions, skills, or strategies in the same module.

Scope
  • Only Text, Boolean, Int, Float literals and qualified enum members are accepted
  • No composed expression in define
  • No secret, environment variable, or local path in define

Function​

A function computes a pure value.

source.tb
function AverageScore
takes score as Float
takes bonus as Float
gives Float
do
give (score + bonus) / 2.0

Immutable local value inside a block:

source.tb
define ScoreLimit as Float = 0.75

function ReviewScore
takes score as Float
takes bonus as Float
gives Text
do
bind adjusted as Float = (score + bonus) / 2.0
bind accepted as Boolean = adjusted >= ScoreLimit
if accepted
give "accepted"
else
give "review"

bind names a local calculation. It does not allow the value to be modified afterward.

Expressions:

  • Arithmetic: +, -, *, /, //, %
  • Booleans: and, or, not
  • Comparisons: ==, !=, <, >, <=, >=
  • Text concatenation: ++
  • Explicit text rendering: text(expr) for Text, Int, Float, and Boolean
  • Field access: value.field

A nominal record is constructed with record TypeName { field as expression, ... } on one source line. Every field is supplied once or has a declared default. copy original { field as expression } creates a new record with explicit replacements. The original stays unchanged; the runtime validates the complete result.

Date is an ISO civil date (YYYY-MM-DD); date("2026-10-21") constructs it and text(date_value) renders it. Dates support comparison. [a, b] constructs a list; an empty list needs an expected type, for example bind items as List of Text = [].

Record fields may declare format "digits" for ASCII digits, length 3 or length 1..40 for Unicode code points or list items, and inclusive numeric min/max bounds. default accepts scalar literals, date("…"), none(Type) and []. Defaults apply to source construction; handlers and models provide complete records. See Typed data.

Tool​

A tool declares an external effect. The actual implementation lives in a host handler.

source.tb
tool SearchDatasets
description "Search approved dataset sources for records matching the request."
takes request as DatasetSearchRequest
gives DatasetSearchResult
requires network

Supported clauses: requires <capability>, secrets HANDLE, uses memory <Name> read, uses memory <Name> write.

Skill​

A skill orchestrates supported functions, tools, skills, interactions, and human validations.

source.tb
skill Search
description "Run the governed dataset search workflow and return normalized results."
takes request as DatasetSearchRequest
gives DatasetSearchResult
uses SearchDatasets
do
SearchDatasets
with request as request
into result
give result

A fallback declares the single failure family it catches:

source.tb
tool Primary
gives Text

skill Backup
gives Text
do
give "backup"

skill Run
gives Text
uses Primary
uses Backup
do
Primary
give "primary"
on failure handler
fallback Backup

Catchable families are business, authority, provider and handler. Cancellation and human stops are never caught.

Composable conditions and iterations​

else if selects the first matching case; the final else is optional. If no case matches, execution continues after the chain. Each for visits its list in order; an empty list skips its body. Inner loops start over on each new entry.

Selection and iteration
module examples.control

function FirstPositive
takes rows as List of List of Int
gives Int
do
for row in rows
for item in row
if item > 0
give item
give 0

function Category
takes value as Int
gives Text
do
if value < 0
give "negative"
else if value == 0
give "zero"
else
give "positive"

give exits the invocation, including from an inner loop. Values remain immutable; there is no assignment, break or continue. while reevaluates its condition and remains bounded: an unchanged condition can reach the limit. Literal while true and while false are rejected at compilation.

The host's max_loop_iterations limit (100 by default) applies per loop activation. Exiting after exactly that many iterations is allowed; starting another iteration is refused. Nesting bypasses neither run-wide budgets nor call authority. A human approval inside a loop requires a new decision for each occurrence.

Agent​

An agent exposes a bounded capability. callable_by declares authorized callers.

source.tb
agent Researcher
description "Research agent that selects an authorized dataset-search capability."
takes request as DatasetSearchRequest
gives DatasetSearchResult
responsibility "Select the declared research capability that matches the request."
uses Search
callable_by strategy RunSearch

Every capability exposed to a decision provider through agent.uses must declare an explicit source description. The runtime refuses before the provider call when that signal is missing.

Typed calls to imported agents use the same named with inputs and into result binding as local calls. The compiler must receive the source modules needed to resolve their signatures. Declaring type identities and callable_by checks are preserved. An authorized capability may be invoked repeatedly within the applicable budgets; the caller does not acquire the specialist’s powers.

The explicit inter-module form is accepted for imported agents:

source.tb
agent Reviewer
responsibility "Fulfill the declared Reviewer responsibility using only authorized capabilities."
callable_by strategy examples.agent_app.RunReview

Typed agent invocation uses the same explicit bindings:

source.tb
Researcher
with request as request
into result

Every declared input requires with, and into receives the final value checked against gives. Interfaces are not inferred from uses. Imported agents with explicit signatures use the same bindings once their source modules are resolved; agents without signatures accept only simple calls.

Strategy​

A strategy is a runtime-executable orchestration entry point.

source.tb
strategy RunSearch
uses Researcher
do
Researcher

A local or imported strategy with gives can also be called from a skill or strategy using with / into. Every parameter is explicit. Local values are isolated; budgets and cancellation stay shared across the run. A pure function cannot call a strategy. A bounded revision can pass typed state into another invocation without implicit shared memory.

Trigger​

A trigger declares a governed event entry point.

source.tb
trigger DailyResearchSchedule
on schedule "every 1s"
runs strategy RunResearch
limits
max_runs_per_day as 1
timeout_ms as 30000

trigger WebhookResearch
on request POST "/webhook/research"
runs strategy RunResearch
limits
max_runs_per_day as 10
timeout_ms as 30000

Touba accepts schedule -> strategy and request -> strategy. The schedule runner supports every <n>s, every <n>m, and every <n>h. The webhook service routes request entries by GET, POST, PUT, PATCH, or DELETE method, absolute path, manifest payload contract, and TD-1 state. Other event families are outside this syntax.

User Interactions​

show and ask are interaction nodes, not functions.

source.tb
show
with message as "age is: " ++ text(age)

ask
with prompt as "Search query?"
into query

Human Validation​

source.tb
human.approve
with context as change
into approval

Logical Memories​

A memory is a logical resource. Actual access goes through a tool, a manifest, and a handler.

source.tb
memory ProjectKnowledge

tool ReadProjectKnowledge
uses memory ProjectKnowledge read
takes query as Query
gives KnowledgeResult
requires memory_read

Visible Syntax Boundaries​

info
  • No inline handler code in .tb
  • No handled_by
  • Enriched agent calls use with / into with an explicit resolved signature, local or imported. No contract is inferred from uses.
  • No host method call such as title.toLowerCase()
  • No stdin read
  • No show or ask in function
  • No uses clause in a pure function; call pure functions directly.
  • on failure belongs to a skill, not a strategy.
  • callable_by belongs to an agent, not a skill.
  • No reassignment or mutable variable
  • No module constant based on a call or composed expression
  • Package imports target declared exports; aliases, wildcard imports, and re-exports are excluded.
  • No runs agent in a trigger

Float uses binary floating-point arithmetic. text() preserves its numeric value and may expose decimal noise such as 0.7000000000000001; it does not silently round. Use Int minor units for exact fixed-scale amounts, and perform display formatting explicitly in the host application.

Structured parallelism​

A skill or strategy distributes a typed list to a declared callable:

parallel Diagnose
each incident in incidents
with request as incident
max_concurrency 4
max_items 32
into diagnoses

Place this fragment inside do with declared variables and target. Agents, strategies, skills, functions and tools with explicit output R are callable. The result is List of R, in input order. Both positive integer bounds are required, with concurrency no greater than cardinality. All arguments are checked before admission. No partial result is published. Existing authority rules apply; pure functions cannot contain this orchestration. See the multi-agent tutorial.