Embedding API
Neptune lang can be embedded in any rust application. The complete API can be browsed through docs.rs. Some key terms that must known are
Embedder Functions
Embedder Functions aka Efuncs are functions that are created by the embedder. The EFunc can push or pop values from the stack using the EFuncContext that is passed to it. EFuncs may be synchronous or asynchronous.
The ToNeptuneValue
trait indicates a type that can be converted to a Neptune value. The
trait is implemented for i32, String and other common data types.
It can be implemented for any type
struct Point {
x: i32,
y: i32
}
impl ToNeptuneValue for Point {
fn to_neptune_value(self, cx: &mut EFuncContext) {
cx.object(); // push an empty object to the stack
cx.int(self.x); // push self.x to the stack
cx.set_object_property("x").unwrap(); // pop self.x and set it as property x
self.y.to_neptune_value(cx); // an alternate way to push to the stack
cx.set_object_property("y").unwrap();
}
}
EFuncs must return Result
where both variants satisfy the ToNeptuneValue
trait. The Err
variant can be returned to throw an exception.
Methods of EFuncContext like as_int
return Err(EFuncError)
on error. To return
NeptuneError
(the Error
class of Neptune) or EFuncError we can use the
EFuncErrorOr enum.
use neptune_lang::*;
let n = VM::new(NoopModuleLoader);
n.create_efunc("inverse", |cx /*: &mut EFuncContext*/ | -> Result<f64,EFuncErrorOr<NeptuneError>> {
// pop an int from the stack
let i = cx.as_int()?;
if i == 0 {
// It would be better to create our own Error type and implement ToNeptuneValue for it.
Err(EFuncErrorOr::Other(NeptuneError("Cannot divide by zero".into())))
}else{
Ok(1.0 / (i as f64))
}
}).unwrap();
EFuncs can be called using the ecall
function in the vm
module.
const {ecall} = import('vm')
ecall(@inverse,0.5) //2.0
Asynchronous efuncs return a Future<Result<T1,T2>>
.
vm.create_efunc_async("sleep", |cx| {
let time = cx.as_int();
async move {
sleep(Duration::from_millis(time? as u64)).await;
Result::<(), EFuncError>::Ok(())
}
})
Resources
Resources are opaque handles to rust values. They can be freed from neptune
using the close()
method. They can be created and used only from efuncs. The
Resource
wrapper struct can be used to return resources
use std::fs::File;
use std::io::prelude::*;
use neptune_lang::*;
n.create_efunc(
"file_open",
|cx| -> Result<Resource<File>, EFuncErrorOr<NeptuneError>> {
Ok(Resource(File::open(cx.as_string()?).or(Err(
EFuncErrorOr::Other(NeptuneError("Error opening file".into())),
))?))
},
)
.unwrap();
n.create_efunc(
"file_read_all",
|cx| -> Result<String, EFuncErrorOr<NeptuneError>> {
let mut contents = String::new();
cx.as_resource::<File>()?
.read_to_string(&mut contents)
.or(Err(EFuncErrorOr::Other(NeptuneError(
"Error reading file".into(),
))))?;
Ok(contents)
},
)
.unwrap();