use std::fmt;
use std::sync::mpsc;
use crate::Locker;
use std::thread;
use crate::{
exp, exp_assert, CallSnapshot, Environment, Exception, ExceptionValue as EV, SourcePosition,
Value,
};
#[derive(Debug, Clone)]
pub struct Expression {
value: Value,
source: Option<SourcePosition>,
}
impl PartialEq for Expression {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
impl Expression {
pub fn new(value: Value) -> Self {
Self {
value,
source: None,
}
}
pub fn with_source(mut self, source_position: SourcePosition) -> Self {
self.source = Some(source_position);
self
}
pub fn nil() -> Self {
Self {
value: Value::List(vec![]),
source: None,
}
}
pub fn t() -> Self {
Self {
value: Value::True,
source: None,
}
}
pub fn get_value(&self) -> &Value {
&self.value
}
pub fn into_value(self) -> Value {
self.value
}
pub fn source(&self) -> &'_ Option<SourcePosition> {
&self.source
}
pub fn eval_async(
self,
parent_snapshot: Locker<CallSnapshot>,
env: Locker<Environment>,
) -> Result<mpsc::Receiver<Result<Self, Exception>>, Exception> {
let exp = self;
let snap = parent_snapshot.clone();
let (sender, receiver) = mpsc::channel::<Result<Self, Exception>>();
if let Ok(th) = thread::Builder::new()
.stack_size(64 * 1024 * 1024)
.spawn(move || {
sender.send(exp.eval(snap, env)).unwrap();
})
{
match th.join() {
Ok(_) => {}
Err(_) => {
return Err(Exception::new(
EV::Concurrency,
Some(parent_snapshot),
Some("could not wait for thread to finish executing".to_string()),
))
}
}
};
Ok(receiver)
}
pub fn eval(
&self,
parent_snapshot: Locker<CallSnapshot>,
env: Locker<Environment>,
) -> Result<Self, Exception> {
use Value::*;
let snapshot = CallSnapshot::new(&self, &parent_snapshot)?;
let snap = || snapshot.clone();
match &self.value {
List(vals) => {
if !vals.is_empty() {
let operator = vals.get(0).unwrap();
let arguments: Vec<&Expression> = vals.iter().skip(1).collect();
match &operator.value {
Operator(operand) => operand.apply(snapshot, arguments, self, env),
List(_) | Symbol(_) => {
let evaled_operator = operator.eval(snap(), env.clone())?;
let mut new_list = vec![evaled_operator];
for arg in arguments {
new_list.push(arg.clone());
}
Expression::new(Value::List(new_list)).eval(snap(), env)
}
Lambda(function) | Macro(function) => {
let mut scoped_env = match &operator.value {
Lambda(_) => Environment::root()
.with_parent(function.lexical_scope.clone(), None),
Macro(_) => {
let mut env =
Environment::root().with_parent(env.clone(), None);
env.add_parent(function.lexical_scope.clone(), None);
env
}
_ => unreachable!(),
};
if function.collapse_input {
let sym = function.params.get(0).unwrap(); let args_evaled = {
let mut list = Vec::new();
for arg_expr in arguments {
list.push(match &operator.value {
Lambda { .. } => arg_expr.eval(snap(), env.clone())?,
Macro { .. } => arg_expr.clone(),
_ => unreachable!(),
});
}
list
};
let arg = Expression::new(Value::List(args_evaled));
scoped_env.assign(sym.clone(), arg, true, snap())?;
} else {
exp_assert!(
function.params.len() == arguments.len(),
EV::ArgumentMismatch(
arguments.len(),
format!("{}", function.params.len())
),
snap()
);
for (symbol, arg_expr) in
function.params.iter().zip(arguments.into_iter())
{
let arg_evaled = match &operator.value {
Lambda { .. } => arg_expr.eval(snap(), env.clone())?,
Macro { .. } => arg_expr.clone(),
_ => unreachable!(),
};
scoped_env.assign(symbol.clone(), arg_evaled, true, snap())?;
}
}
if let Macro { .. } = &operator.value {
scoped_env = scoped_env.shadow();
};
let mut result = Expression::nil();
let scoped_env_lock = Locker::new(scoped_env);
for exp in &function.expressions {
result = exp.eval(snap(), scoped_env_lock.clone())?;
}
Ok(result)
}
val => exp!(EV::InvalidOperator(val.clone()), snapshot),
}
} else {
Ok(self.clone())
}
}
Symbol(sym) => match env
.read()
.expect("unable to access environment (are threads locked?)")
.lookup(&sym)
{
Some(exp) => Ok(exp.read().unwrap().clone()), None => exp!(EV::UndefinedSymbol(sym.clone()), snapshot),
},
_ => Ok(self.clone()),
}
}
}
impl fmt::Display for Expression {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl PartialOrd for Expression {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.value.partial_cmp(&other.value)
}
}