pub struct Engine { /* private fields */ }
Expand description
Start here! An execution engine. Contains information about things like globals, registered types, etc.
Implementations§
source§impl Engine
impl Engine
sourcepub fn new() -> Self
pub fn new() -> Self
Creates a new engine using the default core library.
Although Engine::with_debug_options
and likewise Engine::with_corelib
can panic
if the core library is much too big, the official core library is not big enough to cause
this.
Examples
use mica::Engine;
let mut engine = Engine::new();
sourcepub fn with_corelib(corelib: impl CoreLibrary) -> Self
pub fn with_corelib(corelib: impl CoreLibrary) -> Self
Creates a new engine with an alternative core library.
Panics
Constructing the engine can panic if the core library defines too many globals, functions,
methods, or the like. See Engine::with_debug_options
for more remarks.
Examples
use mica::Engine;
let mut engine = Engine::with_corelib(mica::corelib::Lib);
sourcepub fn with_debug_options(
corelib: impl CoreLibrary,
debug_options: DebugOptions
) -> Self
pub fn with_debug_options(
corelib: impl CoreLibrary,
debug_options: DebugOptions
) -> Self
Creates a new engine with specific debug options.
Engine::new
and Engine::with_corelib
create engines with Default
debug options,
and should generally be preferred unless you’re debugging the language’s internals.
Panics
Constructing the engine can panic if the core library defines too many globals, functions, methods, or the like. In reality this is only really a problem if you let users control the amount of methods registered by your core library (which you should never, ever do.)
Examples
use mica::{Engine, DebugOptions};
// Create a loud engine that prints a bunch of debugging information to stdout.
let mut engine = Engine::with_debug_options(mica::corelib::Lib, DebugOptions {
dump_ast: true,
dump_bytecode: true,
});
sourcepub fn compile(
&mut self,
filename: impl AsRef<str>,
source: impl Into<String>
) -> Result<Script<'_>, Error>
pub fn compile(
&mut self,
filename: impl AsRef<str>,
source: impl Into<String>
) -> Result<Script<'_>, Error>
Compiles a script without executing it.
The filename is used for reporting compilation errors and in stack traces.
Examples
use mica::Engine;
let mut engine = Engine::new();
let script = engine.compile("example.mi", "2 + 2")?;
sourcepub fn start(
&mut self,
filename: impl AsRef<str>,
source: impl Into<String>
) -> Result<Fiber<'_>, Error>
pub fn start(
&mut self,
filename: impl AsRef<str>,
source: impl Into<String>
) -> Result<Fiber<'_>, Error>
sourcepub fn call<T>(
&mut self,
function: Value,
arguments: impl IntoIterator<Item = Value>
) -> Result<T, Error>where
T: TryFromValue,
pub fn call<T>(
&mut self,
function: Value,
arguments: impl IntoIterator<Item = Value>
) -> Result<T, Error>where
T: TryFromValue,
Calls a function with the given arguments.
The function is called in a new fiber, and execution is trampolined until the fiber finishes executing.
Examples
use mica::{Engine, Value};
let mut engine = Engine::new();
let f: Value = engine.start("example.mi", r#" (func (x) = x + 2) "#)?.trampoline()?;
let result: f64 = engine.call(f, [Value::new(1.0)])?;
assert_eq!(result, 3.0);
sourcepub fn method_id(
&mut self,
signature: impl MethodSignature
) -> Result<MethodId, Error>
pub fn method_id(
&mut self,
signature: impl MethodSignature
) -> Result<MethodId, Error>
Returns the unique ID of a method with a given name and arity.
Note that there can only exist about 65 thousand unique method signatures. This is usually not a problem as method names often repeat between types. Also note that unlike functions, a method can only accept up to 255 arguments. Which, to be quite frankly honest, should be enough for anyone.
Examples
use mica::Engine;
let mut engine = Engine::new();
// Two identical calls to method_id always return the same ID.
let m_to_string_1 = engine.method_id(("to_string", 0))?;
let m_to_string_2 = engine.method_id(("to_string", 0))?;
let m_pi = engine.method_id(("pi", 0))?;
assert_eq!(m_to_string_1, m_to_string_2);
assert_ne!(m_to_string_1, m_pi);
sourcepub fn call_method<T>(
&mut self,
receiver: Value,
signature: impl MethodSignature,
arguments: impl IntoIterator<Item = Value>
) -> Result<T, Error>where
T: TryFromValue,
pub fn call_method<T>(
&mut self,
receiver: Value,
signature: impl MethodSignature,
arguments: impl IntoIterator<Item = Value>
) -> Result<T, Error>where
T: TryFromValue,
Calls a method on a receiver with the given arguments.
Note that if you’re calling a method often, it’s cheaper to precompute the method signature
into a MethodId
by using the method_id
function, compared to
passing a (name, arity) pair every single time.
Examples
use mica::{Engine, Value};
let mut engine = Engine::new();
let example = engine
.start(
"dog.mi",
r#" struct Example impl
func new() constructor = nil
end "#
)?
.trampoline()?;
let instance: Value = engine.call_method(example, ("new", 0), [])?;
assert!(matches!(instance, Value::Struct(..)));
sourcepub fn create_value(&self, from: impl IntoValue) -> Value
pub fn create_value(&self, from: impl IntoValue) -> Value
Creates a new value that is potentially user data.
User data values need to retrieve type information from the engine upon their creation,
which prevents them from being created by Value::new
which does not possess the same
information.
Also of note is that because the type information is retrieved during creation, so the type must be registered inside the engine by then; otherwise you’ll get an opaque user data value.
This needn’t be used for user data returned from functions added into the VM, because the engine automatically does the full conversion under the hood.
Examples
use mica::{Engine, TypeBuilder, UserData, Value};
struct Cell {
value: Value,
}
impl UserData for Cell {}
let mut engine = Engine::new();
engine.add_type(
TypeBuilder::<Cell>::new("Cell")
.add_static("new", |value| Cell { value })
.add_function("value", |cell: &Cell| cell.value.clone()),
)?;
// The following will not work, because `Value::new` does not have access to
// Mica type information:
// let cell = Value::new(Cell { value: Value::new(1.0) });
// The following though, will:
let cell = engine.create_value(Cell { value: Value::new(1.0) });
engine.set("one", cell)?;
let okay: Result<Value, _> = engine
.start("okay.mi", "assert(one.value == 1)")?
.trampoline();
assert!(okay.is_ok());
sourcepub fn global_id(&mut self, name: impl GlobalName) -> Result<GlobalId, Error>
pub fn global_id(&mut self, name: impl GlobalName) -> Result<GlobalId, Error>
Returns the unique global ID for the global with the given name, or an error if there are too many globals in scope.
The maximum amount of globals is about 16 million, so you shouldn’t worry too much about hitting that limit unless you’re stress-testing the VM or accepting untrusted input as globals.
Examples
use mica::Engine;
let mut engine = Engine::new();
// Two identical calls to global_id always return the same ID.
let g_print_1 = engine.global_id("print")?;
let g_print_2 = engine.global_id("print")?;
let g_debug = engine.global_id("debug")?;
assert_eq!(g_print_1, g_print_2);
assert_ne!(g_print_1, g_debug);
sourcepub fn set(
&mut self,
id: impl GlobalName,
value: impl IntoValue
) -> Result<(), Error>
pub fn set(
&mut self,
id: impl GlobalName,
value: impl IntoValue
) -> Result<(), Error>
Sets a global variable that’ll be available to scripts executed by the engine.
The id
parameter can be either an &str
or a prefetched global_id
.
Examples
use mica::{Engine, Value};
let mut engine = Engine::new();
engine.set("x", 1.0_f64);
let _: Value = engine
.start("assertion.mi", "assert(x == 1)")?
.trampoline()?;
sourcepub fn get<T>(&self, id: impl OptionalGlobalName) -> Result<T, Error>where
T: TryFromValue,
pub fn get<T>(&self, id: impl OptionalGlobalName) -> Result<T, Error>where
T: TryFromValue,
Returns the value of a global variable, or nil
if it’s not set.
The id
parameter can be either an &str
or a prefetched global_id
.
Examples
use mica::{Engine, Value};
let mut engine = Engine::new();
let _: Value = engine
.start("assertion.mi", "x = 1")?
.trampoline()?;
let x: f64 = engine.get("x")?;
assert_eq!(x, 1.0);
sourcepub fn add_raw_function(
&mut self,
name: &str,
parameter_count: impl Into<FunctionParameterCount>,
f: FunctionKind
) -> Result<(), Error>
pub fn add_raw_function(
&mut self,
name: &str,
parameter_count: impl Into<FunctionParameterCount>,
f: FunctionKind
) -> Result<(), Error>
Declares a “raw” function in the global scope. Raw functions do not perform any type checks by default and accept a variable number of arguments.
You should generally prefer add_function
instead of this.
Note that this cannot accept GlobalId
s, because a name is required to create the
function and global IDs have their name erased.
parameter_count
should reflect the parameter count of the function. Pass None
if the
function accepts a variable number of arguments. Note that because this function omits type
checks you may receive a different amount of arguments than specified.
Examples
use mica::{Engine, Value};
use mica::ll::{bytecode::FunctionKind, value::RawValue};
let mut engine = Engine::new();
engine.add_raw_function(
"a_raw_understanding",
0,
FunctionKind::Foreign(Box::new(|env, gc, arguments| {
Ok(RawValue::from(1.0))
})),
);
let _: Value = engine
.start("assertion.mi", "assert(a_raw_understanding() == 1.0)")?
.trampoline()?;
sourcepub fn add_function<F, V>(&mut self, name: &str, f: F) -> Result<(), Error>where
V: BareMaybeVarargs,
F: ForeignFunction<V, ParameterCount = FunctionParameterCount>,
pub fn add_function<F, V>(&mut self, name: &str, f: F) -> Result<(), Error>where
V: BareMaybeVarargs,
F: ForeignFunction<V, ParameterCount = FunctionParameterCount>,
Declares a function in the global scope.
Examples
use mica::Engine;
let mut engine = Engine::new();
engine.add_function("add", |x: f64, y: f64| x + y);
let x: f64 = engine
.start("example.mi", "add(1, 2)")?
.trampoline()?;
assert_eq!(x, 3.0);