diff --git a/src/interpret.rs b/src/interpret.rs index 888b314e..b0b5049d 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -195,7 +195,24 @@ impl ExecEnv { } Not(expr) => Bool(!self.eval_expr(&expr)?.into_bool()), Literal(value) => value.clone(), - Cart(_) | Index { .. } => todo!("cart support"), + ExprKind::Cart(members) => Value::Cart( + members + .iter() + .map(|(value, key)| { + self.eval_expr(value).and_then(|value| { + self.eval_expr(key) + .map(|key| (key, Rc::new(RefCell::new(value)))) + }) + }) + .collect::, _>>()?, + ), + Index { cart, index } => { + let cart = self.eval_expr(cart)?; + let index = self.eval_expr(index)?; + + // TODO: this probably shouldn't be cloned + cart.index(&index).borrow().clone() + } // TODO: not too happy with constructing an artificial // Iden here. diff --git a/src/variables.rs b/src/variables.rs index 7f3184c8..6a213c56 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -1,10 +1,10 @@ -use std::{cell::RefCell, fmt::Display, io::Write, rc::Rc}; +use std::{cell::RefCell, collections::HashMap, fmt::Display, hash::Hash, io::Write, rc::Rc}; use rand::Rng; use crate::{ast::Stmt, consts}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] pub enum Abool { Never = -1, Sometimes = 0, @@ -43,7 +43,7 @@ pub enum Functio { }, } -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug, Clone)] pub enum Value { Nul, Str(String), @@ -51,8 +51,43 @@ pub enum Value { Bool(bool), Abool(Abool), Functio(Functio), + Cart(HashMap>>), } +impl Hash for Value { + fn hash(&self, state: &mut H) { + match self { + Value::Nul => (), + Value::Str(v) => v.hash(state), + Value::Int(v) => v.hash(state), + Value::Bool(v) => v.hash(state), + Value::Abool(v) => v.to_string().hash(state), + Value::Functio(_) => todo!(), + Value::Cart(_) => self.to_string().hash(state), + } + } +} + +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Value::Nul, Value::Nul) => true, + (Value::Str(left), Value::Str(right)) => left == right, + (Value::Int(left), Value::Int(right)) => left == right, + (Value::Bool(left), Value::Bool(right)) => left == right, + (Value::Abool(left), Value::Abool(right)) => left == right, + (Value::Functio(left), Value::Functio(right)) => left == right, + (Value::Cart(_left), Value::Cart(_right)) => { + todo!() + } + (_, _) => false, + // TODO: do more coercions! + } + } +} + +impl Eq for Value {} + impl Value { /// Write an AbleScript value to a Brainfuck input stream by /// coercing the value to an integer, then truncating that integer @@ -66,10 +101,10 @@ impl Value { } /// Coerce a value to an integer. - pub fn into_i32(self) -> i32 { + pub fn into_i32(&self) -> i32 { match self { - Value::Abool(a) => a as _, - Value::Bool(b) => b as _, + Value::Abool(a) => *a as _, + Value::Bool(b) => *b as _, Value::Functio(func) => match func { Functio::BfFunctio { instructions, @@ -77,23 +112,47 @@ impl Value { } => (instructions.len() + tape_len) as _, Functio::AbleFunctio { params, body } => (params.len() + body.len()) as _, }, - Value::Int(i) => i, + Value::Int(i) => *i, Value::Nul => consts::ANSWER, Value::Str(text) => text.parse().unwrap_or(consts::ANSWER), + Value::Cart(c) => c.len() as _, } } /// Coerce a Value to a boolean. The conversion cannot fail. - pub fn into_bool(self) -> bool { + pub fn into_bool(&self) -> bool { match self { - Value::Abool(b) => b.into(), - Value::Bool(b) => b, + Value::Abool(b) => b.clone().into(), + Value::Bool(b) => *b, Value::Functio(_) => true, - Value::Int(x) => x != 0, + Value::Int(x) => *x != 0, Value::Nul => true, Value::Str(s) => !s.is_empty(), + Value::Cart(c) => !c.is_empty(), } } + + /// Index a value with another value, as in the "a[b]" syntax. + pub fn index(&self, index: &Value) -> Rc> { + Rc::new(RefCell::new(match self { + Value::Nul => Value::Nul, + Value::Str(s) => Value::Int(s.as_bytes()[index.into_i32() as usize] as i32), + Value::Int(i) => Value::Int( + (format!("{}", i).as_bytes()[index.into_i32() as usize] - ('0' as u8)) as i32, + ), + Value::Bool(b) => Value::Int( + format!("{}", b) + .chars() + .nth(index.into_i32() as usize) + .unwrap_or_else(|| '?') as i32, + ), + Value::Abool(b) => Value::Int(*b as i32), + Value::Functio(_) => Value::Int(42), + Value::Cart(c) => { + return (c.get(index).cloned()).unwrap_or_else(|| Rc::new(RefCell::new(Value::Nul))) + } + })) + } } impl Display for Value { @@ -128,6 +187,15 @@ impl Display for Value { ) } }, + Value::Cart(c) => { + write!(f, "[")?; + + for (key, value) in c { + write!(f, "{} <= {},", value.borrow(), key)?; + } + + write!(f, "]") + } } } }