Documentation
¶
Overview ¶
ast.go - Duso Abstract Syntax Tree node definitions
This file defines the data structures that represent a parsed Duso program. The parser converts tokens into an AST using these node types.
CORE LANGUAGE COMPONENT: This is part of the minimal core language. The AST is the intermediate representation between source code and evaluation.
Node types include: - Program: Root node containing all statements - Statements: if/elseif/else, while/for loops, function definitions, assignments - Expressions: Binary/unary operations, function calls, literals, variables - Values: Numbers, strings, booleans, arrays, objects, nil - Functions: Function definitions and calls (both user and built-in)
The AST structure enables: - Proper error reporting (statements have source locations) - Correct evaluation order and precedence - Support for all language constructs
environment.go - Duso variable scoping system
This file implements the lexical scoping and variable lookup system for Duso. An Environment is a single scope level, with optional parent scopes forming a scope chain.
CORE LANGUAGE COMPONENT: This is part of the minimal core runtime. Scope management is essential for: - Local variables in functions - Nested scopes (if blocks, function bodies, loops) - Variable shadowing (redefining in inner scopes) - Function closure support
The scoping model is simple and dynamically typed: - Variables are stored as Value structs - Each environment has an optional parent environment - Variable lookup walks up the scope chain - Function scopes prevent assignments from walking to parent (local declarations) - The "self" value provides context for method calls
STORAGE: most scopes (function frames, loop bodies) hold only a handful of variables, so the first smallScopeSize variables live in inline arrays on the struct — creating and using such a scope costs a single allocation and lookups are a short linear scan (with Go's pointer-equality fast path for the shared AST identifier strings). Scopes that outgrow the inline slots spill to the overflow map.
CONCURRENCY INVARIANT: Environments are deliberately unsynchronized. An env tree is only ever touched by one goroutine, with one exception: parallel() branches read parent scopes concurrently. That is safe because parallel() blocks the parent goroutine on wg.Wait() (no writer exists while readers run), and branch writes stop at the branch's own function env via isParallelContext. Anything that would share an env tree across goroutines in a new way must revisit this.
lexer.go - Duso language tokenizer
This file implements the lexer (scanner/tokenizer) that converts source code strings into a stream of tokens. It is the first stage of compilation, before parsing.
CORE LANGUAGE COMPONENT: This is part of the minimal core language. It is required for all script execution, both in embedded applications and the CLI.
The lexer handles: - Character-by-character reading from source code - Token identification (keywords, operators, literals, identifiers) - Line and column tracking for error reporting - String and number literal parsing - Comment handling
registry.go - Global builtin function registry
This file maintains the global registry of builtin functions. The registry is populated once at startup by the host (runtime package or CLI). Each evaluator gets a copy of the map for lock-free function lookups.
resolver.go - Compile-time slot resolution for function parameters
After parsing, resolveProgram walks the AST and annotates Identifier nodes that provably refer to a parameter of the enclosing function with a slot index. Parameters occupy the first inline storage slots of the function environment in declaration order (both call paths guarantee this), so an annotated identifier reads e.env.fnScope.vals[slot] directly instead of walking the scope chain with string compares.
CORE LANGUAGE COMPONENT: This is part of the minimal core runtime.
The resolver is deliberately conservative: it annotates a use only when the dynamic path would provably produce the same result, because local lookup wins over self properties and parent scopes for both reads and writes (see Environment.Get/Set). Anything ambiguous stays un-annotated and takes the existing name-based path. Punt rules:
- only parameters are slotted; they are bound before the body runs no matter how they were supplied (positional, named, or default)
- a parameter shadowed anywhere in the function body — by a var declaration, a for-loop variable, a catch variable, or a nested function definition name — is not slotted at all
- identifiers inside named-argument expressions are never slotted: object constructors evaluate them in a temp scope where earlier named args are visible (see callObject)
- parameter default expressions are never slotted (evaluated in the closure environment, not the function environment)
- nested function bodies get their own scope; outer parameters are not slottable inside them (closure capture stays name-based)
- a parameter named "self" is never slotted (Get special-cases the name)
- only the first smallScopeSize parameters get slots (inline storage)
value.go - Duso runtime type system
This file defines the core value types and runtime representation for all Duso data. Every value computed during script execution is represented as a Value struct.
CORE LANGUAGE COMPONENT: This is part of the minimal core runtime. All values in Duso scripts map to one of these types: - NIL: Absence of a value (uninitialized variables) - NUMBER: Floating-point numbers (no integer type) - STRING: Text values - BOOL: True/false - ARRAY: Ordered lists of values (indexed by numbers) - OBJECT: Maps/tables (key-value pairs with string keys) - FUNCTION: Callable functions (either Go or Duso functions)
This type system is simple and dynamically typed to match Duso's design goal of being easy to embed and learn.
Index ¶
- Variables
- func ArgKey(i int) string
- func ClearRequestContext(gid uint64)
- func CopyBuiltins() map[string]GoFunction
- func CopyFastBuiltins() map[string]GoFunctionFast
- func DecodeValue(buf []byte) (any, []byte, error)
- func DeepCopyAny(val any) any
- func EncodeValue(buf []byte, v any) ([]byte, error)
- func FormatErrorWithStack(err *DusoError) string
- func GetAllBuiltinNames() []string
- func GetGoroutineID() uint64
- func GetKeywords() map[string]TokenType
- func IsReservedName(name string) bool
- func RegisterBuiltin(name string, fn GoFunction)
- func RegisterBuiltinFast(name string, fn GoFunctionFast)
- func ResolveScriptPath(requestedPath, callingScriptFilename string) string
- func ResolveScriptPathFromDir(requestedPath, scriptDir string) string
- func SetDatastoreQueueAppender(appender DatastoreQueueAppender)
- func SetRequestContextWithData(gid uint64, ctx *RequestContext, spawnedData any)
- func UnescapeString(s string) string
- func ValueForDisplay(val Value) string
- func ValueSize(v any) int64
- func ValueToDusoString(val Value) string
- func ValueToInterface(v Value) any
- type ArrayLiteral
- type AssignStatement
- type BinaryExpr
- type BinaryValue
- type BoolLiteral
- type BracketInfo
- type BreakIteration
- type BreakStatement
- type BreakpointError
- type CallExpr
- type CallFrame
- type CircularDetector
- type CodeValue
- type CompoundAssignStatement
- type ComputedKeyPair
- type ContinueIteration
- type ContinueStatement
- type DatastoreQueueAppender
- type DebugEvent
- type DebugHandler
- type DebugManager
- type DusoError
- type ElseifClause
- type Environment
- func NewChildEnvironment(parent *Environment) *Environment
- func NewChildEnvironmentWithSelf(parent *Environment, self Value) *Environment
- func NewEnvironment() *Environment
- func NewFunctionEnvironment(parent *Environment) *Environment
- func NewFunctionEnvironmentWithSelf(parent *Environment, self Value) *Environment
- func (e *Environment) Define(name string, value Value)
- func (e *Environment) Get(name string) (Value, error)
- func (e *Environment) IsParameter(name string) bool
- func (e *Environment) MarkParameter(name string)
- func (e *Environment) Set(name string, value Value) error
- func (e *Environment) SetLocal(name string, value Value) error
- func (e *Environment) SetParallelContext(isParallel bool)
- type ErrorValue
- type Evaluator
- func (e *Evaluator) CallFunction(fn Value, args map[string]Value) (Value, error)
- func (e *Evaluator) Eval(node Node) (Value, error)
- func (e *Evaluator) EvalModule(prog *Program) (Value, error)
- func (e *Evaluator) EvalTemplateLiteral(template string) (string, error)
- func (e *Evaluator) EvaluateTemplate(templateStr string, bindings map[string]Value) (string, error)
- func (e *Evaluator) GetContext() *ExecContext
- func (e *Evaluator) GetEnv() *Environment
- func (e *Evaluator) GetEnvironment() *Environment
- func (e *Evaluator) GetGoFunctions() map[string]GoFunction
- func (e *Evaluator) GetWatchCache() map[string]Value
- func (e *Evaluator) IsParallelContext() bool
- func (e *Evaluator) ParseExpression(exprStr string) (Node, error)
- func (e *Evaluator) RegisterFunction(name string, fn GoFunction)
- func (e *Evaluator) RegisterObject(name string, methods map[string]GoFunction)
- func (e *Evaluator) ReqCtx() *RequestContext
- func (e *Evaluator) SetEnvironment(env *Environment)
- func (e *Evaluator) SetExecutionFilePath(filePath string)
- func (e *Evaluator) SetParallelContext(isParallel bool)
- func (e *Evaluator) SetReqCtx(rc *RequestContext)
- type ExecContext
- type ExitExecution
- type ForStatement
- type FunctionCaller
- type FunctionDef
- type FunctionExpr
- type GoFunction
- type GoFunctionFast
- type IOConfig
- type Identifier
- type IfStatement
- type IndexExpr
- type Interpreter
- func (i *Interpreter) AppendToIOQueue(eventType string, data any, pid int) error
- func (i *Interpreter) CacheProgram(key string, program *Program)
- func (i *Interpreter) EvalInContext(source string) (string, error)
- func (i *Interpreter) EvalInEnvironment(source string, env *Environment) (string, error)
- func (i *Interpreter) EvalProgram(program *Program) (Value, error)
- func (i *Interpreter) Execute(source string) (string, error)
- func (i *Interpreter) ExecuteFile(path string) (string, error)
- func (i *Interpreter) ExecuteModule(source string) (Value, error)
- func (i *Interpreter) ExecuteModuleProgram(program *Program) (Value, error)
- func (i *Interpreter) ExecuteNode(node Node) error
- func (i *Interpreter) GetCallStack() []CallFrame
- func (i *Interpreter) GetDebugEventChan() chan *DebugEvent
- func (i *Interpreter) GetDebugHandler() DebugHandler
- func (i *Interpreter) GetDebugSessionMutex() *sync.Mutex
- func (i *Interpreter) GetEvaluator() *Evaluator
- func (i *Interpreter) GetFilePath() string
- func (i *Interpreter) GetModuleCache(path string) (Value, int64, bool)
- func (i *Interpreter) GetScriptDir() string
- func (i *Interpreter) ParseScript(path string) (*Program, error)
- func (i *Interpreter) ParseScriptFile(path string, readFile func(string) ([]byte, error), ...) (*Program, error)
- func (i *Interpreter) QueueDebugEvent(event *DebugEvent)
- func (i *Interpreter) RegisterDebugHandler(handler DebugHandler)
- func (i *Interpreter) RegisterFunction(name string, fn GoFunction) error
- func (i *Interpreter) RegisterObject(name string, methods map[string]GoFunction) error
- func (i *Interpreter) Reset()
- func (i *Interpreter) SetFilePath(path string)
- func (i *Interpreter) SetModuleCache(path string, value Value, mtime int64)
- func (i *Interpreter) SetScriptDir(dir string)
- type InvocationFrame
- type Lexer
- type LintAnalyzer
- type LintDiagnostic
- type LintScope
- type ModuleCacheEntry
- type NilLiteral
- type Node
- type NumberLiteral
- type ObjectLiteral
- type Parameter
- type ParseCacheEntry
- type Parser
- type Position
- type PostIncrementStatement
- type Program
- type PropertyAccess
- type RegexLiteral
- type RegexValue
- type RequestContext
- type ReturnStatement
- type ReturnValue
- type ScriptExecutionResult
- type ScriptFunction
- type StringLiteral
- type SymbolInfo
- type TemplateLiteral
- type TernaryExpr
- type TextPart
- type Token
- type TokenType
- type TryStatement
- type UnaryExpr
- type Value
- func DeepCopy(v Value) Value
- func InterfaceToValue(i any) Value
- func NewArray(elements []Value) Value
- func NewBinary(data []byte) Value
- func NewBool(b bool) Value
- func NewCode(src string, prog *Program, meta map[string]Value) Value
- func NewErrorValue(msg Value, stack string) Value
- func NewFunction(fn *ScriptFunction) Value
- func NewGoFunction(fn GoFunction) Value
- func NewNil() Value
- func NewNumber(n float64) Value
- func NewObject(obj map[string]Value) Value
- func NewRegex(pattern string, compiled *regexp.Regexp) Value
- func NewString(s string) Value
- func (v Value) AsArray() []Value
- func (v Value) AsArrayPtr() *[]Value
- func (v Value) AsBinary() *BinaryValue
- func (v Value) AsBool() bool
- func (v Value) AsCode() *CodeValue
- func (v Value) AsErrorVal() *ErrorValue
- func (v Value) AsNumber() float64
- func (v Value) AsObject() map[string]Value
- func (v Value) AsRegex() *RegexValue
- func (v Value) AsString() string
- func (v Value) IsArray() bool
- func (v Value) IsBinary() bool
- func (v Value) IsBool() bool
- func (v Value) IsCode() bool
- func (v Value) IsError() bool
- func (v Value) IsFunction() bool
- func (v Value) IsNil() bool
- func (v Value) IsNumber() bool
- func (v Value) IsObject() bool
- func (v Value) IsRegex() bool
- func (v Value) IsString() bool
- func (v Value) IsTruthy() bool
- func (v Value) String() string
- type ValueRef
- type ValueType
- type WhileStatement
Constants ¶
This section is empty.
Variables ¶
var NoPos = Position{Line: 0, Column: 0}
NoPos represents an unknown or invalid position
Functions ¶
func ClearRequestContext ¶
func ClearRequestContext(gid uint64)
ClearRequestContext removes a request context from goroutine-local storage
func CopyBuiltins ¶
func CopyBuiltins() map[string]GoFunction
CopyBuiltins returns a copy of the global builtin registry. Called once per evaluator so it can have lock-free lookups.
func CopyFastBuiltins ¶
func CopyFastBuiltins() map[string]GoFunctionFast
CopyFastBuiltins returns a copy of the fast builtin registry.
func DecodeValue ¶
DecodeValue decodes one value from buf, returning it with the remaining bytes.
func DeepCopyAny ¶
DeepCopyAny performs deep copy on any type (for scope boundaries)
func EncodeValue ¶
EncodeValue appends the binary encoding of v to buf and returns the extended buffer. v is an any-tree of the kind a datastore holds.
Functions cannot be encoded and are elided rather than raising an error, matching the deep_copy() builtin exactly: a function becomes nil, and a function stored as an object value has its key dropped entirely.
func FormatErrorWithStack ¶
FormatErrorWithStack formats a DusoError with full stack trace for I/O queuing. This matches the format used by DusoError.Error() but is reusable.
func GetAllBuiltinNames ¶
func GetAllBuiltinNames() []string
GetAllBuiltinNames returns a sorted list of all registered builtin function names.
func GetGoroutineID ¶
func GetGoroutineID() uint64
GetGoroutineID extracts the current goroutine ID from the stack trace
func GetKeywords ¶
GetKeywords returns the keywords map for introspection (e.g., for syntax generation)
func IsReservedName ¶
IsReservedName checks if a name conflicts with keywords
func RegisterBuiltin ¶
func RegisterBuiltin(name string, fn GoFunction)
RegisterBuiltin registers a builtin function in the global registry. This is called by the host (runtime package or CLI) during initialization.
func RegisterBuiltinFast ¶
func RegisterBuiltinFast(name string, fn GoFunctionFast)
RegisterBuiltinFast registers a fast-path variant for an existing builtin.
func ResolveScriptPath ¶
ResolveScriptPath resolves a script path relative to a calling script's directory. If the path is absolute or special (/EMBED/, /STORE/), returns it unchanged. If the path is relative, resolves it relative to the calling script's directory. Example: ResolveScriptPath("./worker.du", "/path/to/bees/bees.du")
returns "/path/to/bees/worker.du"
func ResolveScriptPathFromDir ¶
ResolveScriptPathFromDir resolves a script path relative to a given directory. If the path is absolute or special (/EMBED/, /STORE/), returns it unchanged. If the path is relative, resolves it relative to the given directory. Example: ResolveScriptPathFromDir("./worker.du", "/path/to/bees")
returns "/path/to/bees/worker.du"
func SetDatastoreQueueAppender ¶
func SetDatastoreQueueAppender(appender DatastoreQueueAppender)
SetDatastoreQueueAppender sets the global datastore queue appender callback. Called by runtime.RegisterBuiltins() to wire up I/O routing.
func SetRequestContextWithData ¶
func SetRequestContextWithData(gid uint64, ctx *RequestContext, spawnedData any)
SetRequestContextWithData stores a request context with optional spawned context data
func UnescapeString ¶
UnescapeString processes escape sequences in a string, preserving UTF-8 Uses rune-based iteration to handle multi-byte characters correctly
func ValueForDisplay ¶
ValueForDisplay converts a value to a display string for print/output. Strings are printed as-is without quotes (for readability). Other types use Duso syntax so arrays/objects display correctly.
func ValueSize ¶
ValueSize reports the approximate encoded size of a value in bytes, matching what EncodeValue would produce closely enough to enforce a limit against.
It exists so a size cap can be checked without encoding first: encoding a 500MB value to discover it is too big has already done the damage. The walk is O(n) in the value, but every datastore write already deep-copies the value, so this adds a second pass over data that was being traversed anyway — and for the case that actually matters, a large binary, it is O(1).
func ValueToDusoString ¶
ValueToDusoString converts any Value to a Duso-parsable string representation. This is used for tostring(), templates, and any place Duso syntax is needed. The result is valid Duso syntax that can be parsed back with parse().
func ValueToInterface ¶
ValueToInterface converts a Value to interface{} for Go interop. This is used to convert script values to Go types for external functions. For arrays, returns *[]Value directly to allow in-place mutations by builtins.
Types ¶
type ArrayLiteral ¶
type ArrayLiteral struct {
Elements []Node
}
type AssignStatement ¶
type BinaryValue ¶
type BinaryValue struct {
Data *[]byte // Pointer to immutable binary data
Metadata map[string]Value // filename, content_type, size, etc.
}
BinaryValue represents immutable binary data (e.g., files, images)
type BoolLiteral ¶
type BoolLiteral struct {
Value bool
}
type BracketInfo ¶
type BracketInfo struct {
// contains filtered or unexported fields
}
BracketInfo tracks opening brackets for better error messages
type BreakIteration ¶
type BreakIteration struct{}
BreakIteration is used to signal a break from a loop (for future use)
func (*BreakIteration) Error ¶
func (e *BreakIteration) Error() string
type BreakStatement ¶
type BreakStatement struct {
Pos Position
}
type BreakpointError ¶
type BreakpointError struct {
FilePath string
Position Position
CallStack []CallFrame
Env *Environment // Current environment at breakpoint for scope access
Message string // Optional message from breakpoint()/watch() call
}
BreakpointError signals debug breakpoint hit and captures call stack for display
func (*BreakpointError) Error ¶
func (e *BreakpointError) Error() string
type CircularDetector ¶
type CircularDetector struct {
// contains filtered or unexported fields
}
CircularDetector tracks module loading stack to detect circular dependencies
func (*CircularDetector) Pop ¶
func (c *CircularDetector) Pop()
Pop removes the most recent path from the detector's loading stack
func (*CircularDetector) Push ¶
func (c *CircularDetector) Push(path string) error
Push adds a path to the detector's loading stack
type CodeValue ¶
type CodeValue struct {
Source string
Program *Program // parsed AST, immutable
Metadata map[string]Value // optional user metadata from parse(src, meta)
}
CodeValue represents pre-parsed code (source + AST + optional metadata)
type CompoundAssignStatement ¶
type ComputedKeyPair ¶
type ContinueIteration ¶
type ContinueIteration struct{}
ContinueIteration is used to signal a continue in a loop (for future use)
func (*ContinueIteration) Error ¶
func (e *ContinueIteration) Error() string
type ContinueStatement ¶
type ContinueStatement struct {
Pos Position
}
type DatastoreQueueAppender ¶
DatastoreQueueAppender is a callback for appending to an I/O queue in a datastore. Set by the runtime package during initialization to enable I/O routing. Parameters: datastore name, queue key, event type ("out", "err", "exit"), data value, PID
type DebugEvent ¶
type DebugEvent struct {
Error error // The error that occurred (BreakpointError or runtime error)
Message string // Error message (for runtime errors without DusoError wrapper)
FilePath string // File where error occurred
Position Position // Position in file
CallStack []CallFrame // Script call stack at error point
InvocationStack *InvocationFrame // Chain of script invocations that led here
Env *Environment // Environment at time of error
ResumeChan chan bool // Signal to resume execution after REPL
}
DebugEvent represents a debug event (breakpoint or error) that occurred in a child script It's queued for the main process to handle via REPL
type DebugHandler ¶
type DebugHandler func(*DebugEvent)
DebugHandler is a callback function that handles debug events (breakpoints, errors). It receives the debug event and is responsible for: - Displaying the event to the user (via chosen I/O mechanism) - Opening a debug session (REPL, HTTP interface, etc.) - Sending a resume signal when the user is done debugging
type DebugManager ¶
type DebugManager struct {
// contains filtered or unexported fields
}
DebugManager handles debug events sequentially. Scripts call Wait() synchronously and block until the user responds. The manager processes each event from its queue one-by-one, opening the REPL and waiting for user input before resuming the caller.
When -stdin-port is used, the debug REPL's stdin/stdout automatically goes through the HTTP transport (no special HTTP debug server needed).
func GetDebugManager ¶
func GetDebugManager() *DebugManager
GetDebugManager returns the global debug manager instance
func (*DebugManager) Wait ¶
func (dm *DebugManager) Wait(event *DebugEvent, interpreter *Interpreter)
Wait blocks until the user responds to the debug event. This is called synchronously by ExecuteScript when a breakpoint is hit.
type DusoError ¶
type DusoError struct {
Message any // The error message/thrown value (any type, NOT deep copied at throw time)
FilePath string
Position Position
CallStack []CallFrame
}
DusoError represents an error with position information and call stack
type ElseifClause ¶
type Environment ¶
type Environment struct {
// contains filtered or unexported fields
}
Environment represents a scope for variables
func NewChildEnvironment ¶
func NewChildEnvironment(parent *Environment) *Environment
NewChildEnvironment creates a child environment with a parent scope
func NewChildEnvironmentWithSelf ¶
func NewChildEnvironmentWithSelf(parent *Environment, self Value) *Environment
NewChildEnvironmentWithSelf creates a child environment with a parent scope and self
func NewEnvironment ¶
func NewEnvironment() *Environment
NewEnvironment creates a new root environment
func NewFunctionEnvironment ¶
func NewFunctionEnvironment(parent *Environment) *Environment
NewFunctionEnvironment creates a function scope that blocks variable assignment walk-up
func NewFunctionEnvironmentWithSelf ¶
func NewFunctionEnvironmentWithSelf(parent *Environment, self Value) *Environment
NewFunctionEnvironmentWithSelf creates a function scope with self binding
func (*Environment) Define ¶
func (e *Environment) Define(name string, value Value)
Define creates a new variable in the current scope
func (*Environment) Get ¶
func (e *Environment) Get(name string) (Value, error)
Get retrieves a variable, walking up the parent chain if necessary
func (*Environment) IsParameter ¶
func (e *Environment) IsParameter(name string) bool
IsParameter checks if a name is a function parameter Fast path for common names (bit test), slow path for uncommon (map lookup)
func (*Environment) MarkParameter ¶
func (e *Environment) MarkParameter(name string)
MarkParameter marks a name as a function parameter (can't be shadowed with var) Uses bit flags for common names, map for uncommon (memory optimization)
func (*Environment) Set ¶
func (e *Environment) Set(name string, value Value) error
Set updates a variable, checking self properties first, then walking up the parent chain Parallel context blocks assignment walk-up to parent: parent scope becomes read-only
func (*Environment) SetLocal ¶
func (e *Environment) SetLocal(name string, value Value) error
SetLocal updates a variable only in the current scope
func (*Environment) SetParallelContext ¶
func (e *Environment) SetParallelContext(isParallel bool)
SetParallelContext marks this environment as part of a parallel() block When true, assignments don't walk up to parent scope (parent scope is read-only)
type ErrorValue ¶
type ErrorValue struct {
Message Value // the value passed to throw(), or runtime error message string
Stack string // formatted string: file:line:col + call stack
}
ErrorValue represents a first-class error value (message + stack trace string)
type Evaluator ¶
type Evaluator struct {
// contains filtered or unexported fields
}
func (*Evaluator) CallFunction ¶
CallFunction calls a Duso function with the given arguments This delegates to callScriptFunction or callGoFunction based on the function type
func (*Evaluator) EvalModule ¶
EvalModule evaluates a program in an isolated module scope and returns the result. This is used by require() to load modules in isolation - the module's variables don't leak into the caller's scope. The last expression value becomes the module's export.
func (*Evaluator) EvalTemplateLiteral ¶
EvalTemplateLiteral evaluates a template string with embedded expressions
func (*Evaluator) EvaluateTemplate ¶
EvaluateTemplate evaluates a template string with provided variable bindings. The template string can contain {{ }} expressions that are evaluated in the context of the provided bindings. Returns the evaluated template as a string.
Example:
bindings := map[string]Value{
"name": NewString("World"),
"count": NewNumber(42),
}
result, err := evaluator.EvaluateTemplate("Hello {{name}}, count: {{count}}", bindings)
// result = "Hello World, count: 42"
func (*Evaluator) GetContext ¶
func (e *Evaluator) GetContext() *ExecContext
GetContext returns the execution context (FilePath, CallStack, Position info)
func (*Evaluator) GetEnv ¶
func (e *Evaluator) GetEnv() *Environment
GetEnv returns the current environment for variable inspection
func (*Evaluator) GetEnvironment ¶
func (e *Evaluator) GetEnvironment() *Environment
GetEnvironment returns the current evaluation environment
func (*Evaluator) GetGoFunctions ¶
func (e *Evaluator) GetGoFunctions() map[string]GoFunction
GetGoFunctions returns a copy of the registered Go functions
func (*Evaluator) GetWatchCache ¶
GetWatchCache returns the watch cache map for debug watch() expressions
func (*Evaluator) IsParallelContext ¶
IsParallelContext returns true if executing in a parallel() block
func (*Evaluator) ParseExpression ¶
ParseExpression parses a string expression into an AST node
func (*Evaluator) RegisterFunction ¶
func (e *Evaluator) RegisterFunction(name string, fn GoFunction)
RegisterFunction registers a Go function
func (*Evaluator) RegisterObject ¶
func (e *Evaluator) RegisterObject(name string, methods map[string]GoFunction)
RegisterObject registers an object with methods
func (*Evaluator) ReqCtx ¶
func (e *Evaluator) ReqCtx() *RequestContext
ReqCtx returns the request context attached to this evaluator, or nil.
func (*Evaluator) SetEnvironment ¶
func (e *Evaluator) SetEnvironment(env *Environment)
SetEnvironment sets the evaluation environment
func (*Evaluator) SetExecutionFilePath ¶
SetExecutionFilePath sets the FilePath in the execution context for error reporting
func (*Evaluator) SetParallelContext ¶
SetParallelContext sets whether the evaluator is executing in a parallel() block
func (*Evaluator) SetReqCtx ¶
func (e *Evaluator) SetReqCtx(rc *RequestContext)
SetReqCtx attaches the per-execution request context to this evaluator. Evaluators are per-execution (HTTP handler, spawn, run), so this is safe to read without locking from builtins invoked during that execution.
type ExecContext ¶
ExecContext tracks execution state including file path and call stack
func NewExecContext ¶
func NewExecContext(filePath string) *ExecContext
NewExecContext creates a new execution context with the given file path
func (*ExecContext) Depth ¶
func (ctx *ExecContext) Depth() int
Depth returns the current call stack depth
func (*ExecContext) PopCall ¶
func (ctx *ExecContext) PopCall()
PopCall removes the last function call from the stack
func (*ExecContext) PushCall ¶
func (ctx *ExecContext) PushCall(name, file string, pos Position)
PushCall adds a function call to the stack
type ExitExecution ¶
type ExitExecution struct {
Values []any
}
ExitExecution is used to signal exit() with optional return values
func (*ExitExecution) Error ¶
func (e *ExitExecution) Error() string
type ForStatement ¶
type FunctionCaller ¶
type FunctionCaller interface {
// CallFunction calls a Duso function with the given arguments
CallFunction(fn Value, args map[string]Value) (Value, error)
// EvalTemplateLiteral evaluates a template string and returns the result
EvalTemplateLiteral(template string) (string, error)
// GetEnvironment returns the current evaluation environment
GetEnvironment() *Environment
// IsParallelContext returns true if executing in a parallel() block
IsParallelContext() bool
}
FunctionCaller is an interface for invoking Duso functions and accessing evaluation context. This interface decouples Builtins from directly depending on Evaluator, allowing callback-based builtins (map, filter, reduce, etc.) to work without circular dependencies.
type FunctionDef ¶
type FunctionExpr ¶
type GoFunction ¶
func GetBuiltin ¶
func GetBuiltin(name string) GoFunction
GetBuiltin retrieves a single builtin function by name, or nil if not found.
type GoFunctionFast ¶
GoFunctionFast is the fast-path builtin signature: evaluated positional args in, Value out, no interface{} marshalling. See RegisterBuiltinFast.
type IOConfig ¶
type IOConfig struct {
Datastore string // Name of the datastore to use
Queue string // Key in the datastore where I/O events are appended
Out bool // Route print() output to the queue
Err bool // Route error() and runtime errors to the queue
Exit bool // Route exit code to the queue
PID int // Process ID (set by spawn/run, used in queue entries)
}
IOConfig specifies where a spawned/run process should route its I/O When set, print/error/exit output goes to a datastore queue instead of stdout/stderr
type Identifier ¶
type IfStatement ¶
type IfStatement struct {
Pos Position
Condition Node
Then []Node
Elseifs []*ElseifClause
Else []Node
}
type Interpreter ¶
type Interpreter struct {
// I/O routing configuration (optional, set at spawn/run time)
IOConfig *IOConfig // If set, print/error/exit route to datastore instead of default handlers
// Host-provided capabilities (for builtins that need host services)
ScriptLoader func(path string) ([]byte, error) // Loads scripts for spawn/run (required for those builtins)
FileReader func(path string) ([]byte, error) // Reads files for load/readfile (required for those builtins)
FileWriter func(path, content string) error // Writes files for save/writefile (required for those builtins)
FileStatter func(path string) int64 // Gets file modification time for caching (used by http_server)
DirReader func(path string) ([]map[string]any, error) // Lists directory contents, supports /EMBED/ and /STORE/ (used by http_server)
OutputWriter func(msg string) error // Outputs messages for print/error/debug (required for those builtins)
InputReader func(prompt string) (string, error) // Reads input from user (required for input() builtin)
EnvReader func(varname string) string // Reads environment variables (used by env() builtin)
// contains filtered or unexported fields
}
Interpreter is the public API for executing Duso scripts.
CORE INTERPRETER - This is suitable for both embedded Go applications and CLI usage. It uses only the core language runtime with no external dependencies.
To extend with CLI features (file I/O, module loading), see pkg/cli/register.go
func GetExecutionInterpreter ¶
func GetExecutionInterpreter(gid uint64) *Interpreter
GetExecutionInterpreter retrieves the interpreter for the current execution Returns nil if no RequestContext is available
func NewInterpreter ¶
func NewInterpreter() *Interpreter
NewInterpreter creates a new interpreter instance.
This creates a minimal interpreter with only the core Duso language features. Use this in embedded Go applications, then optionally register custom functions with RegisterFunction() or CLI features with pkg/cli.RegisterFunctions().
func (*Interpreter) AppendToIOQueue ¶
func (i *Interpreter) AppendToIOQueue(eventType string, data any, pid int) error
AppendToIOQueue appends an I/O event to the configured datastore queue. eventType should be one of: "out", "err", "exit" The entry includes the PID for sorting/filtering in shared queues. This is called by the I/O handler functions to route output to a datastore. Returns nil if no IOConfig is set (I/O routing not enabled).
func (*Interpreter) CacheProgram ¶
func (i *Interpreter) CacheProgram(key string, program *Program)
CacheProgram stores a pre-parsed program in the cache with the given key. Used by http_server to cache inline code handlers from parse().
func (*Interpreter) EvalInContext ¶
func (i *Interpreter) EvalInContext(source string) (string, error)
EvalInContext evaluates code in the current evaluator context. Used by the debug REPL to maintain variable scope and evaluator state. Unlike Execute(), this preserves all evaluator state without reinitializing.
func (*Interpreter) EvalInEnvironment ¶
func (i *Interpreter) EvalInEnvironment(source string, env *Environment) (string, error)
EvalInEnvironment evaluates code in a specific environment context. This is used by the debug REPL to evaluate expressions in the scope where the breakpoint occurred.
func (*Interpreter) EvalProgram ¶
func (i *Interpreter) EvalProgram(program *Program) (Value, error)
EvalProgram evaluates a pre-parsed program in the current scope. This is used by include() when the AST is already cached. Unlike ExecuteModuleProgram, this executes in the current environment so variables and functions are available after execution.
func (*Interpreter) Execute ¶
func (i *Interpreter) Execute(source string) (string, error)
Execute executes script source code
func (*Interpreter) ExecuteFile ¶
func (i *Interpreter) ExecuteFile(path string) (string, error)
ExecuteFile executes a script file
func (*Interpreter) ExecuteModule ¶
func (i *Interpreter) ExecuteModule(source string) (Value, error)
ExecuteModule executes script source in an isolated module scope and returns the result value. This is used by require() to load modules in isolation. The module's variables don't leak into the caller's scope. The last expression value (or explicit return) is the export.
func (*Interpreter) ExecuteModuleProgram ¶
func (i *Interpreter) ExecuteModuleProgram(program *Program) (Value, error)
ExecuteModuleProgram executes a pre-parsed program in an isolated module scope. This is used by require() when the AST is already cached. The module's variables don't leak into the caller's scope. The last expression value (or explicit return) is the export.
Creates a fresh evaluator for each module to ensure per-execution isolation (no sharing of evaluator state across concurrent requests).
func (*Interpreter) ExecuteNode ¶
func (i *Interpreter) ExecuteNode(node Node) error
ExecuteNode executes a single AST node. Used by debugger for statement-by-statement execution. Maintains evaluator state between calls.
func (*Interpreter) GetCallStack ¶
func (i *Interpreter) GetCallStack() []CallFrame
GetCallStack returns the current call stack for debugging
func (*Interpreter) GetDebugEventChan ¶
func (i *Interpreter) GetDebugEventChan() chan *DebugEvent
GetDebugEventChan returns the channel for receiving debug events from child scripts
func (*Interpreter) GetDebugHandler ¶
func (i *Interpreter) GetDebugHandler() DebugHandler
GetDebugHandler retrieves the currently registered debug handler.
func (*Interpreter) GetDebugSessionMutex ¶
func (i *Interpreter) GetDebugSessionMutex() *sync.Mutex
GetDebugSessionMutex returns the mutex that serializes debug REPL sessions. Only one debug REPL should be active at a time to prevent multiple readers on stdin.
func (*Interpreter) GetEvaluator ¶
func (i *Interpreter) GetEvaluator() *Evaluator
GetEvaluator returns the internal evaluator instance (for advanced use). This is primarily used by CLI functions that need access to registered Go functions.
func (*Interpreter) GetFilePath ¶
func (i *Interpreter) GetFilePath() string
GetFilePath returns the current file path for error reporting
func (*Interpreter) GetModuleCache ¶
func (i *Interpreter) GetModuleCache(path string) (Value, int64, bool)
GetModuleCache retrieves a cached module value by absolute path with mtime validation. Returns (value, mtime, found). Caller should validate mtime if caching should expire. Used by require() to implement module caching with file change detection.
func (*Interpreter) GetScriptDir ¶
func (i *Interpreter) GetScriptDir() string
GetScriptDir returns the directory of the main script.
func (*Interpreter) ParseScript ¶
func (i *Interpreter) ParseScript(path string) (*Program, error)
ParseScript parses a script file with AST caching, using the interpreter's ScriptLoader. This is used by spawn(), run(), and HTTP handlers to avoid re-parsing the same script.
The cache is validated using file modification time: - If the file hasn't changed, the cached AST is returned - If the file is newer, it's re-parsed and the cache is updated - For /EMBED/ files, the cached AST is always returned
This requires ScriptLoader and FileStatter to be set on the interpreter.
func (*Interpreter) ParseScriptFile ¶
func (i *Interpreter) ParseScriptFile(path string, readFile func(string) ([]byte, error), getMtime func(string) int64) (*Program, error)
ParseScriptFile reads and parses a script file with AST caching and mtime checking. This is the centralized script loader used by require(), include(), and main script execution.
The cache is validated using file modification time: - If the file hasn't changed since caching, the cached AST is returned - If the file is newer, it's re-parsed and the cache is updated - For /EMBED/ files, the cached AST is always returned (embedded files don't change)
This function requires a FileReadFunc to be provided for reading files. It's typically called from pkg/cli with an appropriate file reader.
func (*Interpreter) QueueDebugEvent ¶
func (i *Interpreter) QueueDebugEvent(event *DebugEvent)
QueueDebugEvent sends a debug event to the main process (non-blocking due to buffered channel)
func (*Interpreter) RegisterDebugHandler ¶
func (i *Interpreter) RegisterDebugHandler(handler DebugHandler)
RegisterDebugHandler registers a handler function to be called when debug events occur. The handler is responsible for displaying the event to the user and managing the debug session. This allows the runtime to be I/O-agnostic while delegating user interaction to the handler.
Example (console debugging):
interp.RegisterDebugHandler(func(event *DebugEvent) {
handleConsoleDebugEvent(interp, event)
})
The handler will be called from the debug event listener goroutine.
func (*Interpreter) RegisterFunction ¶
func (i *Interpreter) RegisterFunction(name string, fn GoFunction) error
RegisterFunction registers a custom Go function callable from Duso scripts.
This is how embedded applications extend Duso with domain-specific functionality. For CLI-specific functions (load, save, include), see pkg/cli.
func (*Interpreter) RegisterObject ¶
func (i *Interpreter) RegisterObject(name string, methods map[string]GoFunction) error
RegisterObject registers an object with methods (e.g., "agents" with methods like "classify")
func (*Interpreter) SetFilePath ¶
func (i *Interpreter) SetFilePath(path string)
SetFilePath sets the current file path for error reporting
func (*Interpreter) SetModuleCache ¶
func (i *Interpreter) SetModuleCache(path string, value Value, mtime int64)
SetModuleCache stores a module value in the cache by absolute path with its mtime. Used by require() to cache module results so they're only loaded once.
func (*Interpreter) SetScriptDir ¶
func (i *Interpreter) SetScriptDir(dir string)
SetScriptDir sets the directory of the main script for relative path resolution. Used by run() and spawn() to resolve relative script paths when loading from embedded files.
type InvocationFrame ¶
type InvocationFrame struct {
Filename string // Script filename
Line int // Line number where invocation happened
Col int // Column number
Reason string // "http_route", "spawn", etc.
Details map[string]any // Additional context (method, path, etc.)
Parent *InvocationFrame // Previous frame in chain
}
InvocationFrame represents a single level in the call stack
type Lexer ¶
type Lexer struct {
// contains filtered or unexported fields
}
func NewLexerAt ¶
NewLexerAt creates a lexer with a starting line and column position Used for parsing template expressions within strings
type LintAnalyzer ¶
type LintAnalyzer struct {
// contains filtered or unexported fields
}
LintAnalyzer performs static analysis on a Duso AST
func NewLintAnalyzer ¶
func NewLintAnalyzer(program *Program, filename string) *LintAnalyzer
NewLintAnalyzer creates a new analyzer
func (*LintAnalyzer) Analyze ¶
func (a *LintAnalyzer) Analyze() []*LintDiagnostic
Analyze performs all linting checks
type LintDiagnostic ¶
type LintDiagnostic struct {
Message string
Severity int // 0=error, 1=warning
Line int
Column int
}
LintDiagnostic represents a linting issue
type LintScope ¶
type LintScope struct {
Parent *LintScope
Symbols map[string]*SymbolInfo
IsFunction bool
}
LintScope represents a lexical scope (function, block, etc.)
type ModuleCacheEntry ¶
type ModuleCacheEntry struct {
// contains filtered or unexported fields
}
ModuleCacheEntry holds a cached module result with its modification time
type NilLiteral ¶
type NilLiteral struct{}
type Node ¶
type Node interface {
// contains filtered or unexported methods
}
Node is the interface that all AST nodes must implement
type NumberLiteral ¶
type NumberLiteral struct {
Value float64
}
type ObjectLiteral ¶
type ObjectLiteral struct {
StaticPairs map[string]Node
ComputedPairs []*ComputedKeyPair
}
type Parameter ¶
type Parameter struct {
Name string // Parameter name
Default Node // Default value expression (nil if no default)
}
Parameter represents a function parameter with optional default value
type ParseCacheEntry ¶
type ParseCacheEntry struct {
// contains filtered or unexported fields
}
ParseCacheEntry holds a cached parsed AST with its modification time
type Parser ¶
type Parser struct {
// contains filtered or unexported fields
}
func NewParserWithFile ¶
NewParserWithFile creates a parser with an explicit file path for error reporting
type PostIncrementStatement ¶
type PropertyAccess ¶
type RegexLiteral ¶
type RegexLiteral struct {
Pattern string
}
type RegexValue ¶
type RegexValue struct {
Pattern string // Original pattern source
Compiled *regexp.Regexp // Compiled regex
}
RegexValue represents a compiled regular expression pattern
type RequestContext ¶
type RequestContext struct {
Data any // Generic context data (spawn/run data or HTTP request/response functions)
Frame *InvocationFrame // Root invocation frame for this context
ExitChan chan any // Channel to receive exit value from script
ProcessCtx context.Context // Process context for cancellation (kill support)
Interpreter *Interpreter // Reference to shared global interpreter (read-only)
Evaluator *Evaluator // Fresh evaluator for this execution's environment
CircularDetector *CircularDetector // Tracks circular dependency detection for require() calls
IOConfig *IOConfig // Per-execution I/O routing config
OutputWriter func(string) error // Per-execution output writer (may route to datastore)
// contains filtered or unexported fields
}
RequestContext holds context data for any spawned/invoked script Used for spawn() calls, run() calls, and HTTP handlers
func CurrentRequestContext ¶
func CurrentRequestContext(e *Evaluator) (*RequestContext, bool)
CurrentRequestContext returns the request context for the current execution. It prefers the evaluator-attached context (set by ExecuteScript), which avoids the goroutine-ID lookup (runtime.Stack costs ~3µs per call); evaluators without one (main script, module eval, parallel branches) fall back to the goroutine-local registration.
func GetRequestContext ¶
func GetRequestContext(gid uint64) (*RequestContext, bool)
GetRequestContext retrieves a request context from goroutine-local storage
type ReturnStatement ¶
type ReturnValue ¶
type ReturnValue struct {
Value Value
}
ReturnValue is used to signal a return from a function
func (*ReturnValue) Error ¶
func (e *ReturnValue) Error() string
type ScriptExecutionResult ¶
type ScriptExecutionResult struct {
Value any // The exit value or nil
Error error // Any error that occurred
}
ScriptExecutionResult holds the result of script execution
func ExecuteScript ¶
func ExecuteScript( program Node, interpreter *Interpreter, invocationFrame *InvocationFrame, requestContext *RequestContext, timeoutCtx context.Context, ) *ScriptExecutionResult
ExecuteScript executes a parsed script with proper exception handling. Used by run(), spawn(), and HTTP handlers to unify script execution and error handling.
type ScriptFunction ¶
type ScriptFunction struct {
Name string
FilePath string // File where function was defined (for error reporting)
Parameters []*Parameter
Body []Node
Closure *Environment
// contains filtered or unexported fields
}
type StringLiteral ¶
type StringLiteral struct {
Value string
}
type SymbolInfo ¶
type SymbolInfo struct {
Name string
Scope *LintScope
Position Position
Used bool
Kind string // "function", "parameter", "variable"
}
SymbolInfo tracks a definition (function or variable)
type TemplateLiteral ¶
type TernaryExpr ¶
type TokenType ¶
type TokenType int
const ( // Special TOK_EOF TokenType = iota TOK_ERROR TOK_COMMENT // Literals TOK_NUMBER TOK_STRING TOK_TILDE_STRING TOK_TRUE TOK_FALSE TOK_NIL TOK_IDENT // Keywords TOK_IF TOK_THEN TOK_ELSE TOK_ELSEIF TOK_END TOK_WHILE TOK_DO TOK_FOR TOK_IN TOK_FUNCTION TOK_RETURN TOK_BREAK TOK_CONTINUE TOK_TRY TOK_CATCH TOK_AND TOK_OR TOK_NOT TOK_VAR TOK_RAW // Operators TOK_PLUS TOK_MINUS TOK_STAR TOK_SLASH TOK_PERCENT TOK_EQUAL TOK_NOTEQUAL TOK_LT TOK_GT TOK_LTE TOK_GTE TOK_ASSIGN TOK_PLUSASSIGN TOK_MINUSASSIGN TOK_STARASSIGN TOK_SLASHASSIGN TOK_MODASSIGN TOK_INCREMENT TOK_DECREMENT // Delimiters TOK_LPAREN TOK_RPAREN TOK_LBRACKET TOK_RBRACKET TOK_LBRACE TOK_RBRACE TOK_COMMA TOK_DOT TOK_COLON TOK_QUESTION )
func LookupKeyword ¶
type TryStatement ¶
type Value ¶
type Value struct {
Type ValueType
Num float64 // inline storage for VAL_NUMBER — keeps arithmetic off the heap
Data any
}
func InterfaceToValue ¶
InterfaceToValue converts Go any to script values. This is used to convert Go values to script Values for builtins.
func NewErrorValue ¶
func NewFunction ¶
func NewFunction(fn *ScriptFunction) Value
func NewGoFunction ¶
func NewGoFunction(fn GoFunction) Value
func (Value) AsArrayPtr ¶
AsArrayPtr returns a pointer to the array for in-place mutations
func (Value) AsBinary ¶
func (v Value) AsBinary() *BinaryValue
func (Value) AsErrorVal ¶
func (v Value) AsErrorVal() *ErrorValue
func (Value) AsRegex ¶
func (v Value) AsRegex() *RegexValue
func (Value) IsFunction ¶
type ValueRef ¶
type ValueRef struct {
Val Value
}
ValueRef wraps a Value so it can pass through the any interface without losing type info
type ValueType ¶
type ValueType int
const ( VAL_NIL ValueType = iota VAL_NUMBER VAL_STRING VAL_BOOL VAL_ARRAY VAL_OBJECT VAL_FUNCTION VAL_CODE // pre-parsed code (source + AST + metadata) VAL_ERROR // first-class error value (message + stack string) VAL_BINARY // immutable binary data (files, images, etc.) VAL_REGEX // compiled regular expression pattern )