inc 0.1.3

Incremental approach to compiler construction
//! Core types shared by most of the program
use colored::Colorize;
use std::fmt;

/// Abstract Syntax Tree for a single expression
#[derive(Debug, PartialEq, Clone)]
pub enum Expr {
    // An empty list `()`
    Nil,
    // 61b number with a 3bit tag
    Number(i64),
    // #t & #f
    Boolean(bool),
    // A unicode char encoded in UTF-8 can take upto 4 bytes and won't fit in a
    // word; so this implementation makes sense only for ASCII.
    Char(u8),
    // UTF-8 Strings
    Str(String),
    // Scheme Identifiers
    Identifier(String),
    // Symbols
    Symbol(String),
    // Since Rust needs to know the size of the Expr type upfront, we need an
    // indirection here with `Vec<>` for recursive types. In this context, Vec
    // is just a convenient way to have a `Box<[Expr]>`
    List(Vec<Expr>),
    // Conditional
    Cond { pred: Box<Expr>, then: Box<Expr>, alt: Option<Box<Expr>> },
    // Variable bindings
    Let { bindings: Vec<(String, Expr)>, body: Vec<Expr> },
    // Functions
    Lambda(Code),
}

/// Code is a refinement type for Expression specialized for lambdas
#[derive(Debug, PartialEq, Clone)]
pub struct Code {
    // A rose by any other name would smell as sweet
    pub name: Option<String>,
    // Formal arguments to the function, filled in by the parser
    pub formals: Vec<String>,
    // Free variables, added post closure conversion
    pub free: Vec<String>,
    // A body is a list of expressions evaluated in order
    pub body: Vec<Expr>,
}

/// Pretty print an Expr
impl fmt::Display for Expr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Expr::Nil => write!(f, "()"),
            Expr::Number(n) => write!(f, "{}", n),
            Expr::Boolean(t) => write!(f, "{}", if *t { "#t" } else { "#f" }),
            Expr::Char(c) => write!(f, "{}", c),
            Expr::Str(s) => write!(f, "\"{}\"", s),
            Expr::Identifier(i) => write!(f, "{}", i),
            Expr::Symbol(i) => write!(f, "'{}", i),
            Expr::List(l) => {
                write!(f, "(")?;
                for i in l {
                    write!(f, "{} ", i)?;
                }
                write!(f, ")")
            }
            Expr::Cond { pred, then, alt } => match alt {
                None => write!(f, "(if {} {})", pred, then),
                Some(t) => write!(f, "(if {} {} {})", pred, then, t),
            },
            Expr::Let { bindings, body } => {
                write!(f, "(let (")?;
                bindings.iter().for_each(|(a, b)| write!(f, "({} {})", a, b).unwrap());
                write!(f, ") ")?;
                body.iter().for_each(|b| write!(f, "{}", b).unwrap());
                write!(f, ")")
            }
            Expr::Lambda(Code { formals, body, .. }) => {
                write!(f, "(λ (")?;
                formals.iter().for_each(|arg| write!(f, "{}", arg).unwrap());
                write!(f, ") ")?;
                body.iter().for_each(|b| write!(f, "{}", b).unwrap());
                write!(f, ")")
            }
        }
    }
}

/// Idiomatic type conversions from the primitive types to Expr
///
/// https://doc.rust-lang.org/rust-by-example/conversion/from_into.html
/// https://ricardomartins.cc/2016/08/03/convenient_and_idiomatic_conversions_in_rust
impl From<i64> for Expr {
    fn from(i: i64) -> Self {
        Expr::Number(i)
    }
}

impl From<bool> for Expr {
    fn from(b: bool) -> Self {
        Expr::Boolean(b)
    }
}

impl From<char> for Expr {
    fn from(c: char) -> Self {
        Expr::Char(c as u8)
    }
}

impl From<&str> for Expr {
    fn from(i: &str) -> Self {
        Expr::Identifier(String::from(i))
    }
}

/// Control behavior and external interaction of the program.
pub struct Config {
    /// Program is the input source
    pub program: String,
    /// Name of the generated asm and executable, stdout otherwise
    pub output: String,
}

impl Config {
    pub fn asm(&self) -> String {
        let stdout = String::from("/dev/stdout");
        if self.output == stdout {
            stdout
        } else {
            format!("{}.s", self.output)
        }
    }
}

/// Custom error type for all of inc
// See these links for more context on how custom error types work in Rust.
// - https://learning-rust.github.io/docs/e7.custom_error_types.html
// - https://rust-lang-nursery.github.io/cli-wg/tutorial/errors.html
#[derive(Debug)]
pub enum Error<'a> {
    // Errors returned by nom
    Parser(nom::Err<(&'a str, nom::error::ErrorKind)>),
    // Internal errors are unexpected errors within the compiler
    Internal { message: String, e: Option<std::io::Error> },
    // Runtime errors in scheme like an undefined variable
    Runtime(String),
    // Compilation errors in Scheme like missing functions and type errors
    Compilation(String),
}

// Implement std::convert::From for Error; from io::Error
impl<'a> From<std::io::Error> for Error<'a> {
    fn from(error: std::io::Error) -> Self {
        Error::Internal { message: String::from(""), e: Some(error) }
    }
}

impl<'a> fmt::Display for Error<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Parser(e) => {
                writeln!(f, "{}\n", "Failed to parse program".red().bold())?;
                writeln!(f, "{:?}", e)
            }
            Self::Internal { message, e } => {
                writeln!(f, "{}\n", "Something went wrong!".red().bold())?;
                writeln!(f, "{}", message)?;
                writeln!(f, "{:?}", e)
            }
            Self::Runtime(e) => {
                writeln!(f, "{}\n", "Runtime error!".red().bold())?;
                writeln!(f, "{:?}", e)
            }
            Self::Compilation(e) => {
                writeln!(f, "{}\n", "Failed to compile program".red().bold())?;
                writeln!(f, "{:?}", e)
            }
        }
    }
}