Runtime API
The Runtime API provides the execution engine for Prism programs, handling AST interpretation, value management, confidence tracking, and context management.
Overview
The runtime system includes:
- AST interpretation and execution
- Value types with confidence tracking
- Environment management for variable scoping
- Context management for uncertainty handling
- Built-in functions and LLM integration
- Error handling with detailed error messages
Interpreter Class
Constructor
class Interpreter {
constructor()
}
Creates a new interpreter instance with an empty environment and context manager.
Methods
interpret()
async interpret(node: ASTNode): Promise<Value>
Interprets an AST node and returns the resulting value.
Parameters:
node
: The AST node to interpret
Returns: A Promise resolving to the execution result
Example:
import { parse, Interpreter } from '@prism-lang/core';
const interpreter = new Interpreter();
const ast = parse('x = 10; y = x * 2;');
const result = await interpreter.interpret(ast);
console.log(result.toString()); // "20"
registerLLMProvider()
registerLLMProvider(name: string, provider: LLMProvider): void
Registers an LLM provider for use in llm()
function calls.
Parameters:
name
: Unique identifier for the providerprovider
: LLMProvider implementation
setDefaultLLMProvider()
setDefaultLLMProvider(name: string): void
Sets the default LLM provider for llm()
calls.
Parameters:
name
: Name of a registered provider
Example:
import { MockLLMProvider } from '@prism-lang/llm';
const interpreter = new Interpreter();
const mockProvider = new MockLLMProvider();
interpreter.registerLLMProvider('mock', mockProvider);
interpreter.setDefaultLLMProvider('mock');
Value Types
All runtime values extend the abstract Value
class:
abstract class Value {
abstract type: string;
abstract value: unknown;
abstract equals(other: Value): boolean;
abstract isTruthy(): boolean;
abstract toString(): string;
}
NumberValue
class NumberValue extends Value {
constructor(value: number)
type: 'number'
value: number
}
Represents numeric values.
Truthy: When not zero
StringValue
class StringValue extends Value {
constructor(value: string)
type: 'string'
value: string
}
Represents string values.
Truthy: When not empty
BooleanValue
class BooleanValue extends Value {
constructor(value: boolean)
type: 'boolean'
value: boolean
}
Represents boolean values.
Truthy: When true
NullValue
class NullValue extends Value {
constructor()
type: 'null'
value: null
}
Represents null values.
Truthy: Always false
UndefinedValue
class UndefinedValue extends Value {
constructor()
type: 'undefined'
value: undefined
}
Represents undefined values.
Truthy: Always false
ArrayValue
class ArrayValue extends Value {
constructor(elements: Value[])
type: 'array'
value: Value[]
elements: Value[]
}
Represents array values.
Truthy: Always true
Example:
const arr = new ArrayValue([
new NumberValue(1),
new StringValue("hello")
]);
ObjectValue
class ObjectValue extends Value {
constructor(properties: Map<string, Value>)
type: 'object'
value: Map<string, Value>
properties: Map<string, Value>
}
Represents object values with string keys.
Truthy: Always true
Example:
const obj = new ObjectValue(new Map([
['name', new StringValue('Alice')],
['age', new NumberValue(30)]
]));
FunctionValue
class FunctionValue extends Value {
constructor(
name: string,
value: (args: Value[]) => Promise<Value>,
arity?: number
)
type: 'function'
name: string
value: (args: Value[]) => Promise<Value>
arity?: number
}
Represents function values, including built-ins and lambdas.
Truthy: Always true
ConfidenceValue
class ConfidenceValue extends Value {
constructor(
value: Value,
confidence: ConfidenceLib
)
type: 'confident'
value: Value
confidence: ConfidenceLib
}
Wraps any value with a confidence score.
Truthy: Based on wrapped value
Example:
const confident = new ConfidenceValue(
new StringValue("prediction"),
new ConfidenceLib(0.85)
);
console.log(confident.toString()); // "prediction (~0.85)"
Environment Management
Environment Class
class Environment {
constructor(parent?: Environment)
define(name: string, value: Value): void
get(name: string): Value
set(name: string, value: Value): void
getAllVariables(): Map<string, Value>
}
Manages variable scoping with lexical parent chaining.
Methods:
define()
: Creates a new variable in current scopeget()
: Retrieves variable value (searches parent scopes)set()
: Updates existing variable (searches parent scopes)getAllVariables()
: Returns all variables in current scope
Example:
const global = new Environment();
global.define('x', new NumberValue(10));
const local = new Environment(global);
local.define('y', new NumberValue(20));
console.log(local.get('x')); // NumberValue(10) - from parent
console.log(local.get('y')); // NumberValue(20) - from local
Error Handling
RuntimeError
class RuntimeError extends Error {
constructor(
message: string,
node?: ASTNode,
location?: { line: number; column: number }
)
line?: number
column?: number
node?: ASTNode
}
Thrown during runtime execution errors.
Example:
try {
await interpreter.interpret(ast);
} catch (error) {
if (error instanceof RuntimeError) {
console.error(`Runtime error at line ${error.line}: ${error.message}`);
}
}
LoopControlError
class LoopControlError extends Error {
constructor(type: 'break' | 'continue')
type: 'break' | 'continue'
}
Internal error used for loop control flow (break/continue).
Built-in Functions
The interpreter provides several built-in functions:
llm()
llm(prompt: string): Promise<ConfidenceValue<string>>
Calls the configured LLM provider.
Parameters:
prompt
: The prompt string
Returns: Confident string response
Example:
response = llm("What is 2+2?");
// Returns: "4" ~> 0.95
Array Functions
map()
map(array: Array, fn: Function): Array
Transforms array elements.
Example:
numbers = [1, 2, 3];
doubled = map(numbers, x => x * 2);
// Returns: [2, 4, 6]
filter()
filter(array: Array, predicate: Function): Array
Filters array elements.
Example:
numbers = [1, 2, 3, 4, 5];
evens = filter(numbers, x => x % 2 == 0);
// Returns: [2, 4]
reduce()
reduce(array: Array, reducer: Function, initial?: any): any
Reduces array to single value.
Example:
numbers = [1, 2, 3, 4];
sum = reduce(numbers, (acc, x) => acc + x, 0);
// Returns: 10
Math Functions
max()
max(...values: number[]): number
Returns maximum value.
Example:
largest = max(10, 5, 20, 15);
// Returns: 20
min()
min(...values: number[]): number
Returns minimum value.
Example:
smallest = min(10, 5, 20, 15);
// Returns: 5
Execution Features
Confidence Propagation
Confidence values are propagated through operations:
x = 10 ~> 0.9;
y = 20 ~> 0.8;
z = x + y; // Result has confidence based on inputs
Context Management
The runtime maintains execution contexts for uncertainty handling:
in context "analysis" {
// Code executes in analysis context
result = processData(input);
}
Loop Control
Supports break and continue statements:
for i = 0; i < 10; i++ {
if (i == 5) {
break; // Exit loop
}
if (i % 2 == 0) {
continue; // Skip even numbers
}
// Process odd numbers
}
Destructuring Assignment
Supports array and object destructuring with confidence:
// Array destructuring
[a, b, c] = [1, 2, 3];
[x, ...rest] = array;
// Object destructuring
{name, age} = person;
{x: coordX, y: coordY} = point;
// With confidence thresholds
[a, b] ~> 0.8 = uncertainArray;
Performance Considerations
- Async Execution: All operations are async to support LLM calls
- Environment Lookup: Variable lookup is O(depth) where depth is scope nesting
- Confidence Tracking: Adds minimal overhead to operations
- Memory Management: Values are garbage collected when out of scope
Example: Custom Function Registration
import { Interpreter, FunctionValue, NumberValue, Value } from '@prism-lang/core';
const interpreter = new Interpreter();
// Add custom sqrt function
interpreter.environment.define('sqrt', new FunctionValue(
'sqrt',
async (args: Value[]) => {
if (args.length !== 1) {
throw new RuntimeError('sqrt() requires exactly one argument');
}
const arg = args[0];
if (!(arg instanceof NumberValue)) {
throw new RuntimeError('sqrt() requires a number');
}
return new NumberValue(Math.sqrt(arg.value));
},
1 // arity
));
// Use the custom function
const result = await interpreter.interpret(parse('sqrt(16)'));
console.log(result.toString()); // "4"