dist_agent_lang Syntax Reference

DAL (dist_agent_lang) is a networking-oriented language that is built with Rust: services, agents, messages, and spawn are first-class; syntax uses fn, let, blocks, :: namespacing, and type annotations in a Rust-friendly way. This document reflects the parser and AST as implemented and how DAL connects to libraries and src/: standard library calls use namespace::function(args), implemented in src/stdlib/ and dispatched in src/runtime/engine.rs. See Testing Quick Reference and Attributes Reference for patterns and pitfalls.

Table of Contents


Basic Syntax

Program Structure

A DAL program is a sequence of top-level statements; there is no single “main” entry point. The language is built around networking and Rust-like structure:

Statements and Semicolons

let x = 10;
print("Hello");
return result;

Data Types

Primitive Types

Collection and Generic Types

Type annotations use identifiers or type keywords with optional generic parameters:

Examples:

let numbers: list<int> = [1, 2, 3];
let items: vector<string> = ["a", "b"];
let user: map<string, any> = { name: "Alice", age: 30 };

Variables

Variable Declaration

let name = "Alice";
let age: int = 30;
let is_active: bool = true;

The parser allows an optional mut after let (it is consumed but not reflected in the AST).

Variable Assignment

Only simple assignment is supported: =. Compound assignment operators (+=, -=, *=, /=, etc.) are not parsed as assignments.

let x = 10;
x = 20;

Service Fields

Fields are declared with name, type, optional initial value, and a semicolon. Visibility modifiers (e.g. public / private) are not parsed; all fields are treated as public.

service MyService {
    count: int = 0;
    name: string = "default";

    fn increment() {
        self.count = self.count + 1;
    }
}

Service Instantiation

Syntax 1 – type name as namespace:

let instance = MyService::new();
instance.increment();

Syntax 2 – service namespace:

let instance = service::new("MyService");
instance.increment();

Both create a new instance of the service.

Service Method Calls and Field Access

Within service methods, use self.field to read and assign fields. Method calls use instance.method(args) or namespace::function(args).

service TokenContract {
    balances: map<string, int> = {};

    fn initialize(owner: string) {
        self.balances[owner] = 1000;
    }

    fn transfer(to: string, amount: int) {
        self.balances[to] = self.balances[to] + amount;
    }
}

let token = TokenContract::new();
token.initialize("0x123...");
token.transfer("0x456...", 100);

Indexed Access and Assignment

Map and array indexing use expr[index]. Assignment to an index is expr[index] = value (parsed as a special assignment form).

self.balances[address] = amount;
return self.balances[address];
self.items[0] = item;

Functions

Function Declaration

fn function_name(param1: type, param2: type) -> return_type {
    return value;
}

Function Examples

fn greet(name: string) -> string {
    return "Hello, " + name;
}

fn print_message(msg: string) {
    print(msg);
}

fn get_current_time() -> int {
    return 1234567890;
}

Async Functions

async fn is supported at the top level or inside a service. The parser accepts async fn name(...) [ -> type ] { body }. Attributes (e.g. @async) may appear before fn or async fn when allowed by the parser.

async fn fetch_data(url: string) -> string {
    return await http::get(url);
}

Services

Service Declaration

Services can be preceded by attributes (e.g. @trust, @chain, @secure, @compile_target). See Attributes Reference.

@trust("hybrid")
@chain("ethereum")
service MyService {
    balance: int = 0;

    fn deposit(amount: int) {
        self.balance = self.balance + amount;
    }

    fn get_balance() -> int {
        return self.balance;
    }
}

Service Members

Field visibility is not parsed; all fields are effectively public.


Agents

Agents can be created in two ways: language syntax (spawn / agent) and stdlib API (ai::create_agent).

Create agent (stdlib API)

The ai::create_agent(config) function creates an agent instance at runtime. config is a literal (e.g. role, capabilities). This is the usual pattern in examples and scripts.

let agent_config = {
    role: "assistant",
    capabilities: ["analysis", "reasoning"]
};
let agent_instance = ai::create_agent(agent_config);

For coordinators: ai::create_agent_coordinator().

Spawn Statement

spawn as a statement has one of these forms:

  1. spawn agent_name { body } – body block is required.
  2. spawn agent_name : type { body } – optional type (e.g. ai, worker).
  3. spawn agent_name : type { config } { body } – config is a struct literal; body required.

Config follows literal rules: keys are identifiers or string literals, colon, then expression.

spawn agent_name:ai {
    role: "assistant",
    capabilities: ["analysis", "reasoning"]
} {
    log::info("agent", "Agent started");
}

Spawn Expression

spawn can also be used as an expression: spawn expr. For example, spawning the result of a call:

let handle = spawn worker_process(i);

The parser treats spawn in expression context as a unary operator: spawn followed by an expression (e.g. a function call).

Agent Declaration

agent name : type { config } [ with capabilities ] { body } defines an agent type (name, type, config, and body). It does not create a running instance.

agent MyAgent:ai {
    role: "worker",
    capabilities: ["processing"]
} {
    fn process() {
        // Agent logic
    }
}

Message Statement

msg recipient { data } ;

msg agent_name {
    type: "task",
    data: { key: "value" }
};

Event Statement

event event_name { data } ;

event task_completed {
    result: "success",
    count: 42
};

Control Flow

If Statements

Parentheses around the condition are required. Use if (condition) { ... }, not if condition.

if (x > 10) {
    print("Large");
} else if (x > 0) {
    print("Positive");
} else {
    print("Small");
}

For Loops

Only for-in is supported: for variable in iterable { body }. variable can be an identifier or keyword used as identifier; iterable is an expression.

for item in [1, 2, 3] {
    print(item);
}

While Loops

while (condition) { body }. Parentheses around the condition are required.

while (count > 0) {
    count = count - 1;
    process(count);
}

Try-Catch-Finally

try {
    risky_operation();
} catch (error_type error_var) {
    log::info("main", error_var);
} finally {
    cleanup();
}

Break Statement

break [ expression ] ;

Exit the innermost loop (while, for, or loop). Optionally returns a value from the loop.

let count = 0;
while (count < 10) {
    count = count + 1;
    if (count == 5) {
        break;  // Exit loop
    }
}

// Break with value
let result = 0;
loop {
    result = result + 1;
    if (result == 42) {
        break result;  // Exit loop and return value
    }
}

Continue Statement

continue;

Skip to the next iteration of the innermost loop (while, for, or loop).

let sum = 0;
for item in [1, 2, 3, 4, 5] {
    if (item % 2 == 0) {
        continue;  // Skip even numbers
    }
    sum = sum + item;
}

Loop Statement

loop { body }

Infinite loop that can be exited with break. Includes timeout protection to prevent infinite execution.

let count = 0;
loop {
    count = count + 1;
    if (count == 10) {
        break;
    }
}

Match Statement

match expression { case1 => body1, case2 => body2, default => body }

Pattern matching expression. Matches the expression against patterns and executes the first matching case.

Patterns:

// Literal patterns
let status = "success";
match status {
    "success" => 1,
    "error" => 0,
    default => -1
}

// Identifier pattern (binds value)
let value = 42;
match value {
    x => x * 2  // x is bound to 42, returns 84
}

// Range pattern
let score = 85;
match score {
    90..100 => "A",
    80..89 => "B",
    70..79 => "C",
    default => "F"
}

// Wildcard pattern
match value {
    _ => "matched"
}

Notes:

Return Statement

return [ expression ] ;

fn calculate() -> int {
    return 42;
}

if (condition) {
    return;
}

Operators

Arithmetic

let sum = 10 + 5;
let diff = 10 - 5;
let prod = 10 * 5;
let quot = 10 / 5;
let rem = 10 % 3;

Comparison

if (x == 10) { }
if (x != 10) { }
if (x < 10) { }
if (x <= 10) { }
if (x > 10) { }
if (x >= 10) { }

Logical

if (x > 0 && x < 10) { }
if (x < 0 || x > 10) { }
if (!is_empty) { }

Assignment

let x = 10;
x = 20;

Expressions

Literals

42
3.14
"Hello"
true
false
null

Array Literals

[ expr1, expr2, ... ]

[1, 2, 3]
["a", "b", "c"]
[]

Literals

{ key: value, ... }

DAL uses key-value pairs inside { } for literals: agent/spawn config, message/event data, and any expression. The form is always key : value (colon between key and value).

Is the colon required? Yes. The parser requires a colon after each key; without it you get an error (except in one narrow compatibility case when the value starts with this).

Context Key allowed Colon Example
Literal (expression) Identifier or string literal Required { name: "Alice", age: 30 } or { "name": "Alice" }
msg / event data Identifier only Required msg x { type: "task", id: 1 };
{ key: "value" }
{ name: "Alice", age: 30 }
{}

Function Calls

print("Hello");
chain::deploy(1, "Contract", {});
instance.method(arg1, arg2);

Inline Closures (Call-Argument Only)

Inside a function argument list, the parser accepts a single-parameter closure with block body:

( param => { body } )

handler.process(items, r => { return r.success; });

Closures are only recognized as the last (or only) argument in a (...) list, immediately after a single identifier and =>.

Field and Index Access

self.field
value.field
map["key"]
arr[i]

Unary and Binary Operations

!condition
-amount
await async_call()
spawn worker(i)
throw error_expr

Libraries and Standard Namespaces

DAL code calls into the standard library using the namespace::function(args) syntax. The runtime resolves these calls in src/runtime/engine.rs (see call_namespace_function) and delegates to the corresponding module under src/stdlib/. Any DAL-specific syntax you use for libraries is this call form plus the argument shapes each namespace expects.

Namespace → implementation mapping

Namespace Implementation Typical DAL usage
service src/stdlib/service.rs service::new("ServiceName") – create instance by name
ai src/stdlib/ai.rs ai::create_agent(config), ai::create_agent_coordinator()
agent src/stdlib/agent.rs agent::create_agent_message(...), agent::create_agent_task(...)
log src/stdlib/log.rs log::info("tag", message), log::audit("tag", message)
auth src/stdlib/auth.rs auth::session(user_id, roles) – returns a session value; use its fields (e.g. user_id) as in API Reference
chain src/stdlib/chain.rs chain::deploy(chain_id, contract_name, {}), chain::estimate_gas(...)
oracle src/stdlib/oracle.rs oracle::fetch(...), oracle::create_query(...)
crypto src/stdlib/crypto.rs crypto::hash(...), crypto::sign(...), crypto::verify(...)
database src/stdlib/database.rs DB helpers (see API Reference)
web src/stdlib/web.rs Web/server helpers
sync, key, kyc, aml, admin, cloudadmin, config, trust, mobile, desktop, iot src/stdlib/<name>.rs Same pattern: namespace::function(args)

DAL-specific library syntax

For full signatures and behavior, see API Reference. For where each namespace is wired and executed, see src/runtime/engine.rs (e.g. call_ai_function, call_log_function, call_auth_function, call_chain_function) and the corresponding src/stdlib/*.rs modules.


Comments

// This is a comment
let x = 10; // Inline comment

/* Multi-line
   comment */

Keywords

Declaration and Structure

Control Flow

Async

Types and Modifiers

Attributes and Targets


Cross-Reference