Implement break and hopback statements

This commit is contained in:
Alexander Bethel 2021-05-25 21:22:38 -05:00
parent e7560f9364
commit 7537b4ac98
2 changed files with 54 additions and 19 deletions

View file

@ -14,4 +14,5 @@ pub enum ErrorKind {
UnknownVariable(String), UnknownVariable(String),
MeloVariable(String), MeloVariable(String),
TypeError(String), TypeError(String),
TopLevelBreak,
} }

View file

@ -34,6 +34,18 @@ struct Scope {
// other information. // other information.
} }
/// The result of successfully executing a set of statements.
enum ControlFlow {
/// The statements evaluated to this value.
Value(Value),
/// A "break" statement occurred at the top level.
Break,
/// A "hopback" statement occurred at the top level.
Hopback,
}
impl ExecEnv { impl ExecEnv {
/// Create a new Scope with no predefined variable definitions or /// Create a new Scope with no predefined variable definitions or
/// other information. /// other information.
@ -47,25 +59,42 @@ impl ExecEnv {
/// value of the last Item evaluated, or an error if one or more /// value of the last Item evaluated, or an error if one or more
/// of the Items failed to evaluate. /// of the Items failed to evaluate.
pub fn eval_items(&mut self, items: &[Item]) -> Result<Value, Error> { pub fn eval_items(&mut self, items: &[Item]) -> Result<Value, Error> {
match self.eval_items_cf(items)? {
ControlFlow::Value(v) => Ok(v),
ControlFlow::Break | ControlFlow::Hopback => Err(Error {
// It's an error to issue a `break` outside of a
// `loop` statement.
kind: ErrorKind::TopLevelBreak,
position: 0..0,
}),
}
}
/// The same as `eval_items`, but reports "break" and "hopback"
/// exit codes as normal conditions in a ControlFlow enum.
fn eval_items_cf(&mut self, items: &[Item]) -> Result<ControlFlow, Error> {
let init_depth = self.stack.len(); let init_depth = self.stack.len();
self.stack.push(Default::default()); self.stack.push(Default::default());
let res = items let mut final_result = Ok(ControlFlow::Value(Value::Nul));
.iter() for item in items {
.map(|item| self.eval_item(item)) final_result = self.eval_item(item);
.try_fold(Value::Nul, |_, result| result); if !matches!(final_result, Ok(ControlFlow::Value(_))) {
break;
}
}
self.stack.pop(); self.stack.pop();
// Invariant: stack size must have net 0 change. // Invariant: stack size must have net 0 change.
debug_assert_eq!(self.stack.len(), init_depth); debug_assert_eq!(self.stack.len(), init_depth);
res final_result
} }
/// Evaluate a single Item, returning its value or an error. /// Evaluate a single Item, returning its value or an error.
fn eval_item(&mut self, item: &Item) -> Result<Value, Error> { fn eval_item(&mut self, item: &Item) -> Result<ControlFlow, Error> {
match item { match item {
Item::Expr(expr) => self.eval_expr(expr), Item::Expr(expr) => self.eval_expr(expr).map(|v| ControlFlow::Value(v)),
Item::Stmt(stmt) => self.eval_stmt(stmt).map(|_| Value::Nul), Item::Stmt(stmt) => self.eval_stmt(stmt),
} }
} }
@ -110,7 +139,7 @@ impl ExecEnv {
} }
/// Perform the action indicated by a statement. /// Perform the action indicated by a statement.
fn eval_stmt(&mut self, stmt: &Stmt) -> Result<(), Error> { fn eval_stmt(&mut self, stmt: &Stmt) -> Result<ControlFlow, Error> {
match stmt { match stmt {
Stmt::Print(expr) => { Stmt::Print(expr) => {
println!("{}", self.eval_expr(expr)?); println!("{}", self.eval_expr(expr)?);
@ -140,28 +169,33 @@ impl ExecEnv {
Stmt::BfFDeclaration { iden: _, body: _ } => todo!(), Stmt::BfFDeclaration { iden: _, body: _ } => todo!(),
Stmt::If { cond, body } => { Stmt::If { cond, body } => {
if self.eval_expr(cond)?.into() { if self.eval_expr(cond)?.into() {
self.eval_items(body)?; return self.eval_items_cf(body);
} }
} }
Stmt::FunctionCall { iden: _, args: _ } => todo!(), Stmt::FunctionCall { iden: _, args: _ } => todo!(),
Stmt::Loop { body } => { Stmt::Loop { body } => loop {
loop { let res = self.eval_items_cf(body)?;
// For now, loops run forever until they reach an match res {
// error. ControlFlow::Value(_) => {}
self.eval_items(body)?; ControlFlow::Break => break,
ControlFlow::Hopback => continue,
} }
} },
Stmt::VarAssignment { iden, value } => { Stmt::VarAssignment { iden, value } => {
self.get_var_mut(&iden.0)?.value = self.eval_expr(value)?; self.get_var_mut(&iden.0)?.value = self.eval_expr(value)?;
} }
Stmt::Break => todo!(), Stmt::Break => {
Stmt::HopBack => todo!(), return Ok(ControlFlow::Break);
}
Stmt::HopBack => {
return Ok(ControlFlow::Hopback);
}
Stmt::Melo(iden) => { Stmt::Melo(iden) => {
self.get_var_mut(&iden.0)?.melo = true; self.get_var_mut(&iden.0)?.melo = true;
} }
} }
Ok(()) Ok(ControlFlow::Value(Value::Nul))
} }
/// Get a shared reference to the value of a variable. Throw an /// Get a shared reference to the value of a variable. Throw an