2 Zuo Base Language
#lang zuo | |
#lang zuo/base |
The zuo language is Zuo’s default language. It’s meant to be familiar to Racket programmers, and the description here leans heavily on comparisons and the Racket documentation, for now. Zuo forms and functions tend use traditional Racket names, even when a different choice might be made in a fresh design, and even when the Zuo construct is not exactly the same. Filesystem operations, however, tend to use the names of Unix programs, which are much shorter than Racket’s long names.
The zuo/base language includes most of the bindings from zuo, but not the ones from zuo/cmdline, zuo/build, zuo/shell, zuo/thread, zuo/glob, or zuo/config.
When using module->hash on zuo/base, zuo, or a module implemented with one of those languages, the resulting hash table includes 'dynamic-require mapped to the dynamic-require function. Getting dynamic-require that way provides a path from the primitive Zuo kernel module protocol to zuo/base module exports.
Changed in version 1.2: Added the 'dynamic-require key for zuo and related languages.
2.1 Syntax and Evaluation Model
A zuo module consists of a sequence of definitions (e.g., define), macro definitions (e.g., define-syntax), imports (e.g., require), exports (e.g., provide), and expressions (e.g., 5). Loading the module first expands it, and then evaluates it. A module is loaded only once, so if a module is demanded more than once, the result of the first load is used.
The expansion process expands macro uses, loads imported modules, and evaluates macro definitions as such forms are encountered for the module body. Expansion creates a binding for each definition as encountered, but does not expand or evaluate the definition, yet. Expansion of definitions and expressions is deferred until all forms in the module body have been processed. Some expression forms have local definition contexts, which can include further imports and macro definitions, so expansion at those points nests the same two-step process as used for the module body.
Evaluation of a module evaluates its definitions and expressions (some of which may have been introduced by macro expansion) in order. Definitions bind mutually recursively within the enclosing module or definition context, and referencing a defined variable before its evaluation is an error. The value of each expression in a module body is printed using alert compiled with ~v.
A module’s provided variables and macros are made available to other modules that import it. Variables and macros that are not provided are completely inaccessible outside of the module.
There are no phases in the sense of Racket. When zuo macro expansion encounters an import, it makes all of the imported module’s exports immediately available for use in macro implementations, both variables and macros. For example, an imported macro might be used both to implement a macro body and in nearby run-time code or even run-time code generated by the macro’s expansion. The absence of a phase separation is related to the way that each module is evaluated only once, and it’s made workable in part by the absence of mutable data structures in Zuo, and in part because there is no support for compiling a zuo module and saving it separate from its instantiation in a Zuo process or saved image.
Zuo macros consume a representation of syntax that uses plain pairs, numbers, strings, etc., but with an identifier syntax object potentially in place of a symbol. Even for symbols, using a syntax object is optional; by using quote-syntax to create a syntax object, a macro can generate a term with identifiers bound at the macro’s definition site, instead of a use site’s, but the macro expander does not impose or automate that binding. See quote-syntax for more information.
2.2 Binding and Control Forms
A zuo syntactic form is either a definition form or an expression form. Expressions can appear in definition contexts, but not vice versa. In descriptions of syntactic forms body ...+ refers to a context that allows definition forms, but the last form in the expansion of the definition context must be an expression form.
2.2.1 Expression Forms
syntax
(lambda formals body ...+)
formals = (id ... [id expr] ...) | id | (id ... [id expr] ... . id)
syntax
(expr expr ...)
syntax
(let ([id val-expr] ...) body ...+)
(let proc-id ([id init-expr] ...) body ...+)
syntax
(let* ([id val-expr] ...) body ...+)
syntax
(letrec ([id val-expr] ...) body ...+)
syntax
(if test-expr then-expr else-expr)
syntax
(and expr ...)
syntax
(or expr ...)
syntax
(when test-expr body ...+)
syntax
(unless test-expr body ...+)
syntax
(cond cond-clause ...)
cond-clause = [test-expr then-body ...+] | [else then-body ...+]
syntax
syntax
(begin expr ...+)
syntax
(quote datum)
syntax
(quasiquote datum)
syntax
syntax
syntax
(quote-syntax datum)
A Zuo module’s representation starts with plain pairs and symbols, a macro procedure can receive terms containing plain symbols, and it can return a term with plain symbols. A symbol non-hygienically acquires a scope at the point where its binding is resolved or where it creates a binding.
A scope corresponds to a particular binding context. It can be a module context, an internal definition context, or a binding site for an expression form like the formals of a lambda or the right-hand side of a letrec.
An identifier syntax object created by quote-syntax closes over a binding at the point where it is created, closing over the enclosing module scope if the identifier is not (yet) bound. The closure does not change if the identifier is nested in a later quote-syntax form. Identifiers that are introduced by macros are not automatically given a scope or otherwise distinguished from identifiers that appeared as input to a macro, and a plain symbol is implicitly coerced to a syntax object only at the point where it binds or where its binding is resolved as a reference.
There is no quasisyntax, unsyntax, or unsyntax-splicing analog, since quasiquote, unquote, and unquote-splicing are already convenient enough for most purposes. To generate a fresh symbol for the output of a macro expansion, use string->uninterned-symbol.
syntax
2.2.2 Definition Forms
syntax
(define-syntax id expr)
(define-syntax (id . formals) body ...++)
If expr produces a context-consumer wrapper, then when id is used for a macro invocation, the wrapped procedure receives three arguments: the macro use as syntax, a function that acts like free-identifier=?, and either #f or an inferred-name string. (In racket, free-identifier=? and syntax-local-name are implicitly parameterized over the context of a macro invocation. Explicitly providing a comparison procedure and name string to a macro implementation, instead, avoids the implicit parameterization.)
See quote-syntax for more information about the representation of syntax that a macro function consumes and produces.
syntax
(struct id (field-id ...))
syntax
(include module-path)
syntax
(require spec ...)
spec = module-path |
(only-in module-path maybe-renamed-id ...) |
(rename-in module-path renamed-id ...) maybe-renamed-id = id | renamed-id renamed-id = [provided-id id]
syntax
(provide spec ...)
spec = id | (rename-out renamed-id ...) | (all-from-out module-path) maybe-renamed-id = id | renamed-id renamed-id = [id provided-id]
syntax
(module+ id defn-or-expr ...)
A submodule becomes a procedure of zero arguments that is a mapped from the symbol form of id in the enclosing module’s representation as a hash table (see Zuo Module Protocol). Calling the procedure evaluates the defn-or-expr content of the submodule, where expression results are printed and the procedure’s result is (void).
When Zuo loads a starting module (see Building and Running Zuo), it checks for a main submodule and runs it if one is found.
2.3 Booleans
Zuo booleans are written #t or #true and #f or #false. Any value other than #f counts as true for conditionals.
2.4 Numbers
A Zuo number corresponds to a 64-bit two’s complement representation with modular arithmetic (i.e., wraparound on overflow). It is always written in decimal form with a leading - for negative numbers.
procedure
z : integer?
procedure
z : integer? (- z w ...+) → integer? z : integer? w : integer?
procedure
z : integer?
procedure
n : integer? m : integer?
procedure
n : integer? m : integer?
procedure
n : integer? m : integer?
procedure
z : integer? w : integer?
procedure
x : integer? y : integer?
procedure
x : integer? y : integer?
procedure
x : integer? y : integer?
procedure
x : integer? y : integer?
procedure
(bitwise-ior n m) → integer?
n : integer? m : integer?
procedure
(bitwise-and n m) → integer?
n : integer? m : integer?
procedure
(bitwise-xor n m) → integer?
n : integer? m : integer?
procedure
(bitwise-not n) → integer?
n : integer?
Changed in version 1.9: Added remainder and changed modulo to match Racket.
2.5 Pairs and Lists
Zuo pairs and lists work the same as in Racket with the same textual representation.
procedure
v : any/c
procedure
v : any/c
procedure
v : any/c
procedure
a : any/c d : any/c
procedure
p : pair?
procedure
p : pair?
procedure
v : any/c
procedure
v : any/c tail : any/c
procedure
lst : list? (append lst ... v) → any/c lst : list? v : any/c
procedure
lst : list?
procedure
lst : list?
procedure
lst : pair? pos : integer?
procedure
lst : pair? pos : integer? v : any/c
procedure
lst : any/c pos : integer?
procedure
proc : procedure? lst : list?
procedure
proc : (any/c . -> . any/c) lst : list?
procedure
proc : (any/c any/c . -> . any/c) init : any/c lst : list?
procedure
proc : (any/c . -> . any/c) lst : list?
procedure
proc : (any/c . -> . any/c) lst : list?
procedure
proc : (any/c . -> . any/c) lst : list?
procedure
lst : list? less-than? : (any/c any/c . -> . any/c)
2.6 Strings
Zuo strings are sequences of bytes.
procedure
v : any/c
procedure
char : integer?
procedure
(string-length str) → integer?
str : string?
procedure
(string-ref str k) → integer?
str : string? k : integer?
procedure
str : string? start : integer? end : integer? = (string-length str)
procedure
str1 : string? str2 : string?
procedure
(string-ci=? str1 str2) → boolean?
str1 : string? str2 : string?
procedure
str1 : string? str2 : string?
procedure
(string-u32-ref str k) → integer?
str : string? k : integer?
procedure
(string->integer str) → (or/c integer? #f)
str : string?
procedure
(string-sha256 str) → string?
str : string?
See also file-sha256 and sha256-length.
syntax
(char str)
procedure
(string-split str) → (listof string?)
str : string? (string-split str sep) → (listof string?) str : string? sep : string?
procedure
(string-join strs [sep]) → string?
strs : list? sep : string? = " "
procedure
(string-trim str [edge-str]) → string?
str : string? edge-str : string? = " "
procedure
(string-tree? v) → boolean?
v : any/c
2.7 Symbols
Zuo symbols are interned by the reader, where two interned symbols are eq? when they have the same string content. An uninterned symbol is eq? only to itself. Zuo symbols are the only kind of value that can be used as a key for a Zuo hash table.
The textual representation of symbols does not include escapes for special character, unlike the way | works in Racket. Symbols with those characters will print in a way that cannot be read back into Zuo.
procedure
v : any/c
procedure
(symbol->string sym) → string?
sym : symbol?
procedure
(string->symbol str) → symbol?
str : string?
procedure
(string->uninterned-symbol str) → symbol?
str : string?
2.8 Hash Tables (Persistent Maps)
Zuo hash tables do not actually have anything to do with hashing, but they’re called that for similarity to Racket. A hash table maps symbols to other values, and updating a hash table produces a new hash table (which, internally, may share with the original).
Hash tables print in a way analogous to Racket, but there is no reader support to convert the textual form back into a hash table value.
procedure
v : any/c
procedure
key : symbol? val : any/c
procedure
hash : hash? key : symbol? (hash-ref hash key failure-value) → any/c hash : hash? key : symbol? failure-value : any/c
procedure
hash : hash? key : symbol? v : any/c
procedure
(hash-remove hash key) → hash?
hash : hash? key : symbol?
procedure
hash : hash?
procedure
(hash-count hash) → integer?
hash : hash?
procedure
(hash-keys-subset? hash1 hash2) → boolean?
hash1 : hash? hash2 : hash?
Besides being constrained to symbol keys, there are two additional differences:
the third argument to hash-ref, when supplied, is always used as a value to return if a key is missing, as opposed to a failure thunk; and
the hash-keys function returns interned keys sorted alphabetically.
2.9 Procedures
procedure
(procedure? v) → any/c
v : any/c
procedure
proc : procedure? lst : list?
procedure
proc : (any/c . -> . any/c)
procedure
(call/prompt proc tag) → any/c
proc : (-> any/c) tag : symbol?
procedure
tag : symbol?
2.10 Paths
A path string is a string that is not empty and contains no null bytes.
procedure
(path-string? v) → boolean?
v : any/c
procedure
(relative-path? path) → boolean?
path : path-string?
procedure
(build-raw-path base rel ...) → path-string?
base : path-string? rel : path-string?
procedure
(build-path base rel ...) → path-string?
base : path-string? rel : path-string?
procedure
(split-path path) → pair?
path : path-string?
procedure
(explode-path path) → (listof path-string?)
path : path-string?
procedure
(simple-form-path path) → path-string?
path : path-string?
procedure
(find-relative-path base path) → path-string?
base : path-string? path : path-string?
The result path depends on whether base and path are relative or absolute:
If both are relative, the result is always a relative path. If base starts with ".." elements that are not matched by path, then elements are drawn from (hash-ref (runtime-env) 'dir).
If both are absolute, the result is absolute if base and path do not share a root element, otherwise the result is relative.
If path is absolute and base is relative, path is returned as-is. The intent of this mode is to preserve the “absoluteness” of path in a setting that otherwise works in terms of relative paths.
If base is absolute and path is relative, path is converted to absolute via path->complete-path, and the result is as when both are absolute (so, the result may still be absolute).
procedure
(path-only path) → path-string?
path : path-string?
procedure
(file-name-from-path path) → (or/c path-string? #f)
path : path-string?
procedure
(path->complete-path path) → path-string?
path : path-string?
procedure
(path-replace-extension path suffix) → path-string?
path : path-string? suffix : string?
syntax
(lambda args (apply build-path (cons (path-only (quote-module-path)) args)))
If the argument to the function is an absolute path, however, the enclosing module’s directory is ignored, and the function acts simply like build-path.
2.11 Opaque Records
procedure
(opaque-ref key v failure-val) → any/c
key : any/c v : any/c failure-val : any/c
2.12 Variables
A variable is a value with a name that contains another value. The contained value is initially undefined, and attempting to access the contained value before it’s set results in an error where the variable’s name is used in the error message. A variable’s contained value can be set only once.
procedure
(variable-set! var val) → void?
var : variable? val : any/c
procedure
(variable-ref var) → any/c
var : variable?
2.13 Modules and Evaluation
A module path is a path string or a symbol, where a symbol must contain only the letters A-Z, a-z, 0-9, -, +, _, or /. Furthermore, / in a symbol module path cannot be at the start, end, or adjacent to another /.
procedure
(module-path? v) → boolean?
v : any/c
procedure
(build-module-path base rel-path) → module-path?
base : module-path? rel-path : path-string?
procedure
(module->hash mod-path) → hash?
mod-path : module-path?
procedure
(dynamic-require mod-path export) → any/c
mod-path : module-path? export : symbol?
procedure
(kernel-eval s-exp) → any/c
s-exp : any/c
procedure
(kernel-env) → hash?
2.14 Void
2.15 Reading and Writing Objects
procedure
(string-read str [start where]) → list?
str : string? start : integer? = 0 where : any/c = #f
See also Zuo S-Expression Reader.
Unlike uninterned symbols in racket, Zuo uninterned symbols format in print and write styles with #<symbol:...>. Opaque objects, handles, and variables print with #<:...> notation in all styles.
If the first v is a string, its characters are printed followed by : . All other vs (including the first one if it’s not a string) are combined using ~v, and the resulting string’s characters are printed.
procedure
(arity-error name args) → void?
name : (or/c string? #f) args : list?
2.16 Syntax Objects
A syntax object combines a symbol with a binding scope, where the two are used to determine a binding when the identifier is used in a macro expansion.
procedure
(identifier? v) → boolean?
v : any/c
procedure
v : identifier?
procedure
(syntax->datum v) → any/c
v : any/c
procedure
(datum->syntax ctx v) → any/c
ctx : identifier? v : any/c
procedure
(bound-identifier=? id1 id2) → boolean?
id1 : identifier? id2 : identifier?
procedure
(syntax-error message stx) → void?
message : string? stx : any/c
procedure
(bad-syntax stx) → void?
stx : any/c
procedure
(misplaced-syntax stx) → void?
stx : any/c
procedure
(duplicate-identifier stx) → void?
stx : any/c
procedure
(context-consumer proc) → context-consumer?
proc : procedure
procedure
(context-consumer? v) → boolean?
v : any/c
2.17 Files, Streams, and Processes
Files, input and output streams, and processes are all represented as handles.
procedure
(fd-open-input filename [options]) → handle?
filename : (or/c path-string? 'stdin integer?) options : hash? = (hash)
No keys are currently recognized for options, so it must be an empty hash table.
procedure
(fd-open-output filename [options]) → handle?
filename : (or/c path-string? 'stdout 'stderr integer?) options : hash? = (hash)
value
value
value
value
value
value
In options, a single key is currently recognized: 'exists. The mapping for 'exists must be one of the symbols accepted for #:exists by open-output-file from racket, but not 'replace or 'truncate/replace, and the default mapping is 'error. Any other key in options is an error.
The :error, :truncate, :must-truncate, :append, :update, and :can-update hash tables each map 'exists to the corresponding mode.
The number of bytes in the returned string can be less than amount if the number of currently available bytes is less than amount but at least one byte. The result can be an empty string only if amount is 0 or eof.
On Windows, 'avail mode is not supported for console input.
Changed in version 1.5: Report eof when available in 'avail mode.
Added in version 1.1.
procedure
(fd-terminal? handle [check-ansi?]) → boolean?
handle : handle? check-ansi? : any/c = #f
When using ANSI escapes that change the character style, consider bracketing a change and restore with suspend-signal and resume-signal to avoid leaving a terminal in a mangled state after Ctl-C.
procedure
(file->string name) → string?
name : path-string?
procedure
(display-to-file str name [options]) → void?
str : string? name : path-string? options : hash? = (hash)
procedure
(cleanable-file name) → handle?
name : path-string?
procedure
(cleanable-cancel cleanable) → void?
cleanable : handle?
procedure
executable : path-string? args : string-tree? options : hash? = (hash)
If options is supplied, it controls the process creation and may cause additional keys to be mapped in the result. The recognized keys are as follows, and supplying an unrecognized key in options is an error:
'dir mapped to a path string: the working directory of the new process; if executable is a relative path, it is relative to this directory
'env mapped to a list of pairs of strings: environment variables for the new process, where the car or each pair is an environment variable name and the cdr is its value
'stdin mapped to 'pipe: creates a new output stream connected to the new process’s standard input; the result hash table contains 'stdin mapped to the new stream handle
'stdin mapped to an input stream: supplies (a copy of) the input stream as the new process’s standard input
'stdout mapped to 'pipe: creates a new input stream connected to the new process’s standard output; the result hash table contains 'stdout mapped to the new stream handle
'stdout mapped to an output stream: supplies (a copy of) the output stream as the new process’s standard input
'stderr mapped to 'pipe: creates a new input stream connected to the new process’s standard error; the result hash table contains 'stderr mapped to the new stream handle
'stderr mapped to an output stream: supplies (a copy of) the output stream as the new process’s standard error
'cleanable? mapped to boolean (or any value): if #f, the Zuo process can exit without waiting for the created process to terminate; otherwise, and by default, the Zuo process waits for every processes created with process to terminate before exiting itself, whether exiting normally, by an error, or by a received termination signal (such as Ctl-C); any still-open input or output pipe created for the process is closed before waiting for processes to exit.
'exact? mapped to boolean (or any value): if not #f, a single args must be provided, and it is provided as-is for the created process’s command line on Windows. A non-#f value for 'exact? is not allowed on Unix.
'exec? mapped to boolean (or any value): if not #f, the target executable is run in the current process, after waiting for any other subprocesses and deleting cleanables. A non-#f value for 'exec? is not allowed on Windows or, more generally, when (hash-ref (runtime-env) 'can-exec?) is #f.
See also shell.
Changed in version 1.1: Pipes created for a process are explicitly closed when a Zuo will terminate, and they are closed before waiting for processes to exit.
procedure
(process-wait process ...) → handle?
process : handle?
procedure
(process-status process) → (or/c 'running integer?)
process : handle?
procedure
(find-executable-path name) → (or/c path-string? #f)
name : path-string?
The PATH environment variable is treated as a list of :-separated paths on Unix and ;-separated paths on Windows. On Windows, the current directory is automatically added to the start of PATH.
procedure
(string->shell str) → string?
str : string?
procedure
(shell->strings str [starts-exe?]) → (listof string?)
str : string? starts-exe? : any/c = #f
2.18 Filesystem
procedure
(stat name [follow-links? false-on-error?]) → (or/c hash? #f)
name : path-string? follow-links? : any/c = #t false-on-error? : any/c = #f
If name is a valid path but no such file, directory, or link exists, the result is #f. If accessing name encounters an error (e.g., name uses a file as a directory or permission is denied), then #f is reported instead of an error if false-on-error?. Otherwise, the hash table has similar keys and values as file-or-directory-stat from racket, but with only certain keys per platform:
Unix: 'device-id, 'inode, 'mode, 'type (abbreviated), 'hardlink-count, 'user-id, 'group-id, 'device-id-for-special-file, 'size, 'block-size, 'block-count, 'access-time-seconds, 'modify-time-seconds, 'change-time-seconds, 'access-time-nanoseconds, 'modify-time-nanoseconds, and 'change-time-nanoseconds
Windows: 'device-id, 'inode, 'mode (read and write bits only), 'type (abbreviated), 'hardlink-count, 'size, 'access-time-seconds, 'modify-time-seconds, 'creation-time-seconds, 'access-time-nanoseconds, 'modify-time-nanoseconds, and 'creation-time-nanoseconds
The abbreviated 'type field contains 'file, 'dir, or 'link, with 'link only on Unix and only when follow-links? is #f.
procedure
(ls dir) → (listof path-string?)
dir : path-string?
procedure
(ls* dir) → (listof path-string?)
dir : path-string?
procedure
name : path-string?
procedure
name : path-string?
procedure
name : path-string? new-name : path-string?
procedure
dir : path-string?
procedure
dir : path-string?
procedure
dir : path-string?
procedure
target : path-string? name : path-string?
procedure
name : path-string?
procedure
source : path-string? destination : path-string? options : hash? = (hash)
value
On Unix, if destination does not exist, it is created with
the mode (i.e., permissions) specified by 'mode in
options, which must be an integer between 0 and 65535
inclusive; if 'mode is not provided, the mode of
source is used. The creation-time mode can be modified by
the process’s umask, but unless options maps
'replace-mode to #f, the mode is explicitly applied again
to destination—
The :no-replace-mode hash table maps 'replace-mode to #f.
Changed in version 1.6: Added the options argument and :no-replace-mode.
procedure
source : path-string? destination : path-string? options : hash? = (hash)
Changed in version 1.6: Added the options argument.
procedure
(file-exists? name) → boolean?
name : path-string?
procedure
(directory-exists? name) → boolean?
name : path-string?
procedure
(link-exists? name) → boolean?
name : path-string?
2.19 Run Time Configuration
procedure
(runtime-env) → hash?
'args: command-line arguments provided when the process was started, not counting Zuo configuration arguments or the name of a script to run
'dir: the current directory; on Unix, if the PWD environment variable is set as an absolute path that refer to the same directory as the one reported by the operating system, the PWD form is used
'env: a list of pairs of strings for environment variables
'script: the script provided to Zuo to run, which might be "" to indicate a script read from standard input
'exe: an absolute path for the running Zuo executable
'system-type: 'unix or 'windows
'toolchain-type: 'unix (cc, etc.) or 'windows (cl.exe, etc.); on Windows, the default is determined by the compiler used to build the Zuo executable, but it can be set explicitly by defining either the ZUO_WINDOWS_TOOLCHAIN or ZUO_UNIX_TOOLCHAIN preprocessor symbol when compiling Zuo
'sys-dir (Windows only): the path to the system directory
'can-exec?: a boolean indicating whether process supports a true value for the 'exec? option
'version: Zuo’s major version number as an integer
'minor-version: Zuo’s minor version number as an integer
Changed in version 1.1: Added 'minor-version.
Changed in version 1.11: Changed 'dir to use the PWD
environment variable.
procedure
(system-type) → symbol?
procedure
(current-time) → pair?
procedure
(dump-image-and-exit output) → void?
output : handle?
This function is intended to be used after some set of modules has been loaded, so that the loaded modules are included in the image. The dump fails if any handle is encountered as reachable from loaded modules, however.
procedure
(suspend-signal) → void?
procedure
(resume-signal) → void?