gluon 0.4.1

A static, type inferred programming language for application embedding
Documentation
//! Advanced compiler pipeline which ensures that the compilation phases are run in order even if
//! not the entire compilation procedure is needed.
//!
//! Each trait in this module represents a stage in a full compilation so to only run compilation
//! up until and including typechecking the `Typecheckable` trait can be used. Furthermore, if
//! compilation should continue at some point after typechecking has succeeded, the result of
//! typechecking (`TypecheckValue`) can be used as input to the next stage, ensuring that it is
//! difficult to forget a stage.

use std::borrow::{Borrow, BorrowMut};

use base::ast::SpannedExpr;
use base::error::InFile;
use base::types::ArcType;
use base::source::Source;
use base::symbol::{Name, NameBuf, Symbol, SymbolModule};

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

use {Compiler, Error, Result};

/// Result type of successful macro expansion
pub struct MacroValue<E> {
    pub expr: E,
}

pub trait MacroExpandable {
    type Expr: BorrowMut<SpannedExpr<Symbol>>;

    fn expand_macro(self,
                    compiler: &mut Compiler,
                    thread: &Thread,
                    file: &str)
                    -> Result<MacroValue<Self::Expr>>
        where Self: Sized
    {
        let mut macros = MacroExpander::new(thread);
        let expr = self.expand_macro_with(compiler, &mut macros, file)?;
        macros.finish()?;
        Ok(expr)
    }

    fn expand_macro_with(self,
                         compiler: &mut Compiler,
                         macros: &mut MacroExpander,
                         file: &str)
                         -> Result<MacroValue<Self::Expr>>;
}

impl<'s> MacroExpandable for &'s str {
    type Expr = SpannedExpr<Symbol>;

    fn expand_macro_with(self,
                         compiler: &mut Compiler,
                         macros: &mut MacroExpander,
                         file: &str)
                         -> Result<MacroValue<Self::Expr>> {

        compiler
            .parse_expr(file, self)
            .map_err(From::from)
            .and_then(|mut expr| {
                          expr.expand_macro_with(compiler, macros, file)?;
                          Ok(MacroValue { expr: expr })
                      })
    }
}

impl<'s> MacroExpandable for &'s mut SpannedExpr<Symbol> {
    type Expr = &'s mut SpannedExpr<Symbol>;

    fn expand_macro_with(self,
                         compiler: &mut Compiler,
                         macros: &mut MacroExpander,
                         file: &str)
                         -> Result<MacroValue<Self::Expr>> {
        if compiler.implicit_prelude {
            compiler.include_implicit_prelude(file, self);
        }
        macros.run(self);
        Ok(MacroValue { expr: self })
    }
}

/// Result type of successful typechecking
pub struct TypecheckValue<E> {
    pub expr: E,
    pub typ: ArcType,
}

pub trait Typecheckable: Sized {
    type Expr: BorrowMut<SpannedExpr<Symbol>>;

    fn typecheck(self,
                 compiler: &mut Compiler,
                 thread: &Thread,
                 file: &str,
                 expr_str: &str)
                 -> Result<TypecheckValue<Self::Expr>> {
        self.typecheck_expected(compiler, thread, file, expr_str, None)
    }
    fn typecheck_expected(self,
                          compiler: &mut Compiler,
                          thread: &Thread,
                          file: &str,
                          expr_str: &str,
                          expected_type: Option<&ArcType>)
                          -> Result<TypecheckValue<Self::Expr>>;
}

impl<T> Typecheckable for T
    where T: MacroExpandable
{
    type Expr = T::Expr;

    fn typecheck_expected(self,
                          compiler: &mut Compiler,
                          thread: &Thread,
                          file: &str,
                          expr_str: &str,
                          expected_type: Option<&ArcType>)
                          -> Result<TypecheckValue<Self::Expr>> {

        self.expand_macro(compiler, thread, file)
            .and_then(|expr| {
                          expr.typecheck_expected(compiler, thread, file, expr_str, expected_type)
                      })
    }
}

impl<E> Typecheckable for MacroValue<E>
    where E: BorrowMut<SpannedExpr<Symbol>>
{
    type Expr = E;

    fn typecheck_expected(mut self,
                          compiler: &mut Compiler,
                          thread: &Thread,
                          file: &str,
                          expr_str: &str,
                          expected_type: Option<&ArcType>)
                          -> Result<TypecheckValue<Self::Expr>> {
        use check::typecheck::Typecheck;

        let env = thread.get_env();
        let mut tc = Typecheck::new(file.into(), &mut compiler.symbols, &*env);

        let typ = tc.typecheck_expr_expected(self.expr.borrow_mut(), expected_type)
            .map_err(|err| InFile::new(file, expr_str, err))?;

        Ok(TypecheckValue {
               expr: self.expr,
               typ: typ,
           })
    }
}

/// Result of successful compilation
pub struct CompileValue<E> {
    pub expr: E,
    pub typ: ArcType,
    pub function: CompiledFunction,
}

pub trait Compileable<Extra> {
    type Expr;

    fn compile(self,
               compiler: &mut Compiler,
               thread: &Thread,
               file: &str,
               expr_str: &str,
               arg: Extra)
               -> Result<CompileValue<Self::Expr>>;
}
impl<'a, 'b, T> Compileable<Option<&'b ArcType>> for T
    where T: Typecheckable
{
    type Expr = T::Expr;

    fn compile(self,
               compiler: &mut Compiler,
               thread: &Thread,
               file: &str,
               expr_str: &str,
               expected_type: Option<&'b ArcType>)
               -> Result<CompileValue<Self::Expr>> {

        self.typecheck_expected(compiler, thread, file, expr_str, expected_type)
            .and_then(|tc_value| tc_value.compile(compiler, thread, file, expr_str, ()))
    }
}
impl<E, Extra> Compileable<Extra> for TypecheckValue<E>
    where E: Borrow<SpannedExpr<Symbol>>
{
    type Expr = E;

    fn compile(self,
               compiler: &mut Compiler,
               thread: &Thread,
               filename: &str,
               expr_str: &str,
               _: Extra)
               -> Result<CompileValue<Self::Expr>> {
        use vm::compiler::Compiler;
        debug!("Compile `{}`", filename);
        let mut function = {
            let env = thread.get_env();
            let name = Name::new(filename);
            let name = NameBuf::from(name.module());
            let symbols = SymbolModule::new(String::from(AsRef::<str>::as_ref(&name)),
                                            &mut compiler.symbols);
            let source = Source::new(expr_str);
            let mut compiler = Compiler::new(&*env,
                                             thread.global_env(),
                                             symbols,
                                             &source,
                                             filename.to_string(),
                                             compiler.emit_debug_info);
            compiler.compile_expr(self.expr.borrow())?
        };
        function.id = Symbol::from(filename);
        Ok(CompileValue {
               expr: self.expr,
               typ: self.typ,
               function: function,
           })
    }
}

/// Result of successful execution
pub struct ExecuteValue<'vm, E> {
    pub expr: E,
    pub typ: ArcType,
    pub value: RootedValue<&'vm Thread>,
}

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

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

    fn load_script(self,
                   compiler: &mut Compiler,
                   vm: &'vm Thread,
                   filename: &str,
                   expr_str: &str,
                   arg: Extra)
                   -> BoxFutureValue<'vm, (), Error>;
}
impl<'vm, C, Extra> Executable<'vm, Extra> for C
    where C: Compileable<Extra>,
          C::Expr: BorrowMut<SpannedExpr<Symbol>> + Send + 'vm
{
    type Expr = C::Expr;

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

        match self.compile(compiler, vm, name, expr_str, arg) {
            Ok(v) => v.run_expr(compiler, vm, name, expr_str, ()),
            Err(err) => FutureValue::Value(Err(err)),
        }
    }
    fn load_script(self,
                   compiler: &mut Compiler,
                   vm: &'vm Thread,
                   filename: &str,
                   expr_str: &str,
                   arg: Extra)
                   -> BoxFutureValue<'vm, (), Error> {

        match self.compile(compiler, vm, filename, expr_str, arg) {
            Ok(v) => v.load_script(compiler, vm, filename, expr_str, ()),
            Err(err) => FutureValue::Value(Err(err)),
        }
    }
}
impl<'vm, E> Executable<'vm, ()> for CompileValue<E>
    where E: BorrowMut<SpannedExpr<Symbol>> + Send + 'vm
{
    type Expr = E;

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

        let CompileValue {
            expr,
            typ,
            mut function,
        } = self;
        function.id = Symbol::from(name);
        let closure = try_future!(vm.global_env().new_global_thunk(function));
        vm.call_thunk(closure)
            .map(|(vm, value)| {
                     ExecuteValue {
                         expr: expr,
                         typ: typ,
                         value: vm.root_value(value),
                     }
                 })
            .map_err(Error::from)
            .boxed()
    }
    fn load_script(self,
                   _compiler: &mut Compiler,
                   vm: &'vm Thread,
                   filename: &str,
                   _expr_str: &str,
                   _: ())
                   -> BoxFutureValue<'vm, (), Error> {
        use check::metadata;

        let CompileValue {
            mut expr,
            typ,
            function,
        } = self;
        let metadata = metadata::metadata(&*vm.get_env(), expr.borrow_mut());
        let closure = try_future!(vm.global_env().new_global_thunk(function));
        let filename = filename.to_string();
        vm.call_thunk(closure)
            .map_err(Error::from)
            .and_then(move |(_, value)| {
                          try_future!(vm.set_global(closure.function.name.clone(),
                                                    typ,
                                                    metadata,
                                                    value));
                          info!("Loaded module `{}` filename", filename);
                          FutureValue::sync(Ok(()))
                      })
            .boxed()
    }
}