Plugins
The information on this page lags behind the platform and will be changed soon!
The platform allows creating two types of plugins:
- stateless functions;
- stateful reagents.
The first one can be used to define custom monads/dyads/triads/tetrads/polyads. Let's implement our own add function.
Firstly, make some preparations:
#[macro_use]
extern crate ext;
use ext::{Interpreter, AST};
Define your own function:
#[unwind(allowed)]
extern fn add_fn(lhs: &AST, rhs: &AST, i: &Interpreter) -> AST {
match (lhs.tp(), rhs.tp()) {
(SC_LONG, SC_LONG) => {
i.new_scalar_long(lhs.long() + rhs.long())
}
_ => i.new_signal(i.new_string("nyi"))
}
}
Finally, register a newly created function:
declare_plugin!(
// constructor
|i: &Interpreter| {
// register own function inside interpreter's environment
let sym = i.intern_string("add");
i.insert_entity(sym, i.new_verb_dyad(add_fn));
i.new_string("add registered")
},
// destructor
|| {}
);
Let's test our function through the interpreter:
o)load "libsimple_plugin.so"
"add registered"
o)add[1;2]
3
o)add[1;`2]
nyi
o)1 add 2
3
o)1 add `2
nyi
o)
That's all! Simple, right? Ok, now we are ready to create a stateful reagent. Let it be our own implementation of REPL.
The preparation stage is a bit longer:
use ext::{In, Out, Pipeline, State, AST, Interpreter, ErrorKind};
use mio::unix::EventedFd;
use mio::*;
use rt::rtalloc::ushared::extend_lifetime;
use std::cell::UnsafeCell;
use std::io;
use std::io::Read;
Define the size of buffer for holding data to be read from STDIN:
const BUFFER_SIZE: usize = 64000000;
The core part of each reagent is a PIPE - sender and receiver:
pub struct Receiver {
i: &'static Interpreter,
buf: UnsafeCell<Vec<u8>>,
}
#[derive(Clone)]
pub struct Sender {
i: &'static Interpreter,
}
To make them actual reagents, implement some traits:
impl Out<AST> for Sender {
fn push(&self, v: AST) -> Result<(), ErrorKind> {
self.i.print_format(&v);
Ok(())
}
fn try_push(&self, _v: AST) -> Option<AST> { None }
fn box_clone(&self) -> Box<Out<AST>> { Box::new((*self).clone()) }
}
impl Evented for Receiver {
fn register(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&0).register(poll, token, interest, opts)
}
fn reregister(&self, poll: &Poll, token: Token, interest: Ready, opts: PollOpt) -> io::Result<()> {
EventedFd(&0).reregister(poll, token, interest, opts)
}
fn deregister(&self, poll: &Poll) -> io::Result<()> { EventedFd(&0).deregister(poll) }
}
impl In<AST> for Receiver {
fn pop(&self) -> AST {
unsafe {
let input = self.buf.get();
let size = io::stdin().read(&mut (*input)).expect("STDIN error.");
let ast = self.i.parse(&(*input)[..size]);
self.i.eval(&ast)
}
}
fn try_pop(&self) -> Option<AST> { None }
fn peek(&self) -> Option<&AST> { None }
fn ready(&self, _readiness: Ready) -> State { State::Ready }
}
Define registration function that will be called on reagent creation:
#[unwind(allowed)]
unsafe extern "C" fn simple_reagent(_: &[AST], i: &Interpreter) -> Pipeline<AST> {
let tx = box Sender { i: extend_lifetime(i) };
let rx = box Receiver {
i: extend_lifetime(i),
buf: UnsafeCell::new(vec![0u8; BUFFER_SIZE]),
};
Pipeline::new_async(rx, tx)
}
Almost done, now we need to register our plugin:
declare_plugin!(
// constructor
|i: &Interpreter| {
i.register_reagent("simple", simple_reagent);
i.new_string("simple reagent registered")
},
// destructor
|| {}
);
Finally, we can load our plugin into interpreter:
load "platform/plugins/simple_reagent/target/debug/libsimple_reagent.so";
repl: reagent[`simple];
ps1: {print["o)"]};
out: {repl[x]; ps1[]};
react {[x:repl] out[x]};
ps1[];
join 0;