Repair interpreter after parser changes

This commit is contained in:
Alex Bethel 2021-06-07 16:18:07 -05:00
parent 717d592710
commit 3bca3f88a6
4 changed files with 219 additions and 124 deletions

View file

@ -3,7 +3,7 @@
//! To interpret a piece of AbleScript code, you first need to //! To interpret a piece of AbleScript code, you first need to
//! construct an [ExecEnv], which is responsible for storing the stack //! construct an [ExecEnv], which is responsible for storing the stack
//! of local variable and function definitions accessible from an //! of local variable and function definitions accessible from an
//! AbleScript snippet. You can then call [ExecEnv::eval_items] to //! AbleScript snippet. You can then call [ExecEnv::eval_stmts] to
//! evaluate or execute any number of expressions or statements. //! evaluate or execute any number of expressions or statements.
#[deny(missing_docs)] #[deny(missing_docs)]
@ -11,12 +11,15 @@ use std::collections::HashMap;
use std::{ use std::{
convert::TryFrom, convert::TryFrom,
io::{stdout, Write}, io::{stdout, Write},
process::exit,
}; };
use rand::random;
use crate::{ use crate::{
ast::{Expr, Stmt, StmtKind},
base_55, base_55,
error::{Error, ErrorKind}, error::{Error, ErrorKind},
parser::item::{Expr, Iden, Item, Stmt},
variables::{Functio, Value, Variable}, variables::{Functio, Value, Variable},
}; };
@ -61,12 +64,11 @@ impl ExecEnv {
} }
} }
/// Evaluate a set of Items in their own stack frame. Return the /// Execute a set of Statements in their own stack frame. Return
/// value of the last Item evaluated, or an error if one or more /// an error if one or more of the Stmts failed to evaluate, or if
/// of the Items failed to evaluate or if a `break` or `hopback` /// a `break` or `hopback` statement occurred at the top level.
/// statement occurred at the top level. pub fn eval_stmts(&mut self, stmts: &[Stmt]) -> Result<Value, Error> {
pub fn eval_items(&mut self, items: &[Item]) -> Result<Value, Error> { match self.eval_stmts_hs(stmts)? {
match self.eval_items_hs(items)? {
HaltStatus::Value(v) => Ok(v), HaltStatus::Value(v) => Ok(v),
HaltStatus::Break | HaltStatus::Hopback => Err(Error { HaltStatus::Break | HaltStatus::Hopback => Err(Error {
// It's an error to issue a `break` outside of a // It's an error to issue a `break` outside of a
@ -77,18 +79,18 @@ impl ExecEnv {
} }
} }
/// The same as `eval_items`, but report "break" and "hopback" /// The same as `eval_stmts`, but report "break" and "hopback"
/// exit codes as normal conditions in a HaltStatus enum. /// exit codes as normal conditions in a HaltStatus enum.
/// ///
/// `interpret`-internal code should typically prefer this /// `interpret`-internal code should typically prefer this
/// function over `eval_items`. /// function over `eval_stmts`.
fn eval_items_hs(&mut self, items: &[Item]) -> Result<HaltStatus, Error> { fn eval_stmts_hs(&mut self, stmts: &[Stmt]) -> Result<HaltStatus, Error> {
let init_depth = self.stack.len(); let init_depth = self.stack.len();
self.stack.push(Default::default()); self.stack.push(Default::default());
let mut final_result = Ok(HaltStatus::Value(Value::Nul)); let mut final_result = Ok(HaltStatus::Value(Value::Nul));
for item in items { for stmt in stmts {
final_result = self.eval_item(item); final_result = self.eval_stmt(stmt);
if !matches!(final_result, Ok(HaltStatus::Value(_))) { if !matches!(final_result, Ok(HaltStatus::Value(_))) {
break; break;
} }
@ -100,138 +102,180 @@ impl ExecEnv {
final_result final_result
} }
/// Evaluate a single Item, returning its value or an error.
fn eval_item(&mut self, item: &Item) -> Result<HaltStatus, Error> {
match item {
Item::Expr(expr) => self.eval_expr(expr).map(|v| HaltStatus::Value(v)),
Item::Stmt(stmt) => self.eval_stmt(stmt),
}
}
/// Evaluate an Expr, returning its value or an error. /// Evaluate an Expr, returning its value or an error.
fn eval_expr(&self, expr: &Expr) -> Result<Value, Error> { fn eval_expr(&self, expr: &Expr) -> Result<Value, Error> {
use Expr::*; use crate::ast::BinOpKind::*;
use crate::ast::ExprKind::*;
use Value::*; use Value::*;
// NOTE(Alex): This block will get a whole lot cleaner once Ok(match &expr.kind {
// Ondra's parser stuff gets merged (specifically 97fb19e). BinOp { lhs, rhs, kind } => {
// For now, though, we've got a bunch of manually-checked let lhs = self.eval_expr(&lhs)?;
// unreachable!()s in here which makes me sad... let rhs = self.eval_expr(&rhs)?;
Ok(match expr { match kind {
// Binary expressions.
Add { left, right }
| Subtract { left, right }
| Multiply { left, right }
| Divide { left, right }
| Lt { left, right }
| Gt { left, right }
| Eq { left, right }
| Neq { left, right }
| And { left, right }
| Or { left, right } => {
let left = self.eval_expr(left)?;
let right = self.eval_expr(right)?;
match expr {
// Arithmetic operators. // Arithmetic operators.
Add { .. } Add | Subtract | Multiply | Divide => {
| Subtract { .. } let lhs = i32::try_from(lhs)?;
| Multiply { .. } let rhs = i32::try_from(rhs)?;
| Divide { .. } => {
let left = i32::try_from(left)?;
let right = i32::try_from(right)?;
let res = match expr { let res = match kind {
Add { .. } => left.checked_add(right), Add => lhs.checked_add(rhs),
Subtract { .. } => left.checked_sub(right), Subtract => lhs.checked_sub(rhs),
Multiply { .. } => left.checked_mul(right), Multiply => lhs.checked_mul(rhs),
Divide { .. } => left.checked_div(right), Divide => lhs.checked_div(rhs),
_ => unreachable!(), _ => unreachable!(),
} }
.ok_or(Error { .ok_or(Error {
kind: ErrorKind::ArithmeticError, kind: ErrorKind::ArithmeticError,
span: 0..0, span: expr.span.clone(),
})?; })?;
Int(res) Int(res)
} }
// Numeric comparisons. // Numeric comparisons.
Lt { .. } | Gt { .. } => { Less | Greater => {
let left = i32::try_from(left)?; let lhs = i32::try_from(lhs)?;
let right = i32::try_from(right)?; let rhs = i32::try_from(rhs)?;
let res = match expr { let res = match kind {
Lt { .. } => left < right, Less => lhs < rhs,
Gt { .. } => left > right, Greater => lhs > rhs,
_ => unreachable!(), _ => unreachable!(),
}; };
Bool(res) Bool(res)
} }
// General comparisons. // General comparisons.
Eq { .. } | Neq { .. } => { Equal | NotEqual => {
let res = match expr { let res = match kind {
Eq { .. } => left == right, Equal => lhs == rhs,
Neq { .. } => left != right, NotEqual => lhs != rhs,
_ => unreachable!(), _ => unreachable!(),
}; };
Bool(res) Bool(res)
} }
// Logical connectives. // Logical connectives.
And { .. } | Or { .. } => { And | Or => {
let left = bool::from(left); let lhs = bool::from(lhs);
let right = bool::from(right); let rhs = bool::from(rhs);
let res = match expr { let res = match kind {
And { .. } => left && right, And => lhs && rhs,
Or { .. } => left || right, Or => lhs || rhs,
_ => unreachable!(), _ => unreachable!(),
}; };
Bool(res) Bool(res)
} }
// That's all the binary operations.
_ => unreachable!(),
} }
} }
Not(expr) => Bool(!bool::from(self.eval_expr(expr)?)), Not(expr) => Bool(!bool::from(self.eval_expr(&expr)?)),
Literal(value) => value.clone(), Literal(value) => value.clone(),
Identifier(Iden(name)) => self.get_var(name)?, Variable(name) => self.get_var(&name)?,
// Binary expressions.
// Add | Subtract | Multiply | Divide | Lt | Gt | Eq | Neq | And | Or => {
// let lhs = self.eval_expr(lhs)?;
// let rhs = self.eval_expr(rhs)?;
// match expr {
// // Arithmetic operators.
// Add { .. } | Subtract { .. } | Multiply { .. } | Divide { .. } => {
// let lhs = i32::try_from(lhs)?;
// let rhs = i32::try_from(rhs)?;
// let res = match expr {
// Add { .. } => lhs.checked_add(rhs),
// Subtract { .. } => lhs.checked_sub(rhs),
// Multiply { .. } => lhs.checked_mul(rhs),
// Divide { .. } => lhs.checked_div(rhs),
// _ => unreachable!(),
// }
// .ok_or(Error {
// kind: ErrorKind::ArithmeticError,
// position: 0..0,
// })?;
// Int(res)
// }
// // Numeric comparisons.
// Lt { .. } | Gt { .. } => {
// let lhs = i32::try_from(lhs)?;
// let rhs = i32::try_from(rhs)?;
// let res = match expr {
// Lt { .. } => lhs < rhs,
// Gt { .. } => lhs > rhs,
// _ => unreachable!(),
// };
// Bool(res)
// }
// // General comparisons.
// Eq { .. } | Neq { .. } => {
// let res = match expr {
// Eq { .. } => lhs == rhs,
// Neq { .. } => lhs != rhs,
// _ => unreachable!(),
// };
// Bool(res)
// }
// // Logical connectives.
// And { .. } | Or { .. } => {
// let lhs = bool::from(lhs);
// let rhs = bool::from(rhs);
// let res = match expr {
// And { .. } => lhs && rhs,
// Or { .. } => lhs || rhs,
// _ => unreachable!(),
// };
// Bool(res)
// }
// // That's all the binary operations.
// _ => unreachable!(),
// }
// }
// Not(expr) => Bool(!bool::from(self.eval_expr(expr)?)),
// Literal(value) => value.clone(),
// Identifier(Iden(name)) => self.get_var(name)?,
}) })
} }
/// Perform the action indicated by a statement. /// Perform the action indicated by a statement.
fn eval_stmt(&mut self, stmt: &Stmt) -> Result<HaltStatus, Error> { fn eval_stmt(&mut self, stmt: &Stmt) -> Result<HaltStatus, Error> {
match stmt { match &stmt.kind {
Stmt::Print(expr) => { StmtKind::Print(expr) => {
println!("{}", self.eval_expr(expr)?); println!("{}", self.eval_expr(expr)?);
} }
Stmt::VariableDeclaration { iden, init } => { StmtKind::Var { iden, init } => {
let init = match init { let init = match init {
Some(e) => self.eval_expr(e)?, Some(e) => self.eval_expr(e)?,
None => Value::Nul, None => Value::Nul,
}; };
self.decl_var(&iden.0, init); self.decl_var(&iden.iden, init);
} }
Stmt::FunctionDeclaration { StmtKind::Functio {
iden: _, iden: _,
args: _, args: _,
body: _, body: _,
} => todo!(), } => todo!(),
Stmt::BfFDeclaration { iden, body } => { // This is missing from StmtKind after the interpreter
self.decl_var( // rewrite; presumably, parsing is not yet implemented for
&iden.0, // it. ~~Alex
Value::Functio(Functio::BfFunctio(body.as_bytes().into())), // StmtKind::BfFDeclaration { iden, body } => {
); // self.decl_var(
} // &iden.0,
Stmt::If { cond, body } => { // Value::Functio(Functio::BfFunctio(body.as_bytes().into())),
// );
// }
StmtKind::If { cond, body } => {
if self.eval_expr(cond)?.into() { if self.eval_expr(cond)?.into() {
return self.eval_items_hs(body); return self.eval_stmts_hs(&body.block);
} }
} }
Stmt::FunctionCall { iden, args } => { StmtKind::Call { iden, args } => {
let func = self.get_var(&iden.0)?; let func = self.get_var(&iden.iden)?;
match func { match func {
Value::Functio(func) => { Value::Functio(func) => {
match func { match func {
@ -246,7 +290,7 @@ impl ExecEnv {
crate::brian::interpret_with_io(&body, &input as &[_], &mut output) crate::brian::interpret_with_io(&body, &input as &[_], &mut output)
.map_err(|e| Error { .map_err(|e| Error {
kind: ErrorKind::BfInterpretError(e), kind: ErrorKind::BfInterpretError(e),
span: 0..0, span: stmt.span.clone(),
})?; })?;
// I guess Brainfuck functions write // I guess Brainfuck functions write
@ -263,31 +307,37 @@ impl ExecEnv {
} }
_ => { _ => {
return Err(Error { return Err(Error {
kind: ErrorKind::TypeError(iden.0.to_owned()), kind: ErrorKind::TypeError(iden.iden.to_owned()),
span: 0..0, span: stmt.span.clone(),
}) })
} }
} }
} }
Stmt::Loop { body } => loop { StmtKind::Loop { body } => loop {
let res = self.eval_items_hs(body)?; let res = self.eval_stmts_hs(&body.block)?;
match res { match res {
HaltStatus::Value(_) => {} HaltStatus::Value(_) => {}
HaltStatus::Break => break, HaltStatus::Break => break,
HaltStatus::Hopback => continue, HaltStatus::Hopback => continue,
} }
}, },
Stmt::VarAssignment { iden, value } => { // This is missing as well. ~~Alex
self.get_var_mut(&iden.0)?.value = self.eval_expr(value)?; // StmtKind::VarAssignment { iden, value } => {
} // self.get_var_mut(&iden.0)?.value = self.eval_expr(value)?;
Stmt::Break => { // }
StmtKind::Break => {
return Ok(HaltStatus::Break); return Ok(HaltStatus::Break);
} }
Stmt::HopBack => { StmtKind::HopBack => {
return Ok(HaltStatus::Hopback); return Ok(HaltStatus::Hopback);
} }
Stmt::Melo(iden) => { StmtKind::Melo(iden) => {
self.get_var_mut(&iden.0)?.melo = true; self.get_var_mut(&iden.iden)?.melo = true;
}
StmtKind::Rlyeh => {
// Maybe print a creepy error message or something
// here at some point. ~~Alex
exit(random());
} }
} }
@ -371,6 +421,8 @@ impl ExecEnv {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::ast::ExprKind;
use super::*; use super::*;
#[test] #[test]
@ -378,10 +430,20 @@ mod tests {
// Check that 2 + 2 = 4. // Check that 2 + 2 = 4.
let mut env = ExecEnv::new(); let mut env = ExecEnv::new();
assert_eq!( assert_eq!(
env.eval_items(&[Item::Expr(Expr::Add { env.eval_expr(&Expr {
left: Box::new(Expr::Literal(Value::Int(2))), kind: ExprKind::BinOp {
right: Box::new(Expr::Literal(Value::Int(2))), lhs: Box::new(Expr {
})]) kind: ExprKind::Literal(Value::Int(2)),
span: 0..0,
}),
rhs: Box::new(Expr {
kind: ExprKind::Literal(Value::Int(2)),
span: 0..0,
}),
kind: crate::ast::BinOpKind::Add,
},
span: 0..0
})
.unwrap(), .unwrap(),
Value::Int(4) Value::Int(4)
) )
@ -393,10 +455,20 @@ mod tests {
// error. // error.
let mut env = ExecEnv::new(); let mut env = ExecEnv::new();
assert!(matches!( assert!(matches!(
env.eval_items(&[Item::Expr(Expr::Add { env.eval_expr(&Expr {
left: Box::new(Expr::Literal(Value::Int(i32::MAX))), kind: ExprKind::BinOp {
right: Box::new(Expr::Literal(Value::Bool(false))), lhs: Box::new(Expr {
})]), kind: ExprKind::Literal(Value::Int(2)),
span: 0..0,
}),
rhs: Box::new(Expr {
kind: ExprKind::Literal(Value::Bool(true)),
span: 0..0,
}),
kind: crate::ast::BinOpKind::Add,
},
span: 0..0
}),
Err(Error { Err(Error {
kind: ErrorKind::TypeError(_), kind: ErrorKind::TypeError(_),
span: _, span: _,
@ -410,10 +482,20 @@ mod tests {
// of panicking. // of panicking.
let mut env = ExecEnv::new(); let mut env = ExecEnv::new();
assert!(matches!( assert!(matches!(
env.eval_items(&[Item::Expr(Expr::Add { env.eval_expr(&Expr {
left: Box::new(Expr::Literal(Value::Int(i32::MAX))), kind: ExprKind::BinOp {
right: Box::new(Expr::Literal(Value::Int(1))), lhs: Box::new(Expr {
})]), kind: ExprKind::Literal(Value::Int(i32::MAX)),
span: 0..0,
}),
rhs: Box::new(Expr {
kind: ExprKind::Literal(Value::Int(1)),
span: 0..0,
}),
kind: crate::ast::BinOpKind::Add,
},
span: 0..0
}),
Err(Error { Err(Error {
kind: ErrorKind::ArithmeticError, kind: ErrorKind::ArithmeticError,
span: _, span: _,
@ -422,10 +504,20 @@ mod tests {
// And the same for divide by zero. // And the same for divide by zero.
assert!(matches!( assert!(matches!(
env.eval_items(&[Item::Expr(Expr::Divide { env.eval_expr(&Expr {
left: Box::new(Expr::Literal(Value::Int(1))), kind: ExprKind::BinOp {
right: Box::new(Expr::Literal(Value::Int(0))), lhs: Box::new(Expr {
})]), kind: ExprKind::Literal(Value::Int(1)),
span: 0..0,
}),
rhs: Box::new(Expr {
kind: ExprKind::Literal(Value::Int(0)),
span: 0..0,
}),
kind: crate::ast::BinOpKind::Add,
},
span: 0..0
}),
Err(Error { Err(Error {
kind: ErrorKind::ArithmeticError, kind: ErrorKind::ArithmeticError,
span: _, span: _,

View file

@ -43,7 +43,7 @@ fn main() {
Ok(ast) => { Ok(ast) => {
println!("{:#?}", ast); println!("{:#?}", ast);
let mut env = ExecEnv::new(); let mut env = ExecEnv::new();
println!("{:?}", env.eval_items(&ast)); println!("{:?}", env.eval_stmts(&ast));
} }
Err(e) => { Err(e) => {
println!( println!(

View file

@ -19,7 +19,7 @@ pub fn repl() {
match ast { match ast {
Ok(ast) => { Ok(ast) => {
println!("{:?}", ast); println!("{:?}", ast);
println!("{:?}", env.eval_items(&ast)); println!("{:?}", env.eval_stmts(&ast));
} }
Err(e) => { Err(e) => {
println!( println!(

View file

@ -2,8 +2,11 @@ use std::{convert::TryFrom, fmt::Display, io::Write};
use rand::Rng; use rand::Rng;
use crate::ast::{Expr, Stmt}; use crate::{
use crate::error::{Error, ErrorKind}; ast::Stmt,
error::{Error, ErrorKind},
};
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub enum Abool { pub enum Abool {
Never = -1, Never = -1,
@ -31,13 +34,13 @@ impl From<Abool> for bool {
} }
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Functio { pub enum Functio {
BfFunctio(Vec<u8>), BfFunctio(Vec<u8>),
AbleFunctio(Vec<Stmt>), AbleFunctio(Vec<Stmt>),
} }
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, PartialEq, Clone)]
pub enum Value { pub enum Value {
Nul, Nul,
Str(String), Str(String),