From 1c2032ab879dab5ad8a3edfc77f3218e06497181 Mon Sep 17 00:00:00 2001 From: Alexander Bethel Date: Sun, 23 May 2021 18:46:42 -0500 Subject: [PATCH] Implement more statements Added variable declaration, `if` statements, `loop` statements, variable assignment, and variable banning to go along with printing (which was already implemented). We still need function declarations, brainfuck declarations, function calls, and the control flow operators "break" and "hopback". --- src/interpret.rs | 76 +++++++++++++++++++++++++++++++++++++++++------- src/variables.rs | 26 ++++++++++------- 2 files changed, 81 insertions(+), 21 deletions(-) diff --git a/src/interpret.rs b/src/interpret.rs index 051b0923..3565b62c 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -21,7 +21,6 @@ use crate::{ 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. @@ -82,13 +81,13 @@ impl Scope { } 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)?)?), + And { left, right } => { + Bool(bool::from(self.eval_expr(left)?) && bool::from(self.eval_expr(right)?)) + } + Or { left, right } => { + Bool(bool::from(self.eval_expr(left)?) || bool::from(self.eval_expr(right)?)) + } + Not(expr) => Bool(!bool::from(self.eval_expr(expr)?)), Literal(value) => value.clone(), Identifier(Iden(name)) => self .variables @@ -117,11 +116,66 @@ impl Scope { match stmt { Stmt::Print(expr) => { println!("{}", self.eval_expr(expr)?); - Ok(()) } - _ => { - todo!() + Stmt::VariableDeclaration { iden, init } => { + self.variables.insert( + iden.0.clone(), + Variable { + melo: false, + value: match init { + Some(init) => self.eval_expr(init)?, + None => Value::Nul, + }, + }, + ); + } + Stmt::FunctionDeclaration { + iden: _, + args: _, + body: _, + } => todo!(), + Stmt::BfFDeclaration { iden: _, body: _ } => todo!(), + Stmt::If { cond, body } => { + if self.eval_expr(cond)?.into() { + self.eval_items(body)?; + } + } + Stmt::FunctionCall { iden: _, args: _ } => todo!(), + Stmt::Loop { body } => { + loop { + // For now, loops run forever until they reach an + // error. + self.eval_items(body)?; + } + } + Stmt::VarAssignment { iden, value } => { + let value = self.eval_expr(value)?; + let record = self.variables.get_mut(&iden.0).ok_or_else(|| Error { + kind: ErrorKind::UnknownVariable(iden.0.clone()), + position: 0..0, + })?; + + if record.melo { + return Err(Error { + kind: ErrorKind::MeloVariable(iden.0.clone()), + position: 0..0, + }); + } + + record.value = value; + } + Stmt::Break => todo!(), + Stmt::HopBack => todo!(), + Stmt::Melo(iden) => { + let record = self.variables.get_mut(&iden.0).ok_or_else(|| Error { + kind: ErrorKind::UnknownVariable(iden.0.clone()), + position: 0..0, + })?; + + record.melo = true; } } + + Ok(()) } } diff --git a/src/variables.rs b/src/variables.rs index 3150b447..496138a5 100644 --- a/src/variables.rs +++ b/src/variables.rs @@ -71,17 +71,23 @@ impl TryFrom for i32 { } } -impl TryFrom for bool { - type Error = Error; - - fn try_from(value: Value) -> Result { +// Coercions from a value to a boolean always succeed, so every value +// can be used in an `if` statement. C does things that way, so how +// could it possibly be a bad idea? +impl From for bool { + fn from(value: Value) -> Self { match value { - Value::Bool(b) => Ok(b), - Value::Abool(b) => Ok(b.into()), - _ => Err(Error { - kind: ErrorKind::TypeError(format!("Expected bool, got {}", value)), - position: 0..0, - }), + // Booleans and abooleans have a trivial conversion. + Value::Bool(b) => b, + Value::Abool(b) => b.into(), + // The empty string is falsey, other strings are truthy. + Value::Str(s) => s.len() != 0, + // 0 is falsey, nonzero is truthy. + Value::Int(x) => x != 0, + // And nul is truthy as a symbol of the fact that the + // deep, fundamental truth of this world is nothing but + // the eternal void. + Value::Nul => true, } } }