//! Expression evaluator and statement interpreter. //! //! To interpret a piece of AbleScript code, you first need to //! construct a [Scope], which is responsible for storing the list of //! variable and function definitions accessible from an AbleScript //! snippet. You can then call [Scope::eval_items] to evaluate or //! execute any number of expressions or statements. #[deny(missing_docs)] use std::collections::HashMap; use std::convert::TryFrom; use crate::{ error::{Error, ErrorKind}, parser::item::{Expr, Iden, Item, Stmt}, variables::{Value, Variable}, }; /// A set of visible variable and function definitions, which serves /// as a context in which expressions can be evaluated. pub struct Scope { /// The mapping from variable names to values. variables: HashMap, // In the future, this will store functio definitions, a link to a // parent scope (so we can have nested scopes), and possibly other // information. } impl Scope { /// Create a new Scope with no predefined variable definitions or /// other information. pub fn new() -> Self { Self { variables: HashMap::new(), } } /// Evaluate a set of Items. Returns the value of the last Item /// evaluated, or an error if one or more of the Items failed to /// evaluate. pub fn eval_items(&mut self, items: &[Item]) -> Result { items .iter() .map(|item| self.eval_item(item)) .try_fold(Value::Nul, |_, result| result) } /// Evaluate a single Item, returning its value or an error. fn eval_item(&mut self, item: &Item) -> Result { match item { Item::Expr(expr) => self.eval_expr(expr), Item::Stmt(stmt) => self.eval_stmt(stmt).map(|_| Value::Nul), } } /// Evaluate an Expr, returning its value or an error. fn eval_expr(&self, expr: &Expr) -> Result { use Expr::*; use Value::*; // NOTE(Alex): This is quite nasty, and should probably be // re-done using macros or something. Ok(match expr { Add { left, right } => { Int(i32::try_from(self.eval_expr(left)?)? + i32::try_from(self.eval_expr(right)?)?) } Subtract { left, right } => { Int(i32::try_from(self.eval_expr(left)?)? - i32::try_from(self.eval_expr(right)?)?) } Multiply { left, right } => { Int(i32::try_from(self.eval_expr(left)?)? * i32::try_from(self.eval_expr(right)?)?) } Divide { left, right } => { Int(i32::try_from(self.eval_expr(left)?)? / i32::try_from(self.eval_expr(right)?)?) } Lt { left, right } => { Bool(i32::try_from(self.eval_expr(left)?)? < i32::try_from(self.eval_expr(right)?)?) } Gt { left, right } => { Bool(i32::try_from(self.eval_expr(left)?)? > i32::try_from(self.eval_expr(right)?)?) } Eq { left, right } => Bool(self.eval_expr(left)? == self.eval_expr(right)?), Neq { left, right } => Bool(self.eval_expr(left)? != self.eval_expr(right)?), And { left, right } => Bool( bool::try_from(self.eval_expr(left)?)? && bool::try_from(self.eval_expr(right)?)?, ), Or { left, right } => Bool( bool::try_from(self.eval_expr(left)?)? || bool::try_from(self.eval_expr(right)?)?, ), Not(expr) => Bool(!bool::try_from(self.eval_expr(expr)?)?), Literal(value) => value.clone(), Identifier(Iden(name)) => self .variables .get(name) .ok_or_else(|| Error { kind: ErrorKind::UnknownVariable(name.to_owned()), // TODO: figure out some way to avoid this 0..0 // dumbness position: 0..0, }) .and_then(|var| { if !var.melo { Ok(var.value.clone()) } else { Err(Error { kind: ErrorKind::MeloVariable(name.to_owned()), position: 0..0, }) } })?, }) } /// Perform the action indicated by a statement. fn eval_stmt(&mut self, stmt: &Stmt) -> Result<(), Error> { match stmt { Stmt::Print(expr) => { println!("{}", self.eval_expr(expr)?); Ok(()) } _ => { todo!() } } } }