pub struct Engine { /* private fields */ }Expand description
The persistent embedding entry point. Holds the table of registered host
functions and compiles scripts into reusable Programs.
This is the library analogue of the one-shot [crate::candela_run]: it does
not run a script and hand back stdout, it keeps compiler + VM state resident
so the host can call into the script repeatedly.
Implementations§
Source§impl Engine
impl Engine
pub fn new() -> Self
Sourcepub fn with_lib_dir(self, dir: impl Into<PathBuf>) -> Self
pub fn with_lib_dir(self, dir: impl Into<PathBuf>) -> Self
Points library imports at dir instead of the directory the toolchain
installed.
dir is the one that holds std/, so import "std/string"; reads
dir/std/string.cdl. A host that ships the standard library inside its
own application directory names it here, and the scripts it compiles
find it wherever the application was installed.
let engine = candela::Engine::new().with_lib_dir("/opt/myapp/libs");Sourcepub fn with_cfg(self, cfg: Cfg) -> Self
pub fn with_cfg(self, cfg: Cfg) -> Self
Sets the flags @cfg(...) attributes test in the scripts this engine
compiles.
Code under a @cfg whose condition is false is dropped before names
resolve, so it may import a module or call a function that does not
exist here. A flag the configuration does not enable is false.
let engine = candela::Engine::new().with_cfg(candela::Cfg::from_iter(["web"]));Sourcepub fn with_import_root(self, name: &str, dir: impl Into<PathBuf>) -> Self
pub fn with_import_root(self, name: &str, dir: impl Into<PathBuf>) -> Self
Makes dir the root of the package a script imports as name.
The package is entered through the file its candela.toml names as
entry, or src/main.cdl when the manifest names none or there is no
manifest. import "shapes"; then reads that file, and
import "shapes/circle"; reads circle.cdl from the entry’s
directory. dir is searched for native libraries too, so a package
that ships a shared library under its root binds a dylib import with
no further setup.
let engine = candela::Engine::new().with_import_root("shapes", "/cache/shapes/1.2.3");Sourcepub fn register_host_fn<Marker, F>(&mut self, namespace: &str, name: &str, f: F)where
F: IntoHostFn<Marker>,
pub fn register_host_fn<Marker, F>(&mut self, namespace: &str, name: &str, f: F)where
F: IntoHostFn<Marker>,
Registers a typed host function under namespace::name.
The closure may take any combination of i64/i32, f64, bool,
String (or a single &str) arguments and return one of those or ().
The declared types are checked against the script’s host block when
Engine::compile runs; a mismatch is a clean Diagnostic, never a
panic.
A closure that can fail returns Result<T, HostError> instead of T,
and the error is raised at the call site in the script:
use candela::HostError;
let mut engine = candela::Engine::new();
engine.register_host_fn("fs", "read", |path: &str| {
std::fs::read_to_string(path).map_err(HostError::new)
});The type checked against the declaration is the T inside the Result,
so both spellings bind to the same host signature.
Sourcepub fn register_host_fn_typed<F>(
&mut self,
namespace: &str,
name: &str,
arg_types: Vec<HostType>,
ret_type: HostType,
f: F,
)
pub fn register_host_fn_typed<F>( &mut self, namespace: &str, name: &str, arg_types: Vec<HostType>, ret_type: HostType, f: F, )
Registers a host function whose signature is given as data rather than derived from a Rust closure’s types.
The closure takes the arguments as a &[Value] slice, and arg_types /
ret_type say what the script may pass and expect. The binding is
checked against the host block exactly as Engine::register_host_fn
is, so a declaration that disagrees is a Diagnostic at
Engine::compile time, and the declaration must not use ....
use candela::{HostType, Value};
let mut engine = candela::Engine::new();
engine.register_host_fn_typed(
"gpio",
"read",
vec![HostType::Int],
HostType::Int,
|args: &[Value]| Ok(Value::Int(args[0].as_i64().unwrap_or(0))),
);Use it when the signature is only known at run time: a plugin table, a generated binding, anything with no Rust signature to read the types from.
Sourcepub fn register_host_fn_variadic<F>(
&mut self,
namespace: &str,
name: &str,
f: F,
)
pub fn register_host_fn_variadic<F>( &mut self, namespace: &str, name: &str, f: F, )
Registers a variadic host function under namespace::name.
Unlike Engine::register_host_fn, the closure receives every argument
as a &[Value] slice of any length and returns a single Value (or a
HostError to raise in the script), so arguments of mixed /
dynamically-typed shape can cross the boundary without a fixed Rust
signature. The host block must declare the function with a ...
argument list:
host "app" {
log(...);
}No arity or per-argument type checking is performed at the call site;
the closure interprets the slice it is handed. A non-variadic
declaration bound to a variadic closure (or vice versa) is a clean
Diagnostic at Engine::compile time.
Sourcepub fn register_macro<F>(&mut self, name: &str, expander: F)
pub fn register_macro<F>(&mut self, name: &str, expander: F)
Registers the expander for name!( ... ) invocations in the scripts this
engine compiles.
The expander receives the raw text between the parentheses, which
candela does not interpret, and returns candela source for one
expression that is parsed in its place. A MacroError it returns
instead becomes a compile error at the macro, at the byte offset into
the region the error names.
use candela::macros::MacroError;
let mut engine = candela::Engine::new();
engine.register_macro("rows", |body: &str| {
Ok::<String, MacroError>(body.lines().count().to_string())
});Sourcepub const fn allow_unknown_macros(&mut self, allow: bool)
pub const fn allow_unknown_macros(&mut self, allow: bool)
Sets what a macro with no registered expander does: fail the compile
naming it (the default), or, when allow is true, compile as null.
Tooling that reads scripts written for a host it is not part of turns this on, so the host’s macros do not read as errors.
Sourcepub fn check(
&self,
src: &str,
filename: &str,
) -> Result<Vec<Diagnostic>, Diagnostic>
pub fn check( &self, src: &str, filename: &str, ) -> Result<Vec<Diagnostic>, Diagnostic>
Type-checks src the way Engine::compile does, without producing a
program, and returns the warnings the compile raised.
Every function whose parameters are all annotated is checked at those
types, and a function a host would call with a bare parameter is
checked with it typed any, as compile does. What this leaves out is
everything a check does not need: a dylib block binds the signatures
it declares without opening its library, so a script whose C library a
build step has not produced yet still checks; host blocks are not
matched against registered closures; main is not required and
nothing runs.
§Errors
Returns the Diagnostic of the first error: a parse or type error, a
body that does not compile at its declared parameter types, or a dylib
signature naming a type C cannot represent.
Sourcepub fn compile(&self, src: &str, filename: &str) -> Result<Program, Diagnostic>
pub fn compile(&self, src: &str, filename: &str) -> Result<Program, Diagnostic>
Compiles src into a reusable Program, binding every host function
it declares to the matching registered closure.
Every function in src whose parameters are all annotated is compiled at
those declared types, whether or not anything calls it, so an error in
the body of a function main never calls comes back from here rather
than from the first Program::call. A function with a bare parameter
that nothing in the script calls is checked with that parameter typed
any, and warned about; the warnings come back on
Program::warnings. A function an import brought in stays lazy.
main is executed once here (module instantiation), so any top-level
setup runs before the host makes its first Program::call. The
entry-point check runs first, so a broken body is reported before any
top-level side effect.
§Errors
Returns a Diagnostic if the script fails to parse/type-check, if the
body of an annotated function does not compile at its declared parameter
types, if the script declares no main, if a declared host function
has no registered closure, if a registered closure’s arity/types
disagree with the host block, or if running main raises a runtime
error.
Trait Implementations§
Auto Trait Implementations§
impl !RefUnwindSafe for Engine
impl !Send for Engine
impl !Sync for Engine
impl !UnwindSafe for Engine
impl Freeze for Engine
impl Unpin for Engine
impl UnsafeUnpin for Engine
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> Paint for Twhere
T: ?Sized,
impl<T> Paint for Twhere
T: ?Sized,
§fn fg(&self, value: Color) -> Painted<&T>
fn fg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the foreground set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like red() and
green(), which have the same functionality but are
pithier.
§Example
Set foreground color to white using fg():
use yansi::{Paint, Color};
painted.fg(Color::White);Set foreground color to white using white().
use yansi::Paint;
painted.white();§fn bright_black(&self) -> Painted<&T>
fn bright_black(&self) -> Painted<&T>
§fn bright_red(&self) -> Painted<&T>
fn bright_red(&self) -> Painted<&T>
§fn bright_green(&self) -> Painted<&T>
fn bright_green(&self) -> Painted<&T>
§fn bright_yellow(&self) -> Painted<&T>
fn bright_yellow(&self) -> Painted<&T>
§fn bright_blue(&self) -> Painted<&T>
fn bright_blue(&self) -> Painted<&T>
§fn bright_magenta(&self) -> Painted<&T>
fn bright_magenta(&self) -> Painted<&T>
§fn bright_cyan(&self) -> Painted<&T>
fn bright_cyan(&self) -> Painted<&T>
§fn bright_white(&self) -> Painted<&T>
fn bright_white(&self) -> Painted<&T>
§fn bg(&self, value: Color) -> Painted<&T>
fn bg(&self, value: Color) -> Painted<&T>
Returns a styled value derived from self with the background set to
value.
This method should be used rarely. Instead, prefer to use color-specific
builder methods like on_red() and
on_green(), which have the same functionality but
are pithier.
§Example
Set background color to red using fg():
use yansi::{Paint, Color};
painted.bg(Color::Red);Set background color to red using on_red().
use yansi::Paint;
painted.on_red();§fn on_primary(&self) -> Painted<&T>
fn on_primary(&self) -> Painted<&T>
§fn on_magenta(&self) -> Painted<&T>
fn on_magenta(&self) -> Painted<&T>
§fn on_bright_black(&self) -> Painted<&T>
fn on_bright_black(&self) -> Painted<&T>
§fn on_bright_red(&self) -> Painted<&T>
fn on_bright_red(&self) -> Painted<&T>
§fn on_bright_green(&self) -> Painted<&T>
fn on_bright_green(&self) -> Painted<&T>
§fn on_bright_yellow(&self) -> Painted<&T>
fn on_bright_yellow(&self) -> Painted<&T>
§fn on_bright_blue(&self) -> Painted<&T>
fn on_bright_blue(&self) -> Painted<&T>
§fn on_bright_magenta(&self) -> Painted<&T>
fn on_bright_magenta(&self) -> Painted<&T>
§fn on_bright_cyan(&self) -> Painted<&T>
fn on_bright_cyan(&self) -> Painted<&T>
§fn on_bright_white(&self) -> Painted<&T>
fn on_bright_white(&self) -> Painted<&T>
§fn attr(&self, value: Attribute) -> Painted<&T>
fn attr(&self, value: Attribute) -> Painted<&T>
Enables the styling [Attribute] value.
This method should be used rarely. Instead, prefer to use
attribute-specific builder methods like bold() and
underline(), which have the same functionality
but are pithier.
§Example
Make text bold using attr():
use yansi::{Paint, Attribute};
painted.attr(Attribute::Bold);Make text bold using using bold().
use yansi::Paint;
painted.bold();§fn rapid_blink(&self) -> Painted<&T>
fn rapid_blink(&self) -> Painted<&T>
§fn quirk(&self, value: Quirk) -> Painted<&T>
fn quirk(&self, value: Quirk) -> Painted<&T>
Enables the yansi [Quirk] value.
This method should be used rarely. Instead, prefer to use quirk-specific
builder methods like mask() and
wrap(), which have the same functionality but are
pithier.
§Example
Enable wrapping using .quirk():
use yansi::{Paint, Quirk};
painted.quirk(Quirk::Wrap);Enable wrapping using wrap().
use yansi::Paint;
painted.wrap();§fn clear(&self) -> Painted<&T>
👎Deprecated since 1.0.1: renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
fn clear(&self) -> Painted<&T>
renamed to resetting() due to conflicts with Vec::clear().
The clear() method will be removed in a future release.
§fn whenever(&self, value: Condition) -> Painted<&T>
fn whenever(&self, value: Condition) -> Painted<&T>
Conditionally enable styling based on whether the [Condition] value
applies. Replaces any previous condition.
See the crate level docs for more details.
§Example
Enable styling painted only when both stdout and stderr are TTYs:
use yansi::{Paint, Condition};
painted.red().on_yellow().whenever(Condition::STDOUTERR_ARE_TTY);