From 1f5d544feef0805bd98241cd1de4ed11b3951919 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gabrielle=20Guimar=C3=A3es=20de=20Oliveira?= Date: Thu, 23 Nov 2023 00:20:46 -0300 Subject: [PATCH] refactor: improve eval code --- src/eval.rs | 60 ++++++++++++++++++++++++++++++++++++++++++------- src/lib.rs | 6 +++++ src/semantic.rs | 2 +- 3 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/eval.rs b/src/eval.rs index 7430180..e259a49 100644 --- a/src/eval.rs +++ b/src/eval.rs @@ -4,10 +4,14 @@ use std::{ sync::{Arc, RwLock}, }; +use thiserror::Error; use Trampoline::{Continue, Done, Raise}; use crate::{ - semantic::{defmacro::keyword, Expr, Literal}, + semantic::{ + defmacro::{keyword, soft_vec}, + Expr, Literal, + }, SrcPos, Term, }; @@ -76,7 +80,7 @@ pub enum Value { Nil, } -#[derive(Hash, Clone, PartialEq, Eq)] +#[derive(Debug, Hash, Clone, PartialEq, Eq)] pub struct Keyword { pub name: String, pub is_atom: bool, @@ -137,12 +141,12 @@ impl Trampoline { } impl TryFrom for Keyword { - type Error = Expr; + type Error = ExpansionError; fn try_from(value: Value) -> Result { match value { Value::Keyword(keyword) => Ok(keyword), - _ => Err(keyword!("eval.error/keyword-expected")), + _ => Err(ExpansionError::ExpectedKeyword), } } } @@ -202,6 +206,21 @@ fn fun_expand(fun: crate::semantic::Fun, environment: &Environment) -> Result for Expr { + fn from(error: ExpansionError) -> Self { + match error { + ExpansionError::ExpectedKeyword => keyword!("eval.error/expected-keyword"), + } + } +} + impl Expr { /// Expand the expression into a value. pub fn expand(self, environment: &Environment) -> Result { @@ -268,13 +287,38 @@ impl Expr { } } +/// Errors that can occur during evaluation. +#[derive(Error, Debug, Clone)] +pub enum EvalError { + #[error("undefined keyword")] + UndefinedKeyword(Keyword), + + #[error("expected fun")] + ExpectedFun, + + #[error("expected atomic")] + ExpectedAtomic, +} + +impl From for Expr { + fn from(value: EvalError) -> Self { + match value { + EvalError::UndefinedKeyword(Keyword { name, .. }) => { + soft_vec!(keyword!("eval.error/expected-keyword"), name) + } + EvalError::ExpectedFun => keyword!("eval.error/expected-fun"), + EvalError::ExpectedAtomic => keyword!("eval.error/expected-atomic"), + } + } +} + impl Value { /// Evaluate the expression into a value. pub fn eval(self, environment: &Environment) -> Trampoline { match self { Value::Deref { box value, .. } => { let Value::Atomic(atomic) = value else { - bail!(keyword!("eval.error/atomic-expected")) + bail!(EvalError::ExpectedAtomic) }; let guard = atomic.read().expect("poisoned atomic"); @@ -282,7 +326,7 @@ impl Value { } Value::Set { box target, value } => { let Value::Atomic(atomic) = target else { - bail!(keyword!("eval.error/atomic-expected")) + bail!(EvalError::ExpectedAtomic) }; let mut guard = atomic.write().expect("poisoned atomic"); *guard = value.eval(environment)?.clone(); @@ -291,7 +335,7 @@ impl Value { Value::Keyword(keyword) if !keyword.is_atom => { match environment.find_definition(keyword.clone()) { Some(Definition { value, .. }) => Done(value), - None => Done(Value::Keyword(keyword)), + None => bail!(EvalError::UndefinedKeyword(keyword)), } } Value::Apply { callee, arguments } => match callee.eval(environment)? { @@ -302,7 +346,7 @@ impl Value { } fun.call(environment, new_arguments) } - _ => bail!(keyword!("eval.error/fun-expected")), + _ => bail!(EvalError::ExpectedFun), }, Value::List(old_elements) => { let mut new_elements = Vec::new(); diff --git a/src/lib.rs b/src/lib.rs index 8f511f8..0055720 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,12 @@ impl Display for Term { } } +impl From for Term { + fn from(s: String) -> Self { + Term::String(s) + } +} + impl From for Term { fn from(n: usize) -> Self { Term::Int(n as u64) diff --git a/src/semantic.rs b/src/semantic.rs index 7834406..c44c4ac 100644 --- a/src/semantic.rs +++ b/src/semantic.rs @@ -460,5 +460,5 @@ macro_rules! keyword { use define_ast; use define_builtin; -use soft_vec; pub(crate) use keyword; +pub(crate) use soft_vec;