Core Module API Reference¶
The core module (jsl.core) provides the fundamental data structures, evaluator, and environment management for JSL.
Overview¶
The jsl.core module provides the fundamental data structures that represent the state of a JSL program: Env for environments and Closure for functions. These are the building blocks used by the Evaluator and managed by the JSLRunner.
Classes¶
Env¶
The environment class manages variable bindings and scope chains.
jsl.core.Env(bindings=None, parent=None)
¶
Represents a JSL environment - a scope containing variable bindings.
Environments form a chain: each environment has an optional parent. When looking up a variable, we search the current environment first, then its parent, and so on until we find it or reach the root.
Source code in jsl/core.py
__contains__(name)
¶
Check if a variable exists in this environment or its parents.
__eq__(other)
¶
Check if two environments are equal.
Source code in jsl/core.py
content_hash()
¶
Generate a content-addressable hash with cycle detection.
Source code in jsl/core.py
deepcopy()
¶
Create a deep copy of this environment, including all parents.
Source code in jsl/core.py
define(name, value)
¶
Define a variable in this environment.
Source code in jsl/core.py
extend(new_bindings)
¶
get(name)
¶
Look up a variable in this environment or its parents.
Source code in jsl/core.py
to_dict()
¶
Convert environment bindings to a dictionary (for serialization).
Key Concepts¶
- Scope Chain: When looking up a variable, if it's not found in the current
Env, the search continues up to its parent, and so on, until the rootpreludeis reached. - Immutability: Methods like
extendcreate a new child environment rather than modifying the parent, preserving functional purity.
from jsl.core import Env
# Create a new environment
env = Env({"x": 10, "y": 20})
# Create a child environment that inherits from the parent
child_env = env.extend({"z": 30})
# Variable resolution follows the chain
print(child_env.get("x")) # 10 (from parent)
print(child_env.get("z")) # 30 (from child)
Closure¶
Represents a user-defined function with captured lexical environment.
jsl.core.Closure(params, body, env)
dataclass
¶
Represents a JSL function (closure).
A closure captures three things: 1. The parameter names it expects 2. The body expression to evaluate when called 3. The environment where it was defined (lexical scoping)
__call__(evaluator, args)
¶
Apply this closure to the given arguments.
Source code in jsl/core.py
deepcopy(env=None)
¶
Create a deep copy of this closure.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
env
|
Optional[Env]
|
Optional environment to use for the copy. If not provided, deep copies the closure's environment. |
None
|
Source code in jsl/core.py
Closures capture their defining environment:
from jsl.core import Closure, Env
# Create an environment that the closure will capture
env = Env({"multiplier": 3})
# Create a closure that captures the 'multiplier' variable from its environment
closure = Closure(
params=["x"],
body=["*", "multiplier", "x"],
env=env
)
# The closure remembers the 'multiplier' value
Evaluator¶
The main JSL expression evaluator:
jsl.core.Evaluator(host_dispatcher=None, resource_limits=None, host_gas_policy=None)
¶
The core JSL evaluator - recursive evaluation engine.
This is a clean, elegant reference implementation that uses traditional recursive tree-walking to evaluate S-expressions. It serves as the specification for JSL's semantics.
Characteristics: - Simple and easy to understand - Direct mapping from S-expressions to evaluation - Perfect for learning and testing JSL semantics - Limited by Python's recursion depth for deep expressions
For production use with resumption and better performance, use the stack-based evaluator which compiles to JPN (JSL Postfix Notation).
Source code in jsl/core.py
eval(expr, env)
¶
Evaluate a JSL expression in the given environment.
This is a pure recursive evaluator without resumption support. For resumable evaluation, use the stack-based evaluator.
Source code in jsl/core.py
HostDispatcher¶
Manages host interactions for side effects:
jsl.core.HostDispatcher()
¶
Handles JHIP (JSL Host Interaction Protocol) requests.
This is where all side effects are controlled. The host environment registers handlers for specific commands and decides what operations are permitted.
dispatch(command, args)
¶
Dispatch a host command with arguments.
Source code in jsl/core.py
Resource Management¶
ResourceBudget¶
jsl.core.ResourceBudget(limits=None, host_gas_policy=None)
¶
Comprehensive resource tracking for secure JSL execution.
Tracks gas consumption, memory allocation, execution time, and stack depth to prevent DOS attacks and ensure fair resource allocation.
Initialize resource budget.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
limits
|
Optional[ResourceLimits]
|
Resource limits configuration |
None
|
host_gas_policy
|
Optional[HostGasPolicy]
|
Gas cost policy for host operations |
None
|
allocate_memory(bytes_count, description='')
¶
Account for memory allocation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
bytes_count
|
int
|
Number of bytes to allocate |
required |
description
|
str
|
Description of allocation |
''
|
Raises:
| Type | Description |
|---|---|
MemoryExhausted
|
If memory limit would be exceeded |
check_collection_size(size)
¶
Check if a collection size is within limits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
size
|
int
|
Size of the collection |
required |
Raises:
| Type | Description |
|---|---|
MemoryExhausted
|
If collection size exceeds limit |
check_result(result)
¶
Check resource constraints for a computed result.
This centralizes checking for collection sizes, string lengths, etc.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
result
|
Any
|
The result value to check |
required |
check_string_length(length)
¶
Check if a string length is within limits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
int
|
Length of the string |
required |
Raises:
| Type | Description |
|---|---|
MemoryExhausted
|
If string length exceeds limit |
check_time()
¶
Check if time limit has been exceeded.
Raises:
| Type | Description |
|---|---|
TimeExhausted
|
If time limit has been exceeded |
checkpoint()
¶
Create a checkpoint of current resource usage.
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Dictionary with current resource usage |
consume_gas(amount, operation='')
¶
Consume gas for an operation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
amount
|
int
|
Gas amount to consume |
required |
operation
|
str
|
Description of operation (for error messages) |
''
|
Raises:
| Type | Description |
|---|---|
GasExhausted
|
If gas limit would be exceeded |
consume_host_gas(operation)
¶
Consume gas for a host operation based on namespace.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
operation
|
str
|
Host operation path (e.g., "@file/read") |
required |
enter_call()
¶
Enter a function call (increase stack depth).
Raises:
| Type | Description |
|---|---|
StackOverflow
|
If stack depth limit would be exceeded |
exit_call()
¶
Exit a function call (decrease stack depth).
restore(checkpoint)
¶
Restore resource usage from a checkpoint.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
checkpoint
|
Dict[str, Any]
|
Previously saved checkpoint |
required |
ResourceLimits¶
jsl.core.ResourceLimits(max_gas=None, max_memory=None, max_time_ms=None, max_stack_depth=100, max_collection_size=10000, max_string_length=100000)
¶
Configuration for resource limits.
GasCost¶
jsl.core.GasCost
¶
Bases: IntEnum
Gas costs for different operation types.
Error Types¶
JSLError¶
jsl.core.JSLError
¶
Bases: Exception
Base exception for all JSL runtime errors.
SymbolNotFoundError¶
jsl.core.SymbolNotFoundError
¶
Bases: JSLError
Raised when a symbol cannot be found in the current environment.
JSLTypeError¶
Global State¶
prelude¶
The global prelude environment containing all built-in functions. A global, read-only instance of Env that contains all the JSL built-in functions (e.g., +, map, get). It serves as the ultimate parent of all other environments.
from jsl.core import prelude
# Access built-in functions
plus_func = prelude.get("+")
map_func = prelude.get("map")
Implementation Details¶
Environment Chains¶
JSL uses environment chains for variable resolution:
- Current Environment: Look for variable in current scope
- Parent Environment: If not found, check parent scope
- Continue Chain: Repeat until variable found or chain ends
- Prelude Access: All chains eventually reach the global prelude
Closure Serialization¶
Closures are designed for safe serialization:
- Parameters: Always serializable (list of strings)
- Body: Always serializable (JSON expression)
- Environment: Only user-defined bindings are serialized
- Prelude: Built-in functions are reconstructed, not serialized
This ensures transmitted closures are safe and can be reconstructed in any compatible JSL runtime.
Type Definitions¶
The module defines the following type aliases for clarity:
from typing import Union, List, Dict, Any
JSLValue = Union[None, bool, int, float, str, List[Any], Dict[str, Any], Closure]
JSLExpression = Union[JSLValue, List[Any], Dict[str, Any]]
Usage Examples¶
Basic Evaluation¶
from jsl.core import Evaluator, Env
from jsl.prelude import make_prelude
# Create evaluator and environment
evaluator = Evaluator()
env = make_prelude()
# Evaluate an expression
result = evaluator.eval(["+", 1, 2, 3], env)
print(result) # Output: 6
Working with Closures¶
from jsl.core import Evaluator, Env
from jsl.prelude import make_prelude
evaluator = Evaluator()
env = make_prelude()
# Define a function
evaluator.eval(["def", "square", ["lambda", ["x"], ["*", "x", "x"]]], env)
# Call the function
result = evaluator.eval(["square", 5], env)
print(result) # Output: 25
Resource-Limited Execution¶
from jsl.core import Evaluator, ResourceBudget, ResourceLimits
# Create evaluator with resource limits
limits = ResourceLimits(max_steps=1000, max_gas=10000)
budget = ResourceBudget(limits=limits)
evaluator = Evaluator(resource_budget=budget)
# Execute with resource tracking
result = evaluator.eval(expensive_computation, env)
print(f"Gas used: {budget.gas_used}")
print(f"Steps taken: {budget.steps_taken}")