rain-lang 0.0.1

An implementation of an RVSDG in Rust with a concept of lifetimes
Documentation
/*!
AST-to-`rain`-IR conversion and related utilities
*/
use std::convert::Infallible;
use std::borrow::Borrow;
use smallvec::{smallvec, SmallVec};
use std::hash::Hash;
use super::symbol_table::SymbolTable;
use super::ast::{Expr, Sexpr, Path, Let, Pattern, SimpleAssignment, Scope};
use crate::value::{
    ValueDesc, ValId,
    error::ValueError,
    expr::SexprArgs
};

/// A rain IR builder which consumes an AST and outputs IR, along with handling errors
#[derive(Debug, Clone)]
pub struct Builder<S: Eq + Hash> {
    symbols: SymbolTable<S, ValId>
}

/// Errors building `rain` IR
#[derive(Debug, Clone)]
pub enum BuilderError<'a> {
    /// A value construction error
    ValueError(ValueError),
    /// Symbol not defined
    SymbolUndefined(&'a str),
    /// Build not implemented
    BuildNotImplemented
}

impl<'a> From<ValueError> for BuilderError<'a> {
    fn from(err: ValueError) -> BuilderError<'a> { BuilderError::ValueError(err) }
}

impl<'a> From<Infallible> for BuilderError<'a> {
    fn from(err: Infallible) -> BuilderError<'a> { match err {} }
}

impl<'a> BuilderError<'a> {
    /// Get the builder error corresponding to a given value error
    pub fn val<E: Into<ValueError>>(err: E) -> BuilderError<'a>
    { BuilderError::ValueError(err.into()) }
}

/// The size of a small vector of definitions
pub const SMALL_DEFINITIONS: usize = 1;

/// A symbol definition, along with the previous definition, if any
#[derive(Debug, Clone)]
pub struct Definition<'a> {
    /// The name of the symbol defined
    pub name: &'a str,
    /// The value it is defined to have
    pub value: ValId,
    /// The old value it had before, if any
    pub previous: Option<ValId>
}

impl<'a, S: Eq + Hash + From<&'a str> + Borrow<str>> Builder<S> {
    /// Create a new IR builder
    pub fn new() -> Builder<S> {
        Builder {
            symbols: SymbolTable::new()
        }
    }
    /// Build a let expression, registering the symbols defined in the current scope
    /// Returns a list of symbols defined
    pub fn build_let(&mut self, let_statement: &Let<'a>) ->
    Result<SmallVec<[Definition<'a>; SMALL_DEFINITIONS]>, BuilderError<'a>> {
        self.assign_pattern(&let_statement.pattern, &let_statement.expr)
    }
    /// Assign an expression to a pattern
    /// Returns a list of symbols defined, along with previous definitions (if any)
    pub fn assign_pattern(&mut self, pattern: &Pattern<'a>, expr: &Expr<'a>) ->
    Result<SmallVec<[Definition<'a>; SMALL_DEFINITIONS]>, BuilderError<'a>> {
        match pattern {
            Pattern::Simple(simple_assignment) =>
                self.build_simple_assign(simple_assignment, expr)
                    .map(|assigned| smallvec![assigned])
        }
    }
    /// Build a simple assignment.
    pub fn build_simple_assign(&mut self, simple: &SimpleAssignment<'a>, expr: &Expr<'a>)
    -> Result<Definition<'a>, BuilderError<'a>> {
        //TODO: typing
        let name = simple.name;
        let value = self.build_expr(expr)?;
        let previous = self.symbols.def(S::from(name), value.clone());
        Ok(Definition{ name, value, previous })
    }
    /// Build the value for an expression
    pub fn build_expr(&mut self, expr: &Expr<'a>) -> Result<ValId, BuilderError<'a>> {
        use Expr::*;
        match expr {
            Bool(b) => b.to_node(),
            BoolTy(b) => b.to_node(),
            LogicalOp(l) => l.to_node(),
            Path(p) => self.lookup_path(p),
            Sexpr(s) => self.build_sexpr(s),
            Scope(s) => self.build_scope(s),
            _ => Err(BuilderError::BuildNotImplemented)
        }
    }
    /// Build the value for a scope
    pub fn build_scope(&mut self, scope: &Scope<'a>) -> Result<ValId, BuilderError<'a>> {
        self.symbols.push(); // Push a new scope
        // Build definitions
        for def in scope.definitions.iter() { self.build_let(def)?; }
        // Build value, if any
        let res = if let Some(value) = &scope.value {
            self.build_expr(value)
        } else {
            //TODO: module return
            Err(BuilderError::BuildNotImplemented)
        };
        self.symbols.pop(); // Pop the scope
        res
    }
    /// Look up a path
    pub fn lookup_path(&self, path: &Path<'a>) -> Result<ValId, BuilderError<'a>> {
        if path.names.len() == 1 {
            let name = path.names[0];
            self.symbols.get(name).cloned().ok_or(BuilderError::SymbolUndefined(name))
        } else {
            Err(BuilderError::BuildNotImplemented)
        }
    }
    /// Build the value for an S-expression
    pub fn build_sexpr(&mut self, sexpr: &Sexpr<'a>) -> Result<ValId, BuilderError<'a>> {
        let args = self.build_sexpr_args(sexpr)?;
        let sexpr = args.normalize().map_err(BuilderError::ValueError)?;
        sexpr.to_node()
    }
    /// Build the arguments for an S-expression
    pub fn build_sexpr_args(&mut self, sexpr: &Sexpr<'a>) -> Result<SexprArgs, BuilderError<'a>> {
        let mut ops = SmallVec::new();
        for op in sexpr.ops.iter().map(|op| self.build_expr(op)) {
            match op {
                Ok(op) => ops.push(op),
                Err(err) => return Err(err)
            }
        }
        Ok(SexprArgs(ops))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::{parse_expr, parse_statement};

    #[test]
    fn simple_logical_expressions_build_properly() {
        let mut builder = Builder::<&str>::new();
        let t = &ValId::from(true);
        let f = &ValId::from(false);
        let logical_expressions = [
            ("(#and #true #false)", f),
            ("(#or #true #false)", t),
            ("(#not #true)", f),
            ("(#not #false)", t),
            ("(#xor #true #true)", f),
            ("(#or (#or #false #true) (#xor #false #false))", t)
        ];
        for (expr, result) in logical_expressions.iter() {
            let expr = match parse_expr(expr) {
                Ok((rest, expr)) => {
                    assert_eq!(
                        rest, "",
                        "Did not parse all of valid expression \"{}\": remainder = {:?}",
                        expr, rest
                    );
                    expr
                },
                Err(err) => panic!("Got parse error {:?} for valid expression \"{}\"", err, expr)
            };
            let built = match builder.build_expr(&expr) {
                Ok(built) => built,
                Err(err) => panic!("Valid expression \"{}\" gives build error: {:#?}", expr, err)
            };
            assert_eq!(&built, *result);
        }
    }

    #[test]
    fn simple_assignments_build_properly() {
        let mut builder = Builder::<&str>::new();
        let program = [
            "let false = (#or #false #false);",
            "let true = (#and #true (#and #true #true));",
            "let x = (#xor true false);",
            "let y = (#xor true true);"
        ];
        for line in program.iter() {
            let l = match parse_statement(line) {
                Ok((rest, l)) => {
                    assert_eq!(
                        rest, "",
                        "Unparsed portion {:?} of valid let \"{}\"",
                        rest, line
                    );
                    l
                },
                Err(err) =>
                    panic!("Valid let \"{}\" gives parse error: {:#?}", line, err)
            };
            match builder.build_let(&l) {
                Ok(_) => {},
                Err(err) => panic!("Valid let \"{}\" gives build error: {:#?}", line, err)
            };
        }
        let t = &ValId::from(true);
        let f = &ValId::from(false);
        let variables = [
            ("true", t), ("false", f), ("x", t), ("y", f)
        ];
        for (variable, value) in variables.iter() {
            match builder.build_expr(&parse_expr(variable).expect("Valid identifier").1) {
                Ok(built) => assert_eq!(
                    &built, *value,
                    "Invalid variable assignment {} = {} != {}", variable, built, value),
                Err(err) => panic!("Error looking up variable {}: {:#?}", variable, err)
            }
        }
    }
}