torb repl
torb repl reads entries from standard input, checks each against the session, runs it in the bytecode VM and keeps what it binds and declares for the next entry - a typed session and a piped file behave the same.
torb repl is a shell for TorbScript. Every entry is checked against everything the session holds - the declarations
and the bindings of the entries before it - compiled, and run once in the VM inside torb; what it binds with const
or var and what it declares stay for the entries after it. It reads plain standard input, so torb repl < setup.trb
is the same session a person would have typed, prompts and all: > before a whole new entry, . while one is still
open, written to standard error so standard output stays exactly what the entries printed and showed.
Synopsis#
torb repl Read entries from standard input until its end or :quit
--sandbox Every entry under SCRIPTS.md's grant of the working directory, not unrestricted
--steps <n> With --sandbox: the instructions one entry may run (default 1000000)
--time <ms> With --sandbox: the milliseconds one entry may run (default 2000)
--memory <bytes> With --sandbox: the bytes one entry may allocate (default 64000000)
--environment <pattern> With --sandbox: variables an entry may read, a name or a prefix and `*`; repeatable
--modules <module> With --sandbox: a std package an entry may use, or one module of std/os; repeatable
:type <expression> The type of an expression, checked and not run
:load <file> A file as one entry
:reset A new session: bindings released, declarations forgotten, files read again
:help The commands
:quit The end of the sessionWhat it does#
An entry#
An entry is one line, or as many lines as it takes to be whole: a line that leaves a bracket open, ends with an
operator or =, or opens a """ string or a /* comment that it does not close is continued by the next one. Two
empty lines in a row drop an entry that never became whole. Everything else that does not parse is reported as it is.
An entry may hold declarations - fn, type, trait, extend, use - and statements, in any order. The
declarations are kept for every later entry; the statements run once, in order. A name the top level binds with
const or var is a binding of the session once the entry ran to its end.
What it prints#
What the entry prints goes where a program's output goes. Where the last statement is an expression with a value, the
session shows it on standard output Scala/Kotlin style, value first: 1 + 2 shows 3: Int64, through Show, with the
type dimmed when standard output is a terminal and plain otherwise. A printed line and a shown value are told apart by
the trailing : Type, and print "hello" shows nothing because it answers Void. A value whose type has no Show,
such as a function, is shown as its type alone, in angle brackets. Where the last statement binds instead of
evaluating - const total = 6 * 7 - every name it binds is shown the same way, with its name in front: total: Int64 = 42.
Messages go to standard error, rendered as torb check renders them, with the entry as the file:
<entry 3> is the third entry of the session, a :loaded file is named by its path, and the line and the column are
the entry's own. A panic stops the entry and not the session, and prints what a native program prints for it, with the
entry's line; a ? at the top level that fails prints error: and the failure, as an entry file does. Ctrl+C stops a
running entry the same uneventful way, error: Interrupted, and returns to the prompt with the session intact.
Bindings and declarations#
const count = count + 1 binds a new count, and the old value is released; a var binding is changed in place by
every entry that assigns it. Declaring a function or a type again replaces it for every later entry. A binding whose
type names a type that is declared again keeps working: the old type is kept once more under a generation of its own,
and the binding is shown with it, Point#1 then #2 - nothing about the value is reinterpreted as the new type. A
declaration cannot see the session's bindings, as a top-level function of a program cannot see the locals of the code
that calls it; a closure can.
An entry that stops keeps the declarations it checked and the changes it made to var bindings before the stop, and
none of the bindings it would have made. The end of the session releases every binding, the newest first, so that a
close() runs as it would at the end of a program.
What an entry may do#
Everything the user may: every file, every variable of the environment, std and, in a workspace, the packages the
working directory's package depends on - the session is a file of the working directory that exists only in memory.
Relative paths are read against the working directory. Process.exit(code) ends the session with that code. What the
VM does not run yet - tasks, test and group - is refused with the reason before anything of the entry runs.
torb repl --sandbox narrows this to SCRIPTS.md's own grant instead: the working directory
read and write, no variable of the environment, and its default limits (1,000,000 steps, 64 MB, 2000 ms), a fresh
budget every entry - a runaway one stops itself without needing Ctrl+C. The flags after it set the rest of the grant:
--steps, --time and --memory each take a positive whole number, and --environment a variable an entry may read -
a name such as PATH, a prefix followed by one * such as TORB_*, or * for every one - once per pattern:
$ torb repl --sandbox --steps 50000000 --time 10000 --environment PATH --environment TORB_*Any of them without --sandbox is an error rather than a limit that silently does not apply, and so is a value that is
missing, 0, negative or not a number. :reset keeps --sandbox and its flags for the session it starts fresh.
Without --modules an entry may use every package of std, as an unrestricted session may. With it, only the
packages it names - and inside std/os, whose modules are granted one at a time, the modules it names, such as
std/os/environment; std/os grants all of them. An entry, a :loaded file or a :type whose use names another
module of std is refused before anything of it runs, and the session keeps everything it had:
> use File from "std/fs"
error: The entry may not import `std/fs`: grant it with `--modules std/fs`Relative imports and the workspace's own packages are never narrowed, and a module std does not have ends torb repl
before the session starts.
Exit codes#
0 at the end of the input or after :quit, or the code an entry handed to Process.exit.
Examples#
A session, typed or piped in:
$ torb repl
torb repl: one entry at a time, :help lists the commands
> const prices = [3, 5, 8]
> prices.map({ _ * 2 }).toList()
[6, 10, 16]: List<Int64>
> fn total(values: List<Int>): Int {
. values.sum()
. }
> total(prices)
16: Int64
> :type prices
List<Int64>
> prices[5]
panic: index 5 is out of bounds for a length of 3
at <entry 5>:1:1
>Related#
- The REPL - the design record: how an entry becomes a module, where the bindings live.
- torb run -
--vmruns a whole program in the same VM. - The torb command - every subcommand in one table.