Skip to content

Commit

Permalink
feat(vm): Make the compiler pipeline return futures for executing
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Jan 28, 2017
1 parent 9def999 commit c0d355e
Show file tree
Hide file tree
Showing 5 changed files with 194 additions and 72 deletions.
73 changes: 39 additions & 34 deletions src/compiler_pipeline.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

//! Advanced compiler pipeline which ensures that the compilation phases are run in order even if
//! not the entire compilation procedure is needed.
//!
Expand All @@ -19,10 +18,11 @@ use base::source::Source;
use base::symbol::{Name, NameBuf, Symbol, SymbolModule};

use vm::compiler::CompiledFunction;
use vm::future::BoxFutureValue;
use vm::macros::MacroExpander;
use vm::thread::{RootedValue, Thread, ThreadInternal};

use {Compiler, Result};
use {Compiler, Error, Result};

/// Result type of successful macro expansion
pub struct MacroValue<E> {
Expand Down Expand Up @@ -239,16 +239,17 @@ pub struct ExecuteValue<'vm, E> {
pub value: RootedValue<&'vm Thread>,
}

pub trait Executable<Extra> {
pub trait Executable<'vm, Extra> {
type Expr;

fn run_expr<'vm>(self,
compiler: &mut Compiler,
vm: &'vm Thread,
name: &str,
expr_str: &str,
arg: Extra)
-> Result<ExecuteValue<'vm, Self::Expr>>;
fn run_expr(self,
compiler: &mut Compiler,
vm: &'vm Thread,
name: &str,
expr_str: &str,
arg: Extra)
-> Result<BoxFutureValue<'vm, ExecuteValue<'vm, Self::Expr>, Error>>;

fn load_script(self,
compiler: &mut Compiler,
vm: &Thread,
Expand All @@ -257,19 +258,19 @@ pub trait Executable<Extra> {
arg: Extra)
-> Result<()>;
}
impl<C, Extra> Executable<Extra> for C
impl<'vm, C, Extra> Executable<'vm, Extra> for C
where C: Compileable<Extra>,
C::Expr: BorrowMut<SpannedExpr<Symbol>>,
C::Expr: BorrowMut<SpannedExpr<Symbol>> + Send + 'vm,
{
type Expr = C::Expr;

fn run_expr<'vm>(self,
compiler: &mut Compiler,
vm: &'vm Thread,
name: &str,
expr_str: &str,
arg: Extra)
-> Result<ExecuteValue<'vm, Self::Expr>> {
fn run_expr(self,
compiler: &mut Compiler,
vm: &'vm Thread,
name: &str,
expr_str: &str,
arg: Extra)
-> Result<BoxFutureValue<'vm, ExecuteValue<'vm, Self::Expr>, Error>> {

self.compile(compiler, vm, name, expr_str, arg)
.and_then(|v| v.run_expr(compiler, vm, name, expr_str, ()))
Expand All @@ -286,28 +287,32 @@ impl<C, Extra> Executable<Extra> for C
.and_then(|v| v.load_script(compiler, vm, filename, expr_str, ()))
}
}
impl<E> Executable<()> for CompileValue<E>
where E: BorrowMut<SpannedExpr<Symbol>>,
impl<'vm, E> Executable<'vm, ()> for CompileValue<E>
where E: BorrowMut<SpannedExpr<Symbol>> + Send + 'vm,
{
type Expr = E;

fn run_expr<'vm>(self,
_compiler: &mut Compiler,
vm: &'vm Thread,
name: &str,
_expr_str: &str,
_: ())
-> Result<ExecuteValue<'vm, Self::Expr>> {
fn run_expr(self,
_compiler: &mut Compiler,
vm: &'vm Thread,
name: &str,
_expr_str: &str,
_: ())
-> Result<BoxFutureValue<'vm, ExecuteValue<'vm, Self::Expr>, Error>> {

let CompileValue { expr, typ, mut function } = self;
function.id = Symbol::from(name);
let closure = vm.global_env().new_global_thunk(function)?;
let (_, value) = vm.call_thunk(closure)?.wait()?;
Ok(ExecuteValue {
expr: expr,
typ: typ,
value: vm.root_value_ref(value),
})
Ok(vm.call_thunk(closure)?
.map(|(vm, value)| {
ExecuteValue {
expr: expr,
typ: typ,
value: vm.root_value_ref(value),
}
})
.map_err(Error::from)
.boxed())
}
fn load_script(self,
_compiler: &mut Compiler,
Expand Down
73 changes: 43 additions & 30 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use check::typecheck::TypeError;
use vm::Variants;
use vm::api::{Getable, Hole, VmType, OpaqueValue};
use vm::Error as VmError;
use vm::future::FutureValue;
use vm::compiler::CompiledFunction;
use vm::thread::ThreadInternal;
use vm::macros;
Expand Down Expand Up @@ -260,14 +261,17 @@ impl Compiler {
where T: Getable<'vm> + VmType,
{
let expected = T::make_type(vm);
let ExecuteValue { typ: actual, value, .. } =
expr_str.run_expr(self, vm, name, expr_str, Some(&expected))?;
unsafe {
match T::from_value(vm, Variants::new(&value)) {
Some(value) => Ok((value, actual)),
None => Err(Error::from(VmError::WrongType(expected, actual))),
}
}
expr_str.run_expr(self, vm, name, expr_str, Some(&expected))?
.and_then(|v| {
let ExecuteValue { typ: actual, value, .. } = v;
unsafe {
FutureValue::sync(match T::from_value(vm, Variants::new(&value)) {
Some(value) => Ok((value, actual)),
None => Err(Error::from(VmError::WrongType(expected, actual))),
})
}
})
.wait()
}

/// Compiles and runs `expr_str`. If the expression is of type `IO a` the action is evaluated
Expand All @@ -281,29 +285,38 @@ impl Compiler {
T::Type: Sized,
{
let expected = T::make_type(vm);
let ExecuteValue { typ: actual, value, .. } =
expr_str.run_expr(self, vm, name, expr_str, Some(&expected))?;
let is_io = {
expected.alias_ident()
.and_then(|expected_ident| {
let env = vm.get_env();
env.find_type_info("IO")
.ok()
.map(|alias| *expected_ident == alias.name)
expr_str.run_expr(self, vm, name, expr_str, Some(&expected))?
.and_then(move |v| {
let ExecuteValue { typ: actual, value, .. } = v;
let is_io = {
expected.alias_ident()
.and_then(|expected_ident| {
let env = vm.get_env();
env.find_type_info("IO")
.ok()
.map(|alias| *expected_ident == alias.name)
})
.unwrap_or(false)
};
if is_io {
match vm.execute_io(*value) {
Ok(future) => {
future.map(move |(_, value)| (value, expected, actual))
.map_err(Error::from)
}
Err(err) => FutureValue::Value(Err(err.into())),
}
} else {
FutureValue::Value(Ok((*value, expected, actual)))
}
})
.and_then(|(value, expected, actual)| unsafe {
FutureValue::sync(match T::from_value(vm, Variants::new(&value)) {
Some(value) => Ok((value, actual)),
None => Err(Error::from(VmError::WrongType(expected, actual))),
})
.unwrap_or(false)
};
let value = if is_io {
vm.execute_io(*value)?.wait()?.1
} else {
*value
};
unsafe {
match T::from_value(vm, Variants::new(&value)) {
Some(value) => Ok((value, actual)),
None => Err(Error::from(VmError::WrongType(expected, actual))),
}
}
})
.wait()
}

fn include_implicit_prelude(&mut self, name: &str, expr: &mut SpannedExpr<Symbol>) {
Expand Down
114 changes: 109 additions & 5 deletions vm/src/future.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use futures::{Async, Future, Poll};
use futures::{AndThen, Async, Future, Map, MapErr, OrElse, Poll, Then};
use futures::future::{Either, FutureResult};

use Error;

Expand All @@ -8,18 +9,20 @@ use Error;
pub enum FutureValue<F>
where F: Future,
{
Value(F::Item),
Value(Result<F::Item, F::Error>),
Future(F),
Polled,
}

pub type BoxFutureValue<'a, T, E> = FutureValue<Box<Future<Item = T, Error = E> + Send + 'a>>;

impl<F> FutureValue<F>
where F: Future<Error = Error>,
{
/// Returns the resolved `value` if it was synchronously computed or an error otherwise
pub fn sync_or_error(self) -> Result<F::Item, Error> {
match self {
FutureValue::Value(v) => Ok(v),
FutureValue::Value(v) => v,
FutureValue::Future(_) => {
Err(Error::Message("Future needs to be resolved asynchronously".into()))
}
Expand All @@ -30,6 +33,107 @@ impl<F> FutureValue<F>
}
}

impl<T, E> FutureValue<FutureResult<T, E>> {
pub fn sync(result: Result<T, E>) -> FutureValue<FutureResult<T, E>> {
FutureValue::Value(result)
}
}

impl<'f, F> FutureValue<F>
where F: Future + 'f,
{
pub fn boxed(self) -> FutureValue<Box<Future<Item = F::Item, Error = F::Error> + Send + 'f>>
where F: Send,
{
match self {
FutureValue::Value(v) => FutureValue::Value(v),
FutureValue::Future(f) => FutureValue::Future(Box::new(f)),
FutureValue::Polled => FutureValue::Polled,
}
}

pub fn map<G, U>(self, g: G) -> FutureValue<Map<F, G>>
where G: FnOnce(F::Item) -> U,
{
match self {
FutureValue::Value(v) => FutureValue::Value(v.map(g)),
FutureValue::Future(f) => FutureValue::Future(f.map(g)),
FutureValue::Polled => FutureValue::Polled,
}
}

pub fn map_err<G, U>(self, g: G) -> FutureValue<MapErr<F, G>>
where G: FnOnce(F::Error) -> U,
{
match self {
FutureValue::Value(v) => FutureValue::Value(v.map_err(g)),
FutureValue::Future(f) => FutureValue::Future(f.map_err(g)),
FutureValue::Polled => FutureValue::Polled,
}
}

pub fn and_then<G, B>(self, g: G) -> FutureValue<Either<B, AndThen<F, FutureValue<B>, G>>>
where G: FnOnce(F::Item) -> FutureValue<B>,
B: Future<Error = F::Error>,
{
match self {
FutureValue::Value(v) => {
match v {
Ok(v) => {
match g(v) {
FutureValue::Value(v) => FutureValue::Value(v),
FutureValue::Future(f) => FutureValue::Future(Either::A(f)),
FutureValue::Polled => FutureValue::Polled,
}
}
Err(err) => FutureValue::Value(Err(err)),
}
}
FutureValue::Future(f) => FutureValue::Future(Either::B(f.and_then(g))),
FutureValue::Polled => FutureValue::Polled,
}
}

pub fn then<G, B>(self, g: G) -> FutureValue<Either<B, Then<F, FutureValue<B>, G>>>
where G: FnOnce(Result<F::Item, F::Error>) -> FutureValue<B>,
B: Future<Error = F::Error>,
{
match self {
FutureValue::Value(v) => {
match g(v) {
FutureValue::Value(v) => FutureValue::Value(v),
FutureValue::Future(f) => FutureValue::Future(Either::A(f)),
FutureValue::Polled => FutureValue::Polled,
}
}
FutureValue::Future(f) => FutureValue::Future(Either::B(f.then(g))),
FutureValue::Polled => FutureValue::Polled,
}
}

pub fn or_else<G, B>(self, g: G) -> FutureValue<Either<B, OrElse<F, FutureValue<B>, G>>>
where G: FnOnce(F::Error) -> FutureValue<B>,
B: Future<Item = F::Item>,
{
match self {
FutureValue::Value(v) => {
match v {
Ok(v) => FutureValue::Value(Ok(v)),
Err(err) => {
match g(err) {
FutureValue::Value(v) => FutureValue::Value(v),
FutureValue::Future(f) => FutureValue::Future(Either::A(f)),
FutureValue::Polled => FutureValue::Polled,
}
}
}
}
FutureValue::Future(f) => FutureValue::Future(Either::B(f.or_else(g))),
FutureValue::Polled => FutureValue::Polled,
}
}
}

impl<F> Future for FutureValue<F>
where F: Future,
{
Expand All @@ -40,7 +144,7 @@ impl<F> Future for FutureValue<F>
match *self {
FutureValue::Value(_) => {
match ::std::mem::replace(self, FutureValue::Polled) {
FutureValue::Value(v) => return Ok(Async::Ready(v)),
FutureValue::Value(v) => return v.map(Async::Ready),
_ => unreachable!(),
}
}
Expand All @@ -53,7 +157,7 @@ impl<F> Future for FutureValue<F>

fn wait(self) -> Result<F::Item, F::Error> {
match self {
FutureValue::Value(v) => Ok(v),
FutureValue::Value(v) => v,
FutureValue::Future(f) => f.wait(),
FutureValue::Polled => {
panic!("`FutureValue` may not be polled again if it contained a value")
Expand Down
2 changes: 1 addition & 1 deletion vm/src/primitives.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use std::result::Result as StdResult;

use {Variants, Error};
use primitives as prim;
use api::{generic, Generic, Getable, Pushable, Array, RuntimeResult, primitive, WithVM};
use api::{generic, Generic, Getable, AsyncPushable, Array, RuntimeResult, primitive, WithVM};
use api::generic::A;
use gc::{Gc, Traverseable, DataDef, WriteOnly};
use Result;
Expand Down
4 changes: 2 additions & 2 deletions vm/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -606,7 +606,7 @@ impl ThreadInternal for Thread {
context.stack.push(Closure(closure));
context.borrow_mut().enter_scope(0, State::Closure(closure));
context.execute().map(|async| match async {
Async::Ready(context) => FutureValue::Value((self, context.unwrap().stack.pop())),
Async::Ready(context) => FutureValue::Value(Ok((self, context.unwrap().stack.pop()))),
Async::NotReady => FutureValue::Future(Execute::new(self)),
})
}
Expand Down Expand Up @@ -637,7 +637,7 @@ impl ThreadInternal for Thread {
}
}
let _ = context.exit_scope();
Ok(FutureValue::Value((self, result)))
Ok(FutureValue::Value(Ok((self, result))))
}

/// Calls a function on the stack.
Expand Down

0 comments on commit c0d355e

Please sign in to comment.