use super::Operator;
use crate::{
error::{EggError, EggResult},
evaluator::evaluate,
expression::{self, Value},
scope::Scope,
};
use alloc::string::ToString;
pub struct Do;
impl Operator for Do {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
args.iter().try_fold(Value::Nil, |_, nxt| evaluate(nxt, scope))
}
}
pub struct If;
impl Operator for If {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
debug_assert_eq!(args.len(), 3);
let condition = evaluate(&args[0], scope)?;
let value = match condition {
Value::Number(num) => num != 0.0,
Value::Boolean(b) => b,
_ => {
return Err(EggError::OperatorComplaint(
"if(--) expects a boolean (a number that if zero equals false) as it's parameter".to_string(),
))
}
};
if value {
evaluate(&args[1], scope)
} else {
evaluate(&args[2], scope)
}
}
}
pub struct While;
impl Operator for While {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
debug_assert_eq!(args.len(), 2);
let mut loop_result = Value::Nil;
loop {
let condition = evaluate(&args[0], scope)?;
let continue_condition = match condition {
Value::Number(num) => num != 0.0,
Value::Boolean(b) => b,
_ => return Err(EggError::OperatorComplaint("while(--) expects a number as it's parameter".to_string())),
};
if !continue_condition {
break Ok(loop_result);
}
loop_result = evaluate(&args[1], scope)?;
}
}
}
pub struct Repeat;
impl Operator for Repeat {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
debug_assert_eq!(args.len(), 2);
let mut iterations = 0.0;
let mut loop_value = Value::Nil;
let max_iter = match evaluate(&args[0], scope)? {
Value::Number(num) => num,
_ => return Err(EggError::OperatorComplaint("repeat(--, ...) expects a number as it's first parameter".to_string())),
};
loop {
if iterations >= max_iter.0 {
break Ok(loop_value);
}
loop_value = evaluate(&args[1], scope)?;
iterations += 1.0;
}
}
}
#[cfg(feature = "std")]
pub struct Sleep;
#[cfg(feature = "std")]
impl Operator for Sleep {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
use std::{thread::sleep, time::Duration};
debug_assert_eq!(args.len(), 1);
let sleep_time = evaluate(&args[0], scope)?;
if let Value::Number(value) = sleep_time {
let duration = Duration::from_millis(value.0 as u64);
sleep(duration)
} else {
return Err(EggError::OperatorComplaint("sleep(--) expects a number as it's parameter".to_string()));
}
Ok(sleep_time)
}
}
pub struct Panic;
impl Operator for Panic {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
debug_assert_eq!(args.len(), 1);
match evaluate(&args[0], scope)? {
Value::Number(error_code) => {
panic!("Program has met an unexpected error: ErrorCode: {error_code}")
}
Value::String(message) => panic!("{message}"),
_ => panic!("Program has terminated prematurely due to an unexpected error"),
}
}
}
pub struct Assert;
impl Operator for Assert {
fn evaluate(&self, args: &[expression::Expression], scope: &mut Scope) -> EggResult<Value> {
debug_assert_eq!(args.len(), 2);
let message = match &evaluate(&args[0], scope)? {
Value::Boolean(b) if !b => Some(evaluate(&args[1], scope)?),
_ => None,
};
if let Some(message) = message {
return Err(EggError::AssertionFailed(message));
}
Ok(Value::Nil)
}
}