KAI is a distributed object model for C++ with full runtime reflection, incremental garbage collection, and a multi-language execution environment. Three languages — Pi, Rho, and Tau — share a common lexer/parser/executor pipeline, all running on a stack-based virtual machine.
graph TB
subgraph Languages
RHO[Rho<br/>infix, Python-like]
PI[Pi<br/>stack-based, Forth-like]
TAU[Tau<br/>IDL, code generation]
end
subgraph Translation
RHOLEX[RhoLexer]
RHOPAR[RhoParser]
RHOTRANS[RhoTranslator]
PILEX[PiLexer]
PIPAR[PiParser]
end
subgraph Execution
EXEC[Executor<br/>stack VM]
CONT[Continuation<br/>code sequence]
DATA[Data Stack]
CTX[Context Stack]
end
subgraph Core
REG[Registry<br/>type factory + GC]
OBJ[Object<br/>managed reference]
BS[BinaryStream<br/>serialization]
end
subgraph Network
NODE[Node<br/>network endpoint]
AGENT[Agent<br/>server-side handler]
PROXY[Proxy<br/>client-side stub]
DOMAIN[Domain<br/>node grouping]
ENET[ENet<br/>UDP transport]
end
RHO --> RHOLEX --> RHOPAR --> RHOTRANS --> PIPAR
TAU --> PIPAR
PI --> PILEX --> PIPAR
PIPAR --> CONT --> EXEC
EXEC --> DATA
EXEC --> CTX
EXEC --> REG
REG --> OBJ
OBJ --> BS
BS --> NODE
NODE --> ENET
NODE --> AGENT
NODE --> PROXY
DOMAIN --> NODE
PROXY --> AGENT
The Registry is the central factory and garbage collector. Every value in KAI is wrapped in an Object — a managed, typed reference. Types are registered at startup:
reg.AddClass<int>();
reg.AddClass<String>();
MyType::Register(reg);No macros are required. The reflection system uses template specialisation and the Traits<T> machinery.
Rather than a traditional call stack, KAI uses Continuations — first-class objects that represent a sequence of operations. This enables:
- Suspension and resumption of execution mid-sequence
- Tail-call replacement (
!in Pi) - Coroutine-like patterns
- The
for/while/ifcontrol structures are all implemented as continuations
sequenceDiagram
participant Executor
participant Continuation
participant DataStack
Executor->>Continuation: fetch next op
Continuation-->>Executor: Operation
Executor->>DataStack: push/pop values
Executor->>Continuation: Suspend (& op)
Executor->>Continuation: Resume (... op)
Executor->>Continuation: Replace (! op)
Rho code is compiled to Pi, which is then executed directly:
Rho source
→ RhoLexer (tokens)
→ RhoParser (AST)
→ RhoTranslator (Pi operations)
→ Pi Continuation
→ Executor
Tau IDL is compiled offline to C++ proxy/agent headers — it does not run through the executor.
Pi is an assembly-like stack language. All other languages ultimately emit Pi operations. It uses two stacks:
| Stack | Purpose |
|---|---|
| Data stack | Values and intermediate results |
| Context stack | Continuations, scopes, control flow |
Key operations: dup, drop, swap, over, @ (dereference), ! (replace/store), & (suspend), ... (resume).
Rho provides infix, Python-like syntax that transpiles to Pi. The translator generates Pi operations — it does not evaluate at translation time. This separation is critical: the executor owns all evaluation.
fun factorial(n) {
if (n <= 1) { return 1 }
else { return n * factorial(n - 1) }
}
Tau is a declarative IDL for defining network interfaces. NetworkGenerate reads .tau files and emits C++ headers:
namespace Calc {
interface ICalc {
Future<int> Add(int a, int b);
int Value;
}
}
Generates ICalcProxy (for callers) and ICalcAgent (for implementors). Template return types like Future<int> are handled by the lexer as a single composite token.
graph LR
subgraph "Domain A (server)"
NA[Node A] --> AG[Agent<br/>registers methods<br/>and properties]
AG --> IMPL[Servant<br/>C++ object]
end
subgraph "Domain B (client)"
NB[Node B] --> PR[Proxy<br/>forwards calls]
PR --> FUT[Future<T><br/>async result]
end
NA <-->|ENet UDP| NB
PR -->|RPC request| AG
AG -->|response| FUT
- Agent registers on a
Nodeand exposes methods/properties viaRegisterMethod/RegisterProperty - Proxy holds a
NetHandlepointing to a remote agent and forwards typed calls viaInvoke<R>(...)orFetchProperty<T>(...) - Domain is a thin wrapper that groups a
Nodewith factory methodsMakeAgent<T>()/MakeProxy<T>() Future<T>is returned by all remote calls;Node::WaitFor(future)blocks until resolved
| ID | Decimal | Purpose |
|---|---|---|
ID_KAI_FUNCTION_CALL |
129 | Client → Agent: invoke method |
ID_KAI_FUNCTION_RESPONSE |
131 | Agent → Client: return value |
ID_KAI_EVENT |
130 | Agent → all: broadcast event |
ID_KAI_OBJECT |
128 | Node → all: broadcast KAI object |
ID_KAI_PROPERTY_GET |
132 | Client → Agent: read property |
ID_KAI_PROPERTY_SET |
133 | Client → Agent: write property |
| Pattern | Where used |
|---|---|
| Registry / Factory | Registry::New<T>() for all object creation |
| Visitor | AST traversal in all language translators |
| Observer | SubscribeEvent, SetConnectionEventCallback |
| Command | Operations are first-class Object values |
| Proxy | ProxyBase / Proxy<T> for remote calls |
| Template Method | GenerateProcess base drives GenerateProxy / GenerateAgent |
- Separation of concerns: Lexing → Parsing → Translation → Execution are fully decoupled
- Extensibility: New languages plug in by implementing the
LexerCommon/ParserCommoninterfaces - Uniform object model: Every value is an
Object; the executor never handles raw C++ types - First-class continuations:
Suspend/Resume/Replaceenable powerful control-flow primitives without special VM support - Zero-macro reflection: Types are registered via template specialisation, no source modifications needed