Back to Home

Language Specification

v0.1.0Last updated: December 2024

Introduction

Overview

Gent is a domain-specific language designed for building AI agents with type-safe outputs, radical transparency, and first-class support for agents, tools, and structured data.

This document is the definitive reference for the Gent programming language. It describes the syntax and semantics of Gent programs in precise detail.

Design Philosophy: What you write is what the LLM sees. Gent provides no hidden transformations—every prompt, every tool call, every output is explicit and inspectable.

Notation

The syntax is specified using a variant of Extended Backus-Naur Form (EBNF):

Production  = production_name "=" Expression .
Expression  = Term { "|" Term } .
Term        = Factor { Factor } .
Factor      = production_name | literal | "[" Expression "]"
            | "(" Expression ")" | "{" Expression "}" .

Productions are expressions constructed from terms and the following operators:

  • | alternation
  • ( ) grouping
  • [ ] option (0 or 1 times)
  • { } repetition (0 to n times)

Source Representation

Source code is Unicode text encoded in UTF-8. Each code point is distinct; for instance, uppercase and lowercase letters are different characters.

character   = /* any Unicode code point */ .
letter      = "A" ... "Z" | "a" ... "z" .
digit       = "0" ... "9" .

Lexical Elements

Comments

Gent supports single-line comments starting with //. Comments extend to the end of the line.

Example
// This is a comment
let x = 42  // inline comment
comment = "//" { character } newline .

Identifiers

Identifiers name program entities such as variables, types, and functions. An identifier is a sequence of letters, digits, and underscores, beginning with a letter.

identifier = letter { letter | digit | "_" } .
Examples
myVariable
_private
camelCase
snake_case
Agent1

Keywords

The following keywords are reserved and cannot be used as identifiers:

agentbreakcatchcontinueelseenumfalsefnforifimportininterfaceletmatchnullparallelreturnstructtooltruetrywhile

Literals

String Literals

String literals are character sequences enclosed in double quotes. They support escape sequences and string interpolation with {expr}.

string_literal     = '"' { string_char | interpolation } '"' .
multiline_string   = '"""' { any_char } '"""' .
interpolation      = "{" expression "}" .
escape_sequence    = "\" ( '"' | "\" | "n" | "r" | "t" | "{" | "}" ) .
Examples
"Hello, World!"
"Value: {x + 1}"
"Line 1\nLine 2"
"""
Multi-line
string literal
"""

Number Literals

number_literal = digit { digit } [ "." digit { digit } ] .
42
3.14159
0.5

Boolean Literals

boolean_literal = "true" | "false" .

Null Literal

The null literal represents the absence of a value.

Array Literals

array_literal = "[" [ expression { "," expression } ] "]" .
[1, 2, 3]
["a", "b", "c"]
[]

Object Literals

object_literal = "{" [ object_field { "," object_field } ] "}" .
object_field   = ( identifier | string_literal ) ":" expression .
{ name: "Alice", age: 30 }
{ "key-with-dashes": value }

Duration Literals

Duration literals specify time spans, used in parallel execution timeouts.

duration_literal = digit { digit } duration_unit .
duration_unit    = "ms" | "s" | "m" .
100ms   // 100 milliseconds
30s     // 30 seconds
5m      // 5 minutes

Types

Primitive Types

Gent provides the following primitive types:

stringUnicode text
number64-bit floating point
booleantrue or false
nullAbsence of value

Composite Types

arrayOrdered collection of values
objectKey-value mapping
anyAny type (escape hatch)

Struct Types

A struct is a named composite type with typed fields. Structs define the schema for agent outputs and tool parameters.

struct_decl    = "struct" identifier [ implements ] "{" struct_body "}" .
implements     = "implements" identifier { "," identifier } .
struct_body    = { struct_field [ "," ] } .
struct_field   = identifier ":" field_type .
field_type     = type_name | type_name "[]" | "{" struct_body "}" .
Example
struct User {
  name: string,
  email: string,
  age: number,
  roles: string[],
  metadata: {
    createdAt: string,
    updatedAt: string
  }
}

Enum Types

Enums define a type with a fixed set of named variants. Variants can optionally carry associated data.

enum_decl    = "enum" identifier "{" enum_body "}" .
enum_body    = { enum_variant [ "," ] } .
enum_variant = identifier [ "(" enum_fields ")" ] .
enum_fields  = enum_field { "," enum_field } .
enum_field   = [ identifier ":" ] identifier .
Example
enum Status {
  Pending,
  Active,
  Completed(result: string),
  Failed(error: string, code: number)
}

Declarations

Variable Declarations

Variables are declared with let and are immutable by default.

let_stmt = "let" identifier "=" expression .
let name = "Alice"
let count = 42
let items = [1, 2, 3]

Function Declarations

Functions are pure computations that take parameters and return a value. They cannot access agent state or call tools.

fn_decl    = "fn" identifier "(" [ param_list ] ")" [ return_type ] block .
param_list = param { "," param } .
param      = identifier ":" type_name .
return_type = "->" type_name .
Example
fn add(a: number, b: number) -> number {
  return a + b
}

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

Tool Declarations

Tools are functions that can be called by agents. They define capabilities that extend what an agent can do—accessing APIs, reading files, performing calculations, etc.

tool_decl = "tool" identifier "(" [ param_list ] ")" [ return_type ] block .
Example
tool fetchWeather(city: string) -> object {
  // Implementation that calls weather API
  let response = http_get("https://api.weather.com/{city}")
  return response.data
}

tool calculateTax(amount: number, rate: number) -> number {
  return amount * rate
}
Tool vs Function: Tools are exposed to agents and appear in the LLM's available actions. Functions are internal helpers not visible to agents.

Agent Declarations

Agents are the core abstraction in Gent. An agent declaration defines an AI agent with a system prompt, available tools, optional knowledge base, and a typed output schema.

agent_decl  = "agent" identifier "{" agent_body "}" .
agent_body  = { agent_item } .
agent_item  = tools_field | knowledge_field | output_field | agent_field .
tools_field = "tools" ":" expression .
knowledge_field = "knowledge" ":" expression .
output_field = "output" ":" output_type .
agent_field = identifier ":" expression .
Example
agent Researcher {
  model: "gpt-4",
  temperature: 0.7,

  system: """
    You are a research assistant. Use the provided tools
    to gather information and synthesize findings.
  """,

  tools: [search, summarize, cite],

  knowledge: ["./docs/*.md"],

  output: ResearchReport
}

Struct Declarations

See Struct Types.

Enum Declarations

See Enum Types.

Interface Declarations

Interfaces define contracts that structs can implement. They specify required fields and methods.

interface_decl   = "interface" identifier "{" interface_body "}" .
interface_body   = { interface_member } .
interface_member = interface_field | interface_method .
interface_field  = identifier ":" type_name .
interface_method = identifier "(" [ param_list ] ")" [ return_type ] .
Example
interface Serializable {
  toString() -> string
}

struct User implements Serializable {
  name: string,
  email: string
}

Expressions

Operators

Arithmetic Operators

+ addition
- subtraction
* multiplication
/ division
% modulo

Comparison Operators

== equal
!= not equal
< less than
> greater than
<= less or equal
>= greater or equal

Logical Operators

&& logical AND
|| logical OR
! logical NOT

Operator Precedence

Operators are listed from highest to lowest precedence:

1( )Grouping
2! -Unary
3* / %Multiplicative
4+ -Additive
5< > <= >=Comparison
6== !=Equality
7&&Logical AND
8||Logical OR

Call Expressions

call_expr = expression "(" [ arg_list ] ")" .
arg_list  = expression { "," expression } .
add(1, 2)
greet("World")
agent.run("What is 2 + 2?")

Member Access

member_expr = expression "." identifier .
index_expr  = expression "[" expression "]" .
user.name
response.data.items
array[0]
map["key"]

Lambda Expressions

Lambdas are anonymous functions, useful for callbacks and higher-order operations like map, filter, and reduce.

lambda      = "(" [ lambda_params ] ")" "=>" lambda_body .
lambda_params = identifier { "," identifier } .
lambda_body = block | expression .
Examples
// Single expression
(x) => x * 2

// Multiple parameters
(a, b) => a + b

// Block body
(x) => {
  let doubled = x * 2
  return doubled + 1
}

// Usage with array methods
let doubled = numbers.map((n) => n * 2)
let evens = numbers.filter((n) => n % 2 == 0)

Match Expressions

Match expressions provide pattern matching on enum values.

match_expr    = "match" expression "{" { match_arm } "}" .
match_arm     = match_pattern "=>" match_body [ "," ] .
match_pattern = "_" | enum_pattern .
enum_pattern  = identifier "." identifier [ "(" pattern_bindings ")" ] .
Example
match status {
  Status.Pending => "Waiting...",
  Status.Active => "In progress",
  Status.Completed(result) => "Done: {result}",
  Status.Failed(error, code) => "Error {code}: {error}",
  _ => "Unknown status"
}

Statements

Blocks

block      = "{" { block_stmt } "}" .
block_stmt = let_stmt | assignment_stmt | return_stmt
           | if_stmt | for_stmt | while_stmt
           | try_stmt | break_stmt | continue_stmt
           | expr_stmt .

If Statements

if_stmt = "if" expression block [ "else" block ] .
if count > 10 {
  print("Many items")
} else {
  print("Few items")
}

For Loops

For loops iterate over arrays and ranges.

for_stmt   = "for" identifier "in" expression block .
range_expr = number ".." number .
for item in items {
  print(item)
}

for i in 0..10 {
  print(i)
}

While Loops

while_stmt = "while" expression block .
while count < 100 {
  count = count + 1
}

Try-Catch Statements

Error handling uses try-catch blocks. The catch clause binds the error to an identifier.

try_stmt = "try" block "catch" identifier block .
try {
  let result = riskyOperation()
  print(result)
} catch error {
  print("Error: {error}")
}

Return Statements

return_stmt = "return" [ expression ] .
return 42
return { name: "result", value: x }
return  // returns null

Agents

Agents are the central abstraction in Gent. They encapsulate an LLM with a system prompt, available tools, optional knowledge, and a typed output schema.

Agent Declaration

An agent declaration creates a new agent type. The agent body contains configuration fields and special fields for tools, knowledge, and output.

Complete Example
agent CodeReviewer {
  // Model configuration
  model: "gpt-4",
  temperature: 0.3,
  max_tokens: 2000,

  // System prompt - what the LLM sees
  system: """
    You are an expert code reviewer. Analyze the provided code
    for bugs, security issues, and style improvements.

    Be thorough but constructive in your feedback.
  """,

  // Available tools
  tools: [analyzeAST, checkStyle, findVulnerabilities],

  // Knowledge base for RAG
  knowledge: ["./style-guide.md", "./security-patterns.md"],

  // Typed output schema
  output: ReviewResult
}

struct ReviewResult {
  summary: string,
  issues: Issue[],
  score: number
}

struct Issue {
  severity: string,
  line: number,
  message: string,
  suggestion: string
}

Tools Field

The tools field specifies which tools are available to the agent. Tools appear in the LLM's function calling interface.

tools: [search, calculate, fetchData]
tools: []  // No tools available

Knowledge Field

The knowledge field enables Retrieval-Augmented Generation (RAG). It accepts an array of file paths or glob patterns. Matched documents are indexed and relevant chunks are injected into the agent's context.

knowledge: ["./docs/api.md"]
knowledge: ["./knowledge/**/*.md", "./examples/*.gnt"]
Auto-RAG: Gent automatically handles document chunking, embedding, and retrieval. No manual configuration required.

Output Field

The output field specifies the agent's return type. The LLM is constrained to produce valid JSON matching this schema. This provides compile-time type safety for agent outputs.

output: UserProfile        // Named struct
output: {                   // Inline struct
  success: boolean,
  message: string
}

Agent Execution

Agents are executed by calling them as functions with a prompt string. The agent runs to completion and returns a typed result.

let result = CodeReviewer("Review this Python code: {code}")

// Parallel execution
parallel ReviewTeam {
  agents: [SecurityReviewer, StyleReviewer, PerfReviewer],
  timeout: 30s
}

Modules

Import Statements

Import statements bring declarations from other Gent files into scope.

import_stmt = "import" "{" import_list "}" "from" string_literal .
import_list = identifier { "," identifier } .
import { UserAgent, fetchUser } from "./agents/user.gnt"
import { validateEmail } from "./utils/validation.gnt"

Exports

All top-level declarations (agents, tools, functions, structs, enums) are automatically exported and can be imported by other modules.