From 73c2b8fb85129f93b362256dc64bd9ad8f7c0450 Mon Sep 17 00:00:00 2001 From: Vincent Ambo Date: Wed, 13 Jan 2021 16:55:29 +0300 Subject: refactor(tazjin/rlox): Wrap interpreter values in new enum This makes it possible to distinguish between literal and other values, such as functions. Change-Id: I4d87b96c2988e25a61eecfeeb56188fabfd0dc40 Reviewed-on: https://cl.tvl.fyi/c/depot/+/2367 Reviewed-by: tazjin Tested-by: BuildkiteCI --- users/tazjin/rlox/src/interpreter.rs | 77 ++++++++++++++++++++++++------------ 1 file changed, 52 insertions(+), 25 deletions(-) (limited to 'users/tazjin/rlox') diff --git a/users/tazjin/rlox/src/interpreter.rs b/users/tazjin/rlox/src/interpreter.rs index b3fc3d1900a8..b59034ea970c 100644 --- a/users/tazjin/rlox/src/interpreter.rs +++ b/users/tazjin/rlox/src/interpreter.rs @@ -7,20 +7,41 @@ use std::sync::RwLock; // Tree-walk interpreter +// Representation of an in-language value. +#[derive(Clone, Debug)] +enum Value { + Literal(Literal), +} + +impl From for Value { + fn from(lit: Literal) -> Value { + Value::Literal(lit) + } +} + +impl Value { + fn expect_literal(self) -> Result { + match self { + Value::Literal(lit) => Ok(lit), + _ => unimplemented!(), // which error? which line? + } + } +} + #[derive(Debug, Default)] struct Environment { enclosing: Option>>, - values: HashMap, + values: HashMap, } impl Environment { - fn define(&mut self, name: &scanner::Token, value: Literal) -> Result<(), Error> { + fn define(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> { let ident = identifier_str(name)?; self.values.insert(ident.into(), value); Ok(()) } - fn get(&self, name: &parser::Variable) -> Result { + fn get(&self, name: &parser::Variable) -> Result { let ident = identifier_str(&name.0)?; self.values @@ -39,7 +60,7 @@ impl Environment { }) } - fn assign(&mut self, name: &scanner::Token, value: Literal) -> Result<(), Error> { + fn assign(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> { let ident = identifier_str(name)?; match self.values.get_mut(ident) { @@ -79,21 +100,21 @@ pub struct Interpreter { impl Interpreter { // Environment modification helpers - fn define_var(&mut self, name: &scanner::Token, value: Literal) -> Result<(), Error> { + fn define_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> { self.env .write() .expect("environment lock is poisoned") .define(name, value) } - fn assign_var(&mut self, name: &scanner::Token, value: Literal) -> Result<(), Error> { + fn assign_var(&mut self, name: &scanner::Token, value: Value) -> Result<(), Error> { self.env .write() .expect("environment lock is poisoned") .assign(name, value) } - fn get_var(&mut self, var: &parser::Variable) -> Result { + fn get_var(&mut self, var: &parser::Variable) -> Result { self.env .read() .expect("environment lock is poisoned") @@ -181,10 +202,10 @@ impl Interpreter { Ok(()) } - fn eval<'a>(&mut self, expr: &Expr<'a>) -> Result { + fn eval<'a>(&mut self, expr: &Expr<'a>) -> Result { match expr { Expr::Assign(assign) => self.eval_assign(assign), - Expr::Literal(lit) => Ok(lit.clone()), + Expr::Literal(lit) => Ok(lit.clone().into()), Expr::Grouping(grouping) => self.eval(&*grouping.0), Expr::Unary(unary) => self.eval_unary(unary), Expr::Binary(binary) => self.eval_binary(binary), @@ -194,12 +215,14 @@ impl Interpreter { } } - fn eval_unary<'a>(&mut self, expr: &parser::Unary<'a>) -> Result { + fn eval_unary<'a>(&mut self, expr: &parser::Unary<'a>) -> Result { let right = self.eval(&*expr.right)?; match (&expr.operator.kind, right) { - (TokenKind::Minus, Literal::Number(num)) => Ok(Literal::Number(-num)), - (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right))), + (TokenKind::Minus, Value::Literal(Literal::Number(num))) => { + Ok(Literal::Number(-num).into()) + } + (TokenKind::Bang, right) => Ok(Literal::Boolean(!eval_truthy(&right)).into()), (op, right) => Err(Error { line: expr.operator.line, @@ -211,9 +234,9 @@ impl Interpreter { } } - fn eval_binary<'a>(&mut self, expr: &parser::Binary<'a>) -> Result { - let left = self.eval(&*expr.left)?; - let right = self.eval(&*expr.right)?; + fn eval_binary<'a>(&mut self, expr: &parser::Binary<'a>) -> Result { + let left = self.eval(&*expr.left)?.expect_literal()?; + let right = self.eval(&*expr.right)?.expect_literal()?; let result = match (&expr.operator.kind, left, right) { // Numeric @@ -252,22 +275,22 @@ impl Interpreter { } }; - Ok(result) + Ok(result.into()) } - fn eval_assign<'a>(&mut self, assign: &parser::Assign<'a>) -> Result { + fn eval_assign<'a>(&mut self, assign: &parser::Assign<'a>) -> Result { let value = self.eval(&assign.value)?; self.assign_var(&assign.name, value.clone())?; Ok(value) } - fn eval_logical<'a>(&mut self, logical: &parser::Logical<'a>) -> Result { + fn eval_logical<'a>(&mut self, logical: &parser::Logical<'a>) -> Result { let left = eval_truthy(&self.eval(&logical.left)?); let right = eval_truthy(&self.eval(&logical.right)?); match &logical.operator.kind { - TokenKind::And => Ok(Literal::Boolean(left && right)), - TokenKind::Or => Ok(Literal::Boolean(left || right)), + TokenKind::And => Ok(Literal::Boolean(left && right).into()), + TokenKind::Or => Ok(Literal::Boolean(left || right).into()), kind => Err(Error { line: logical.operator.line, kind: ErrorKind::InternalError(format!("Invalid logical operator: {:?}", kind)), @@ -278,10 +301,14 @@ impl Interpreter { // Interpreter functions not dependent on interpreter-state. -fn eval_truthy(lit: &Literal) -> bool { - match lit { - Literal::Nil => false, - Literal::Boolean(b) => *b, - _ => true, +fn eval_truthy(lit: &Value) -> bool { + if let Value::Literal(lit) = lit { + match lit { + Literal::Nil => false, + Literal::Boolean(b) => *b, + _ => true, + } + } else { + false } } -- cgit 1.4.1