Receiver scripts
A .trb file can be loaded as the body of a receiver closure, type checked against a receiver type before it runs, and run by the sandboxed VM.
Draft. It runs in the VM: a program torb run runs loads and applies a script, and so does the toolchain for a
project.trb; a native binary runs its scripts in the VM of the script host it links, and the value crosses into
that VM and back through the derived Encode and Decode of the receiver
(Receiver Scripts and the Sandbox).
A receiver closure is a value passed at a call site. A
receiver script is the same idea with a whole file standing in for that value:
project.trb is one already, and any .trb file can become one through Sandbox.load.
Example#
use Sandbox, SandboxError from "std/sandbox"
type ServerConfig {
var host: String = "localhost"
var port: Int = 8080
}
fn loadConfig(path: String): Result<ServerConfig, SandboxError> {
const script = Sandbox.load<ServerConfig>(path)?
var config = ServerConfig()
script.apply(config)?
config
}Syntax#
Sandbox.load<Value>(path: String): Result<Script<Value>, SandboxError>
Script.apply(var value: Value): Result<Void, SandboxError>Rules#
Loading and running are two steps, and each has its own failure.
Sandbox.load<Value>(path)reports what is wrong with the file itself: a syntax error, a type error againstValue, or a module the script may not import.script.apply(value)reports what went wrong while the body ran, against avalueof the caller's own.The type argument of
loadis the receiver, and it is checked as the body of(var self: Value) => Void.Valuedoubles as the whitelist of what the file can do: its members are what the script can name beyond the prelude.The path is relative to the directory of the project, not to the file that calls
load. That is the opposite ofuse, whose relative path is relative to the importing file - a loaded script is a question about where the program runs, not about the source tree.A receiver script may hold top-level code, unlike an ordinary module: it is one of the four kinds of file top-level code allows, alongside an entry file, an unimported script and a
*.test.trbfile.Only the VM runs a receiver script. A program
torb runruns in the VM loads and applies one, and so does a native binary thattorb build --embed-vmbuilt, which embeds the VM. A native binarytorb buildcompiled runs it in the VM of the script host it links, and its receiver is thenEncode & Decode, which the value crosses by. A literal path names a script the checker checked with the program; a path the program computes is checked against the type argument when the program loads it, by thetorbthat runs it. The toolchain runs one too:torb manifestevaluates aproject.trb, while the commands that build and test still read it by parsing its literal top-level command calls.
What this is not#
A receiver script is not imported. use names a module and its exports; Sandbox.load names a file and checks
it as a closure body, and the file itself declares nothing another file could import from it.
use Sandbox from "std/sandbox"
type ServerConfig {
var host: String = "localhost"
}
fn describe(path: String) {
print "loading {path} as ServerConfig"
}
describe "./config"use Host from "./config"Related#
- The sandbox - the capabilities granted at the call site of
Sandbox.load. - Receiver closures - the closure form a receiver script's body is checked as.
- Top-level code - why a receiver script may hold statements a module cannot.