about summary refs log blame commit diff
path: root/src/interpreter/value.rs
blob: 55ba42f9de584af056ad6d3b74286720d385c7af (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                     



                                        
                



                                        

                                            
 

                         
                                
                             
                                 


                        
                       
                  


               
                         


                           










                                                                      





                                                                      
                                                                        




                                                                         

 












                                                          




                             
                              




                                                              
                                                   
                                                                              



         
                  




                                         
                                            
                                                                                   


         
                                                    
         

                                 

                                                             
                                            


                                               




                                                                                               

                                                                   


               


                                         
                                  
 
                                




                                                              
                                 
     
                     





                               

                                    

                                  
                                            


     

                                    

                                             
                                                                   


     

                                    

                                             
                                                                   


     

                                    

                                             
                                                                   


     

                                    

                                             
                                                                   



                  
                                  


                     
                                   




                      
                                   




                     
                                   


                   

                        
                                   


                     
use std::borrow::Cow;
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::ops::{Add, Div, Mul, Neg, Sub};
use std::rc::Rc;
use std::result;

use derive_more::{Deref, From, TryInto};

use super::{Error, Result};
use crate::ast::hir::Expr;
use crate::ast::{FunctionType, Ident, Type};

#[derive(Debug, Clone)]
pub struct Function<'a> {
    pub type_: FunctionType<'a>,
    pub args: Vec<Ident<'a>>,
    pub body: Expr<'a, Type<'a>>,
}

#[derive(From, TryInto)]
#[try_into(owned, ref)]
pub enum Val<'a> {
    Int(i64),
    Float(f64),
    Bool(bool),
    String(Cow<'a, str>),
    Function(Function<'a>),
}

impl<'a> TryFrom<Val<'a>> for String {
    type Error = ();

    fn try_from(value: Val<'a>) -> result::Result<Self, Self::Error> {
        match value {
            Val::String(s) => Ok(s.into_owned()),
            _ => Err(()),
        }
    }
}

impl<'a> fmt::Debug for Val<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Val::Int(x) => f.debug_tuple("Int").field(x).finish(),
            Val::Float(x) => f.debug_tuple("Float").field(x).finish(),
            Val::Bool(x) => f.debug_tuple("Bool").field(x).finish(),
            Val::String(s) => f.debug_tuple("String").field(s).finish(),
            Val::Function(Function { type_, .. }) => {
                f.debug_struct("Function").field("type_", type_).finish()
            }
        }
    }
}

impl<'a> PartialEq for Val<'a> {
    fn eq(&self, other: &Self) -> bool {
        match (self, other) {
            (Val::Int(x), Val::Int(y)) => x == y,
            (Val::Float(x), Val::Float(y)) => x == y,
            (Val::Bool(x), Val::Bool(y)) => x == y,
            (Val::Function(_), Val::Function(_)) => false,
            (_, _) => false,
        }
    }
}

impl<'a> From<u64> for Val<'a> {
    fn from(i: u64) -> Self {
        Self::from(i as i64)
    }
}

impl<'a> Display for Val<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match self {
            Val::Int(x) => x.fmt(f),
            Val::Float(x) => x.fmt(f),
            Val::Bool(x) => x.fmt(f),
            Val::String(s) => write!(f, "{:?}", s),
            Val::Function(Function { type_, .. }) => write!(f, "<{}>", type_),
        }
    }
}

impl<'a> Val<'a> {
    pub fn type_(&self) -> Type {
        match self {
            Val::Int(_) => Type::Int,
            Val::Float(_) => Type::Float,
            Val::Bool(_) => Type::Bool,
            Val::String(_) => Type::CString,
            Val::Function(Function { type_, .. }) => Type::Function(type_.clone()),
        }
    }

    pub fn as_type<'b, T>(&'b self) -> Result<&'b T>
    where
        T: TypeOf + 'b + Clone,
        &'b T: TryFrom<&'b Self>,
    {
        <&T>::try_from(self).map_err(|_| Error::InvalidType {
            actual: self.type_().to_owned(),
            expected: <T as TypeOf>::type_of(),
        })
    }

    pub fn as_function<'b>(&'b self, function_type: FunctionType) -> Result<&'b Function<'a>> {
        match self {
            Val::Function(f) if f.type_ == function_type => Ok(&f),
            _ => Err(Error::InvalidType {
                actual: self.type_().to_owned(),
                expected: Type::Function(function_type.to_owned()),
            }),
        }
    }
}

#[derive(Debug, PartialEq, Clone, Deref)]
pub struct Value<'a>(Rc<Val<'a>>);

impl<'a> Display for Value<'a> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.0.fmt(f)
    }
}

impl<'a, T> From<T> for Value<'a>
where
    Val<'a>: From<T>,
{
    fn from(x: T) -> Self {
        Self(Rc::new(x.into()))
    }
}

impl<'a> Neg for Value<'a> {
    type Output = Result<Value<'a>>;

    fn neg(self) -> Self::Output {
        Ok((-self.as_type::<i64>()?).into())
    }
}

impl<'a> Add for Value<'a> {
    type Output = Result<Value<'a>>;

    fn add(self, rhs: Self) -> Self::Output {
        Ok((self.as_type::<i64>()? + rhs.as_type::<i64>()?).into())
    }
}

impl<'a> Sub for Value<'a> {
    type Output = Result<Value<'a>>;

    fn sub(self, rhs: Self) -> Self::Output {
        Ok((self.as_type::<i64>()? - rhs.as_type::<i64>()?).into())
    }
}

impl<'a> Mul for Value<'a> {
    type Output = Result<Value<'a>>;

    fn mul(self, rhs: Self) -> Self::Output {
        Ok((self.as_type::<i64>()? * rhs.as_type::<i64>()?).into())
    }
}

impl<'a> Div for Value<'a> {
    type Output = Result<Value<'a>>;

    fn div(self, rhs: Self) -> Self::Output {
        Ok((self.as_type::<f64>()? / rhs.as_type::<f64>()?).into())
    }
}

pub trait TypeOf {
    fn type_of() -> Type<'static>;
}

impl TypeOf for i64 {
    fn type_of() -> Type<'static> {
        Type::Int
    }
}

impl TypeOf for bool {
    fn type_of() -> Type<'static> {
        Type::Bool
    }
}

impl TypeOf for f64 {
    fn type_of() -> Type<'static> {
        Type::Float
    }
}

impl TypeOf for String {
    fn type_of() -> Type<'static> {
        Type::CString
    }
}