Documentation
¶
Index ¶
- func DumpErrorStack(w io.Writer, err error)
- func ExtendPackage(env *lisp.LEnv, name string) *lisp.LVal
- func Load(env *lisp.LEnv, fn Loader) *lisp.LVal
- func Validate(p Package) error
- type Builtin
- type FileLoader
- type Loader
- type Package
- type PackageBuiltins
- type PackageDocumented
- type PackageInit
- type PackageMacros
- type PackageSpecialOps
- type Template
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func DumpErrorStack ¶ added in v1.85.0
DumpErrorStack writes the trace of the first *lisp.ErrorVal in err's chain to w, followed by a newline. It writes the trace with lisp.ErrorVal.WriteTrace, so the trace has the error's output limit. For an err with no *lisp.ErrorVal in its chain, a nil err included, it writes nothing. It ignores write errors.
To show a trace beside the output of the code that failed, as a test does, write it to env.Runtime.Stderr.
func ExtendPackage ¶ added in v1.73.0
ExtendPackage makes the package named name env's current package, exactly as the Lisp form (in-package 'name) does: the package is created if it does not exist, and a package this call creates uses the language package (when the registry has one), while an existing package is entered unchanged. The name is validated as in-package validates it, with the same error.
It is the first step of a loader that adds Go builtins to a package that Lisp code may also define or extend (luthersystems/elps#745); follow it with LEnv.BindBuiltins. Like in-package it does not restore the previous package -- the Load helpers in this package do that for a loader.
It charges no evaluation step. A PackageLoader package does not use the language package; use ExtendPackage only where in-package semantics are what the package had before.
func Load ¶
Load loads an elps package implemented in Go. Load restores the package that was active when it was called, whether or not fn succeeds, so that further defined symbols end up in that package by default.
A loader that returns a Go nil *LVal, rather than lisp.Nil(), is reported as an error naming the loader instead of panicking.
func Validate ¶ added in v1.50.0
Validate reports mistakes in an elps package implemented in Go that the Go type system cannot catch. It is intended to be called from an embedder's test:
func TestPackage(t *testing.T) {
if err := elpsutil.Validate(&MyPackage{}); err != nil {
t.Fatal(err)
}
}
Validate reports every problem it finds, joined into a single error. A nil return means PackageLoader will not reject the package.
Validate performs one check that PackageLoader deliberately does not: it reports a package that contributes no builtins, no special operators, no macros and has no PackageInit. Such a package is almost certainly a mistake, but "almost" is not enough to fail a load -- a namespace-only package is unusual, not illegal.
Types ¶
type Builtin ¶
type Builtin struct {
// contains filtered or unexported fields
}
Builtin captures Go functions that are callable from elps.
func Function ¶
Function is a helper to construct builtins.
formals must be a list of symbols, as returned by lisp.Formals. A function that takes no arguments is declared with lisp.Formals(), the empty list; a nil formals list is not a valid spelling of that and is rejected by PackageLoader and Validate.
The definition holds a sealed copy of formals, as the interpreter's own builtin tables hold sealed formals. Binding the definition aliases sealed formals into the environment where it would copy unsealed ones, and a template shares them with every VM it makes instead of giving each VM its own copy. The caller's list is not changed.
A function constructed with Function has no documentation. Use FunctionDoc to give it a docstring.
func FunctionDoc ¶ added in v1.58.0
FunctionDoc is a helper to construct documented builtins. It is Function plus the docstring the function is registered with, which is what `elps doc` prints, what the language server shows on hover, and what the linter reads.
The docstring follows the same conventions as the interpreter's own builtins. In particular a paragraph beginning "Deprecated:" (or "DEPRECATED:") marks the function deprecated, exactly as it does in a Go doc comment: the `deprecated` lint check then reports every use of the function and quotes the rest of that paragraph as the reason. For example
elpsutil.FunctionDoc("blend-paths", lisp.Formals("a", "b"), blendPaths,
"Blend two paths.\n\nDeprecated: use join-paths instead.")
Like Function, it holds a sealed copy of formals.
func (*Builtin) Docstring ¶ added in v1.58.0
Docstring returns the documentation for a function, which is empty for a function constructed with Function. It satisfies the interface lisp checks for when registering a definition, so the string reaches the registered value and everything that reads documentation from it.
type FileLoader ¶ added in v1.85.0
type FileLoader struct {
// contains filtered or unexported fields
}
FileLoader loads constant source, such as a //go:embed file, into each environment it is given. It parses the source once, at the first Load, into a sealed lisp.Program, and evaluates that one Program in every environment. Hold a FileLoader at package scope so that every environment in the process shares the parse.
Each environment gets its own bindings. The program's literals are sealed and shared, so Lisp code that mutates one raises modify-literal-error; copy a literal before mutating it. Go code must not write the exported fields of the program's values.
FileLoader parses with its own parser.NewReader, not with an environment's Runtime.Reader, because every environment shares the one parse. A reader that the embedder sets or wraps on a runtime does not see these sources.
A FileLoader is safe for concurrent use.
func NewFileLoader ¶ added in v1.85.0
func NewFileLoader(bytecode []byte, filename string) *FileLoader
NewFileLoader returns a FileLoader for the source bytecode. Errors and stack traces name the source filename. NewFileLoader does not copy bytecode, so do not modify it afterwards. NewFileLoader parses nothing; the first Load parses the source.
func (*FileLoader) Load ¶ added in v1.85.0
func (l *FileLoader) Load(env *lisp.LEnv) *lisp.LVal
Load implements Loader. It evaluates the parsed source in env and returns the value of the last expression, or the first error.
When the source does not parse, Load returns an error that names the source. It returns the same error on every call, not only on the first, and does not parse again. The source is a constant, so a parse failure is a defect in the program that embeds it.
Load also recovers a panic out of the parser and returns it as the parse error, with the Go stack of the panic. sync.Once counts a call that panics as done, so without the recover every later Load would dereference a nil Loader and the original panic would be lost.
func (*FileLoader) ParseCount ¶ added in v1.85.0
func (l *FileLoader) ParseCount() uint64
ParseCount returns the number of times the loader has parsed its source: 0 before the first Load and 1 after a Load that parsed it. A source that fails to parse leaves it at 0.
type Loader ¶
Loader is a generic function to initialize/load an LEnv. A Loader should first define and switch into the package(s) it loads. The helpers in this package (Load, LoadAll, LibraryLoader and PackageLoader) restore the package that was active when they were called, whether the load succeeds or fails, so that symbols defined after a load end up where the caller expects them. A load performed from the user package -- the conventional top-level usage -- therefore ends in the user package, as it always has.
A chain of loaders may be formed to load a library.
func LibraryLoader ¶
LibraryLoader loads multiple elps packages implemented in Go.
func LoadAll ¶
LoadAll loads multiple elps files implemented in Go. Like Load, LoadAll restores the package that was active when it was called, whether or not the loaders succeed, and runs each loader from that package.
A loader that returns a Go nil *LVal, rather than lisp.Nil(), is reported as an error naming the loader and its position in the chain instead of panicking.
func PackageLoader ¶
PackageLoader loads an elps package implemented in Go.
PackageLoader restores the package that was active when it was called, whether or not the load succeeds, the same invariant the stdlib LoadPackage functions honor (https://github.com/luthersystems/elps/issues/99). It also re-establishes the loaded package after PackageInit returns, so that a PackageInit which switches packages -- directly, or indirectly through a nested Load -- does not cause the package's builtins, special operators and macros to be registered and exported somewhere else (https://github.com/luthersystems/elps/issues/352).
PackageLoader validates the package definition before registering anything, and returns an *LVal error identifying the package and the offending definition rather than letting lisp panic or deferring a nil dereference to call time. A definition lisp accepts today is still accepted.
type Package ¶
type Package interface {
PackageName() string
}
Package is an elps package implemented in Go.
type PackageBuiltins ¶
type PackageBuiltins interface {
Package
Builtins() []lisp.LBuiltinDef
}
PackageBuiltins retrieves the exposed builtins for an elps package implemented in Go.
type PackageDocumented ¶ added in v1.16.12
PackageDocumented allows an elps package to provide a documentation string.
type PackageInit ¶
PackageInit allows initialization of an elps package implemented in Go.
type PackageMacros ¶
type PackageMacros interface {
Package
Macros() []lisp.LBuiltinDef
}
PackageMacros returns the macros for an elps package implemented in Go.
type PackageSpecialOps ¶
type PackageSpecialOps interface {
Package
SpecialOps() []lisp.LBuiltinDef
}
PackageSpecialOps returns the special operators for an elps package implemented in Go.
type Template ¶ added in v1.73.0
type Template struct {
// contains filtered or unexported fields
}
Template is a quasiquote-style form, read once by the elps reader and expanded by Go macros into fresh syntax.
The source is one ordinary elps form: symbols, lists, (), strings, numbers and 'form are copied as written. Placeholders use the forms elps quasiquote uses, naming a declared parameter:
(unquote name) the argument, inserted by pointer (unquote-splicing name) the argument's cells, spliced into the enclosing list
A placeholder is recognised anywhere in the form, including under a quote, so '(unquote name) quotes the argument. unquote-splicing must appear directly inside a list, not at the top level or directly under a quote. Because every unquote form is a placeholder, a template cannot contain a literal unquote form or a nested quasiquote: in (quasiquote (a (unquote x))) the inner unquote is substituted. Build such a form in Go and pass it as an argument.
Expand makes none of the checks that quasiquote makes. ExpandChecked expands as quasiquote would, under env's limits, with its errors.
A template stores no LVals. Each Expand constructs fresh, unsealed, unlocated syntax, as the Go macro contract in lisp.LEnv.AddMacros requires: the evaluator locates it at the macro call site. Arguments are inserted by pointer, and () uses the immutable lisp.Nil singleton. Expansion neither reads nor evaluates anything, and templates may be expanded concurrently.
func MustTemplate ¶ added in v1.73.0
MustTemplate reads src with the elps reader and compiles it, with params declaring placeholder names in argument order. It is meant for package initialization and panics if src is not exactly one form, fails to parse, or contains a value the reader produces that a template cannot copy; on an undeclared placeholder, a duplicate or unused parameter, a malformed unquote, or a misplaced unquote-splicing.
func (*Template) Expand ¶ added in v1.73.0
Expand returns a fresh expansion, inserting args by pointer without copying or evaluating them. Quoted forms use lisp.Quote, as the reader does; Quote may copy an argument's header to add a quote without changing the argument. Expand panics if the argument count differs from the declared parameter count or a spliced argument is not an unquoted list (nil is accepted).
func (*Template) ExpandChecked ¶ added in v1.93.0
ExpandChecked returns what (quasiquote <template>) evaluates to in env when each placeholder's parameter is bound to its argument: the expansion, or the error the quasiquote raised. It runs quasiquote's own walk (lisp.LEnv.QuasiquoteWith), so it makes the same checks in the same order with the same messages: the value depth limit at each node, quote wrappers included, and the allocation limit as each list adds a cell, where a splice adds its argument's length. It checks env's context, as the quasiquote's evaluation of a placeholder's parameter did: at each placeholder on the walk, and once, before it expands, when it skips the walk (below), since every placeholder sees the same context. It charges no step and does not check the step limit or budget, so under a step limit the defmacro can fail where ExpandChecked does not. A spliced argument that is not a list is quasiquote's error, not a panic, and a nil argument is an error.
A Go macro that replaces a defmacro whose body was a quasiquote of the same form calls it, so the expansion fails where the defmacro's failed and a handler that catches the error reads the same message (luthersystems/elps#894). The expansion is fresh syntax, as Expand's is, with arguments inserted by pointer. When no check of the walk can fail, which is so for a small template under the default limits, ExpandChecked skips the walk and costs what Expand costs, and a () in the template is then the lisp.Nil singleton, as in Expand's expansion, where the walk builds an empty list. Otherwise it builds the form before it walks it, about twice Expand's cost. It panics if the argument count differs from the declared parameter count, and on a template with a list whose head is a quoted unquote, such as (f ('unquote y)): Expand copies that list, but quasiquote reads it as an unquote of y, so no defmacro expands as the template does.