Sesi Language Specification (v1.5)
1. Philosophy & Design Principles
Sesi is built on these core principles:
- Conciseness and Legibility: The syntax is minimal
- Buildable from Scratch: Sesi is a complete, functioning language with its own lexer, parser, and interpreter.
- Simplicity Enables Power: Because the core language is simple, complex operations (like hitting APIs or orchestrating processes) become trivial extensions of the language, rather than tangled SDK implementations.
- Transparency Over Magic: Sesi executes exactly what you write.
- Practicality: Focus on reducing boilerplate code, emphasizing what developers actually need over academic completeness.
2. Target Users
Primary: Developers who want a clean, fast, and legible language where writing code, whether purely logic-based or calling out to a Reasoning model, is completely frictionless.
Secondary:
- Engineers transitioning from traditional languages (TypeScript, Python, Go)
- Developers looking for minimal boilerplate.
- Teams requiring complex logic with a fraction of the boilerplate
Use Cases:
- Writing clean CLI tools and scripts
- Interacting with APIs without SDK boilerplate
- Quickly orchestrating shell commands
- Rapid prototyping and scripting
3. V1.5 Feature Set (Current)
Core Language Features
- ✅ Variables and bindings (
let) - ✅ Functions (named, anonymous)
- ✅ Conditionals (
if/else) - ✅ Loops (
while,for) - ✅ Error Handling (
try/catchblocks) - ✅ Data types (number, string, bool, array, object)
- ✅ Process Execution (
spawn,exec,time,random,convert,format) - ✅ Comments (
//,/ /) — text is preserved in AST asleadingCommentson declarations for doc tooling - ✅ Operators (arithmetic, logical, comparison)
- ✅ Standard library (print, len, range, etc.)
- ✅ Interactive REPL shell environment (
sesi) - ✅ Diagnostic Tools (
--astand--tokenspretty visualization) - ✅ Statement execution tracing (
SESI_DEBUG=1env variable) - ✅
promptblocks (composable templates for concise formatting) - ✅
structured_output()(schema-guided structured output with JSON recovery and empty-object fallback on failure) - ✅
tool_call()(Fully functional function calling for tool use) - ✅
memory(simple multi-turn script memory and retrieval context management)
Reasoning-Native Features
- ✅
model()calls (native model with configuration) - ✅
image()calls (native image generation with configuration) - ✅
imagesconfig key (multimodal vision input formodel()andimage())
Type System
- ✅ Primitive types:
number,string,bool,null - ✅ Collection types:
array,object - ✅ Type inference
- ✅ Union types:
T | U - ✅ Optional types:
T?
Module System
- ✅
import/export - ✅ Lib support
- ✅ Built-in modules
- ✅ Multi-path module resolution (
SESI_PATH,~/.sesi/libglobal library)
4. Target Language (Syntax)
4.1 Lexical Elements
#### Keywords
let if else while for fn print import export async
prompt model image convert memory structured_output tool_call break continue try catch true false null await
#### Identifiers & Literals
identifier: [a-zA-Z_][a-zA-Z0-9_]*
number: [0-9]+ | [0-9]*\.[0-9]+
string: "..." | '...'
comment: // ... | /* ... */
#### String Escapes & Multiline Rules
- Supported escape sequences:
\\n,\\t,\\r,\\\\,\\",\\' - Unknown escape sequences are runtime errors during lexing with line and column context
- Strings can span multiple lines when a literal newline appears before the closing quote
- Unterminated strings report the starting line and column of the string literal
4.2 Program Structure
program := statement*
statement := declaration | expression_statement | block_statement
4.3 Declarations
#### Variable Declaration
let_stmt := 'let' identifier ('=' expression)? (';' | newline)
Example:
let x = 10
let y = 20
let z // z is null initially
#### Function Declaration
fn_stmt := 'async'? 'fn' identifier '(' parameters ')' '->' type? block
parameters := (identifier ':' type ('=' expr)?)? (',' identifier ':' type ('=' expr)?)*
Example:
fn add(a: number, b: number) {print a + b}
fn greet(name: string = "World") {print "Hello," name}
#### Import/Export
import_stmt := 'import' (identifier | '{' identifiers '}') 'from' string
allow_stmt := 'allow' string 'in' with (identifier | '{' identifiers '}' | string)
export_stmt := 'export' (fn_stmt | let_stmt)
Example:
allow "math" in with {
add, subtract
}
export fn multiply(a, b) {print a * b}
4.4 Control Flow
#### If Statement
if_stmt := 'if' expression block ('else' block)?
#### Loops
while_stmt := 'while' expression block
for_stmt := 'for' identifier 'in' expression block | 'for' identifier '=' expr 'to' expr block
#### Error Handling
try_stmt := 'try' block 'catch' '(' identifier ')' block
#### Loop Control
break_stmt := 'break'
continue_stmt := 'continue'
Example:
for i = 0 to 10 {print i}
try
{let result = "Hello"
} catch (e) {
print e}
4.5 Expressions
#### Literals
literal := number | string | bool | null | array | object
array := '[' (expression (',' expression)*)? ']'
object := '{' (string ':' expression (',' string ':' expression)*)? '}'
#### Operators (Left to Right, Lowest to Highest Precedence)
expr := assignment
assignment := logical_or ('=' assignment)?
logical_or := pipe ('||' pipe)*
pipe := logical_and ('|' logical_and)*
logical_and := equality ('&&' equality)*
equality := comparison (('==' | '!=') comparison)*
comparison := addition (('<' | '>' | '<=' | '>=' | '<>') addition)*
addition := multiplication (('+' | '-') multiplication)*
multiplication := unary (('*' | '/' | '%') unary)*
unary := ('!' | '-' | 'await') unary | postfix
postfix := primary ('['expression']' | '.'identifier | '('args?')' | primary)*
primary := identifier | literal | '('expression')' | prompt | model | image | convert | memory | call
#### Function Call
call := identifier '(' (expression (',' expression)*)? ')'
#### Prompt Block
prompt := 'prompt' identifier '{'content'}'
content := (string | expression | newline)
Example:
prompt codeReview {"Review this code for bugs:" code "Provide specific issues found."}
#### Model & Image Calls
model_call := 'model' '('STRING')' config_block? '{'prompt'}'
image_call := 'image' '('STRING')' config_block? '{'prompt'}'
convert := 'convert' '('(identifier | STRING)')' config_block? '{'file'}'
config_block := '{' config_entry (',' config_entry)* '}'
config_entry := (STRING | identifier) ':' expression
Config keys:
| Key | Applies to | Type | Description | ||
|---|---|---|---|---|---|
thinkingLevel |
model |
string \ |
object |
Recommended: Effort level ("minimal", "low", "medium", "high") |
|
temperature |
model, image |
number |
\Will be deprecated in Gemini 3.x+\_ (Sampling temperature) | ||
max_tokens |
model |
number |
Max output token count | ||
top_k |
model |
number |
\Will be deprecated in Gemini 3.x+\_ | ||
top_p |
model |
number |
\Will be deprecated in Gemini 3.x+\_ | ||
ratio |
image |
string |
Aspect ratio e.g. "16:9" |
||
size |
image |
string |
"512", "1K", "2K", "4K" |
||
images |
model, image |
string \ |
array |
Local file path(s) passed as visual input | |
stream |
model |
bool \ |
fn |
Enable real-time streaming to stdout (true) or a callback function |
Example:
let result = model("gemini-3.5-flash") {images: "scan.png", thinkingLevel: "low"} {"Transcribe all visible text."}
let output = model("gemini-3.5-flash") {thinkingLevel: "medium"} {prompt}
#### Convert Expression
convert := 'convert' '('(identifier | STRING)')' config_block? '{'file'}'
The convert expression transforms documents or media files between different formats (e.g. images, audio, documents).
Config keys:
file_type: The input format extension (e.g."md","csv","png","wav"). If the input is a local file path, this key is optional and can be inferred from the file extension.output_type: The target format extension (e.g."html","json","jpg","mp3"). This key is required.
Example:
let html = convert(doc) {file_type: "md", output_type: "html"} {"# Heading\nHello world"}
let json = convert(doc) {file_type: "csv", output_type: "json"} {"name,age\nAlice,30"}
let converted_file = convert(doc) {file_type: "md", output_type: "html"} {"input.md"}
#### Await Expression
await_expr := 'await' expression
The await expression is used to block execution and resolve the value of a Sesi Promise returned by an asynchronous function.
Example:
async fn getGreeting(name) {
return "Hello, " + name
}
let p = getGreeting("Alice") // returns a Sesi promise
let greeting = await p // blocks and resolves to "Hello, Alice"
#### Structured Output
structured_output := 'structured_output' '('schema')' '('expression')'
schema := '{' (identifier ':' type (',' identifier ':' type)*)? '}'
Example:
let rawJson = "{\"projectName\": \"Sesi\", \"version\": \"1.5.9\", \"status\": \"active\"}"
let parsedRegistry = structured_output({projectName: string, version: string, status: string})(rawJson)
#### Tool Call
tool_call := 'tool_call' '('function_name')' '('(model_call | expressions)?')'
Example (Native Sandboxed Dispatch):
fn add(a: number, b: number) -> number { return a + b }
let sum = tool_call(add)(10, 20)
#### Pipe Operator
The pipe operator | passes the result of the left expression as the first argument to the function call on the right.
pipe_expr := expression '|' function_call_or_identifier
Example:
fn increment(x) { return x + 1 }
fn double(x) { return x * 2 }
let result = 5 | increment | double // evaluates to 12
#### Memory (State Management)
memory := 'memory' identifier ('{'expressions'}')?
Example:
memory conversation {"Previous messages here"}
let response = model("gemini-3-flash-preview") {prompt {conversation "New question:" userInput}}
conversation = conversation "Assistant:" response
4.6 Type Annotations
type := primitive_type | collection_type | union_type | optional_type
primitive_type := 'number' | 'num' | 'string' | 'str' | 'bool' | 'null' | 'any'
collection_type := 'array' '<' type '>' | 'object' '<' type '>'
union_type := type ('|' type)
optional_type := type '?'
Type aliases: num is an alias for number, str is an alias for string, and any accepts any value. These can be used interchangeably in function signatures, variable annotations, and return types.
5. Expression Evaluation Rules
- Short-circuit evaluation:
&&and||short-circuit - Type coercion: Automatic for numeric operations; explicit for string/number
- Null propagation: Operations on
nullreturnnull(no exceptions in v1.x) - Model responses: Always returned as strings initially; structured_output provides type safety
6. Scope and Binding
- Global scope: Module level
- Function scope: Within function definitions
- Block scope: Within blocks (if/while/for)
- Lexical scoping: Inner scopes shadow outer scopes
- Closure support: Functions capture enclosing scope
7. Runtime Semantics
Execution Order
- Tokenize (lexer)
- Parse (parser) → AST
- Evaluate (interpreter)
- Model calls are blocking (no async in v1)
Memory Model
- Stack: Local variables, function parameters
- Heap: Arrays, objects, strings
- Reasoning Context: Implicit conversation history per
memorybinding
Error Handling (V1 Simple)
- Runtime and model errors can be caught with
try/catch - Model errors throw when Gemini returns no text or a non-
STOPfinish reason read_file(),write_file(), andlist_dir()throw on filesystem failurestructured_output()currently logs parsing failures and returns{}if recovery fails
8. Built-in Functions (V1.x)
print(any) // Output to stdout
debug() // Pauses and opens interactive REPL debugger
len(array | string | object) // Length
range(number) -> array // [0, 1, ..., n-1]
type(any) -> string // Type name
str(any) -> string // Convert to string
num(any) -> number // Convert to number
bool(any) -> bool // Convert to bool
keys(object) -> array // Object keys
values(object) -> array // Object values
push(array, any) // Add element
pop(array) -> any // Remove last
join(array, string) -> string // Join with separator
split(string, string) -> array // Split by separator
to_upper(string) -> string // Convert to uppercase
to_lower(string) -> string // Convert to lowercase
trim(string) -> string // Trim whitespace
slice(collection, number, number?) -> collection // Slice string or array
swap(string, string, string) -> string // Replace all target substrings
contains(string, string) -> bool // Check if string contains substring
locate(string, string) -> number // First index of substring, or -1
map(array, fn) -> array // Transform array elements
filter(array, fn) -> array // Filter array elements
reduce(array, fn, any?) -> any // Accumulate array elements
find(array, fn) -> any // Find matching element
retry(fn, number | object) -> any // Execute function with retry and backoff
read_file(string) -> string // Read file contents
write_file(string, string) -> bool // Write file contents
write_image(string, string) -> bool // Write base64 image data to file
list_dir(string) -> array // List directory contents
make_dir(string) -> bool // Create directory (recursive)
spawn(string) -> number // Concurrent process creation
exec(string) -> string // Synchronous shell execution
time() -> number // Current Unix timestamp
random() -> number // Random float (0.0 to 1.0)
convert() -> bool // Convert between formats
Built-in Global Variables
args
9. Module System
Runtime module execution and standard library modules are fully implemented and natively supported in v1.x.
Defining Modules
// math.sesi
export fn add(a, b) {print a + b}
export fn multiply(a, b) {print a * b}
export let PI = 3.14159
Importing Modules
You can import variables and functions from other modules using traditional import or the scoped library allow syntax:
// Option 1: import named bindings directly
import {
add,
multiply,
PI
} from "math"
let result = add(10, 20)
// Option 2: allow module to bind under a scoped library namespace
allow "math" in with Math
let result = Math.add(10, 20)
// Option 3: allow module to bind specific names directly
allow "math" in with {
add, multiply
}
let result = add(10, 20)
Built-in Standard Library Modules
allow "std/time" in with Time // Time/date functions
allow "std/math" in with Math // Math operations
allow "std/json" in with JSON // JSON parsing
Module Resolution Order (v1.x)
When you write allow "mymodule" in with {x} , Sesi searches for mymodule.sesi in the following order, stopping at the first match:
| Priority | Location | Description | |
|---|---|---|---|
| 1 | Script's own directory | Same folder as the currently running .sesi file |
|
| 2 | Current working directory | The directory you ran sesi from |
|
| 3 | SESI_PATH |
Semicolon-separated (Windows) or colon-separated (Unix) list of additional directories | |
| 4 | ~/.sesi/lib |
Global shared library directory — available system-wide |
This means imports always resolve correctly regardless of where you run sesi from.
Global Library: ~/.sesi/lib
The global library directory (C:\Users\ on Windows, ~/.sesi/lib on Unix) lets you maintain shared modules that are importable from any project on your system.
To install a module globally, copy it to the lib directory:
# Windows
copy mymodule.sesi $env:USERPROFILE\.sesi\lib\
# Unix / macOS
cp mymodule.sesi ~/.sesi/lib/
Then import it from any project without copying the file:
// Works from any folder anywhere on your system
allow "mymodule" in with {
function1,
function2,
function3
}
allow "another-module" in with Name
Custom Library Paths: SESI_PATH
For team or monorepo setups, set the SESI_PATH environment variable to point to one or more shared library directories:
# Windows — add to your shell profile for persistence
$env:SESI_PATH = "C:\MyLibs\sesi-shared;C:\Projects\common"
# Unix / macOS
export SESI_PATH="/mylibs/sesi-shared:/projects/common"
Multiple paths are separated by ; on Windows and : on Unix.
Sub-module Resolution
When a module is loaded from any search path, its own imports are resolved relative to that module's directory first. This means modules can safely import their own siblings without any path configuration.
10. Reasoning Features Details
Prompt Blocks
Prompts are composable message templates:
prompt translate {"translate the following to Spanish:" sourceText}
prompt summarize {"Summarize this in 3 sentences:" text}
prompt combined {summarize " Now " translate}
Model & Image Calls
Model calls can take optional configuration parameters (written on a single line) followed by one or more prompts/strings.
// Model call with native thinking effort level
let response = model("gemini-3.5-flash") {thinkingLevel: "low"} {"Say hello"}
print response // Returns string
let logo = image("gemini-3.1-flash-image") {ratio: "1:1", size: "512"} {"A vector logo"}
write_image("logo.png", logo)
print "Image written to logo.png"
#### Config Block Options:
thinkingLevel:string("minimal","low","medium","high") or legacyobjectwith keys"thinking"and"level". Natively configures Gemini's reasoning budget.max_tokens:number(maximum response tokens)images:stringorarray(paths to multimodal vision input files)cache:bool(set tofalseto explicitly bypass Sesi Logic Caching)stream:bool | fn(set totrueto stream tokens to standard output, or pass a Sesi function reference to handle incoming chunks)temperature: Will be deprecated in Gemini 3.x+, use thinkingLevel instead. — reasoning is pre-optimized for defaults.top_k/top_p: Will be deprecated in Gemini 3.x+, use thinkingLevel instead. — reasoning is pre-optimized for defaults.
Reasoning with Structured Output
let result = structured_output({title: string, category: string, confidence: number})(model("gemini-3.1-flash-lite") {"Extract metadata from this text:" text})
print result["title"] // Access fields
print result["confidence"] // Type-safe access
Reasoning with Tool Calling
fn calculateTax(amount: number, rate: number) {print amount * rate}
let taxAmount = tool_call(calculateTax)(model("gemini-3.1-flash-lite") {"Calculate 8% tax on $100"})
taxAmount
Reasoning with Memory
memory chat {"System: You are a helpful assistant."}
fn askQuestion(question: string)
{let response = model("gemini-3-flash-preview") {chat "User:" question}
chat = chat "Assistant:" response}
11. Examples
Example 1: Simple Computation
let x = 10
let y = 20
print x + y // Output: 30
Example 2: Function with Reasoning
fn analyzeText(text: string) -> string {return model("gemini-3.5-flash") {thinkingLevel: "low"} {"Analyze this text and return key insights:" text}}
print analyzeText("Reasoning is transforming industries")
Example 3: Reasoning with Structured Output
let sentiment = structured_output({label: string, score: number})(model("gemini-3-flash-preview") {"Analyze sentiment of:" userInput})
print sentiment.label
print sentiment.score
12. Undefined Behavior & Limitations (V1.x)
- No async/await: All operations within a script are blocking (including model calls). Concurrency must be achieved via
spawn(). - No custom types: Only built-in types are supported natively.
- No pattern matching: Basic if/else only.
- No generics: Array and object collections are untyped at runtime.
- Limited introspection: Basic type() only.
- No macros: No compile-time code generation.
- Single-threaded runtime: Execution per script is single-threaded. System-level concurrency is handled via multi-process
spawn(). - No garbage collection tuning: Rely on Node.js GC.
13. Compatibility Notes
- Sesi programs run on Node.js 18+
- Requires
@google/genaiSDK v2.0.1+ - Requires valid Gemini API key (GEMINI_API_KEY env var)