Checkpoint
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
use crate::eval::primtype::PrimitiveType;
|
||||
use crate::eval::value::Value;
|
||||
|
||||
use super::primtype::{UnknownPrimType, ValuePrimitiveTypeError};
|
||||
|
||||
/// Errors that can occur running primitive operations in the evaluators.
|
||||
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
|
||||
pub enum PrimOpError {
|
||||
@@ -14,7 +16,7 @@ pub enum PrimOpError {
|
||||
/// This variant covers when an operator must take a particular
|
||||
/// type, but the user has provided a different one.
|
||||
#[error("Bad type for operator {0}: {1}")]
|
||||
BadTypeFor(&'static str, Value),
|
||||
BadTypeFor(String, Value),
|
||||
/// Probably obvious from the name, but just to be very clear: this
|
||||
/// happens when you pass three arguments to a two argument operator,
|
||||
/// etc. Technically that's a type error of some sort, but we split
|
||||
@@ -28,8 +30,10 @@ pub enum PrimOpError {
|
||||
from: PrimitiveType,
|
||||
to: PrimitiveType,
|
||||
},
|
||||
#[error("Unknown primitive type {0}")]
|
||||
UnknownPrimType(String),
|
||||
#[error(transparent)]
|
||||
UnknownPrimType(#[from] UnknownPrimType),
|
||||
#[error(transparent)]
|
||||
ValuePrimitiveTypeError(#[from] ValuePrimitiveTypeError),
|
||||
}
|
||||
|
||||
// Implementing primitives in an interpreter like this is *super* tedious,
|
||||
@@ -63,7 +67,7 @@ impl Value {
|
||||
Value::I16(x) => Ok(Value::I16(x.wrapping_neg())),
|
||||
Value::I32(x) => Ok(Value::I32(x.wrapping_neg())),
|
||||
Value::I64(x) => Ok(Value::I64(x.wrapping_neg())),
|
||||
_ => Err(PrimOpError::BadTypeFor("-", value.clone())),
|
||||
_ => Err(PrimOpError::BadTypeFor("-".to_string(), value.clone())),
|
||||
},
|
||||
_ => Err(PrimOpError::BadArgCount(operation.to_owned(), 1)),
|
||||
}
|
||||
@@ -135,6 +139,9 @@ impl Value {
|
||||
right.clone(),
|
||||
)),
|
||||
},
|
||||
Value::Function(_, _) => {
|
||||
Err(PrimOpError::BadTypeFor(operation.to_string(), left.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,17 +31,28 @@ impl Display for PrimitiveType {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Value> for PrimitiveType {
|
||||
fn from(value: &Value) -> Self {
|
||||
#[derive(Clone, Debug, PartialEq, thiserror::Error)]
|
||||
pub enum ValuePrimitiveTypeError {
|
||||
#[error("Could not convert function value to primitive type (possible function name: {0:?}")]
|
||||
CannotConvertFunction(Option<String>),
|
||||
}
|
||||
|
||||
impl<'a> TryFrom<&'a Value> for PrimitiveType {
|
||||
type Error = ValuePrimitiveTypeError;
|
||||
|
||||
fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
Value::I8(_) => PrimitiveType::I8,
|
||||
Value::I16(_) => PrimitiveType::I16,
|
||||
Value::I32(_) => PrimitiveType::I32,
|
||||
Value::I64(_) => PrimitiveType::I64,
|
||||
Value::U8(_) => PrimitiveType::U8,
|
||||
Value::U16(_) => PrimitiveType::U16,
|
||||
Value::U32(_) => PrimitiveType::U32,
|
||||
Value::U64(_) => PrimitiveType::U64,
|
||||
Value::I8(_) => Ok(PrimitiveType::I8),
|
||||
Value::I16(_) => Ok(PrimitiveType::I16),
|
||||
Value::I32(_) => Ok(PrimitiveType::I32),
|
||||
Value::I64(_) => Ok(PrimitiveType::I64),
|
||||
Value::U8(_) => Ok(PrimitiveType::U8),
|
||||
Value::U16(_) => Ok(PrimitiveType::U16),
|
||||
Value::U32(_) => Ok(PrimitiveType::U32),
|
||||
Value::U64(_) => Ok(PrimitiveType::U64),
|
||||
Value::Function(name, _) => {
|
||||
Err(ValuePrimitiveTypeError::CannotConvertFunction(name.clone()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,8 +72,14 @@ impl From<ConstantType> for PrimitiveType {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
|
||||
pub enum UnknownPrimType {
|
||||
#[error("Could not convert '{0}' into a primitive type")]
|
||||
UnknownPrimType(String),
|
||||
}
|
||||
|
||||
impl FromStr for PrimitiveType {
|
||||
type Err = PrimOpError;
|
||||
type Err = UnknownPrimType;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
@@ -74,7 +91,7 @@ impl FromStr for PrimitiveType {
|
||||
"u16" => Ok(PrimitiveType::U16),
|
||||
"u32" => Ok(PrimitiveType::U32),
|
||||
"u64" => Ok(PrimitiveType::U64),
|
||||
_ => Err(PrimOpError::UnknownPrimType(s.to_string())),
|
||||
_ => Err(UnknownPrimType::UnknownPrimType(s.to_owned())),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,7 +169,7 @@ impl PrimitiveType {
|
||||
(PrimitiveType::I64, Value::I64(x)) => Ok(Value::I64(*x)),
|
||||
|
||||
_ => Err(PrimOpError::UnsafeCast {
|
||||
from: source.into(),
|
||||
from: PrimitiveType::try_from(source)?,
|
||||
to: *self,
|
||||
}),
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
use std::fmt::Display;
|
||||
use super::EvalError;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// Values in the interpreter.
|
||||
///
|
||||
/// Yes, this is yet another definition of a structure called `Value`, which
|
||||
/// are almost entirely identical. However, it's nice to have them separated
|
||||
/// by type so that we don't mix them up.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[derive(Clone)]
|
||||
pub enum Value {
|
||||
I8(i8),
|
||||
I16(i16),
|
||||
@@ -15,19 +17,79 @@ pub enum Value {
|
||||
U16(u16),
|
||||
U32(u32),
|
||||
U64(u64),
|
||||
Function(
|
||||
Option<String>,
|
||||
Rc<dyn Fn(Vec<Value>) -> Result<Value, EvalError>>,
|
||||
),
|
||||
}
|
||||
|
||||
impl Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
fn format_value(value: &Value, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match value {
|
||||
Value::I8(x) => write!(f, "{}i8", x),
|
||||
Value::I16(x) => write!(f, "{}i16", x),
|
||||
Value::I32(x) => write!(f, "{}i32", x),
|
||||
Value::I64(x) => write!(f, "{}i64", x),
|
||||
Value::U8(x) => write!(f, "{}u8", x),
|
||||
Value::U16(x) => write!(f, "{}u16", x),
|
||||
Value::U32(x) => write!(f, "{}u32", x),
|
||||
Value::U64(x) => write!(f, "{}u64", x),
|
||||
Value::Function(Some(name), _) => write!(f, "<function {}>", name),
|
||||
Value::Function(None, _) => write!(f, "<function>"),
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
format_value(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
format_value(self, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Value {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
match self {
|
||||
Value::I8(x) => write!(f, "{}i8", x),
|
||||
Value::I16(x) => write!(f, "{}i16", x),
|
||||
Value::I32(x) => write!(f, "{}i32", x),
|
||||
Value::I64(x) => write!(f, "{}i64", x),
|
||||
Value::U8(x) => write!(f, "{}u8", x),
|
||||
Value::U16(x) => write!(f, "{}u16", x),
|
||||
Value::U32(x) => write!(f, "{}u32", x),
|
||||
Value::U64(x) => write!(f, "{}u64", x),
|
||||
Value::I8(x) => match other {
|
||||
Value::I8(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::I16(x) => match other {
|
||||
Value::I16(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::I32(x) => match other {
|
||||
Value::I32(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::I64(x) => match other {
|
||||
Value::I64(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::U8(x) => match other {
|
||||
Value::U8(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::U16(x) => match other {
|
||||
Value::U16(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::U32(x) => match other {
|
||||
Value::U32(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::U64(x) => match other {
|
||||
Value::U64(y) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::Function(Some(x), _) => match other {
|
||||
Value::Function(Some(y), _) => x == y,
|
||||
_ => false,
|
||||
},
|
||||
Value::Function(None, _) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user