Fix interpreter variable persistence bug
This commit is contained in:
parent
dc5ef63fef
commit
7d7c1e4a1b
|
@ -61,15 +61,16 @@ impl ExecEnv {
|
||||||
/// other information.
|
/// other information.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
stack: Default::default(),
|
// We always need at least one stackframe.
|
||||||
|
stack: vec![Default::default()],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute a set of Statements in their own stack frame. Return
|
/// Execute a set of Statements in the root stack frame. Return an
|
||||||
/// an error if one or more of the Stmts failed to evaluate, or if
|
/// error if one or more of the Stmts failed to evaluate, or if a
|
||||||
/// a `break` or `hopback` statement occurred at the top level.
|
/// `break` or `hopback` statement occurred at the top level.
|
||||||
pub fn eval_stmts(&mut self, stmts: &[Stmt]) -> Result<(), Error> {
|
pub fn eval_stmts(&mut self, stmts: &[Stmt]) -> Result<(), Error> {
|
||||||
match self.eval_stmts_hs(stmts)? {
|
match self.eval_stmts_hs(stmts, false)? {
|
||||||
HaltStatus::Finished => Ok(()),
|
HaltStatus::Finished => Ok(()),
|
||||||
HaltStatus::Break(span) | HaltStatus::Hopback(span) => Err(Error {
|
HaltStatus::Break(span) | HaltStatus::Hopback(span) => Err(Error {
|
||||||
// It's an error to issue a `break` outside of a
|
// It's an error to issue a `break` outside of a
|
||||||
|
@ -81,14 +82,18 @@ impl ExecEnv {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The same as `eval_stmts`, 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, and
|
||||||
|
/// create a new stack frame if `stackframe` is true.
|
||||||
///
|
///
|
||||||
/// `interpret`-internal code should typically prefer this
|
/// `interpret`-internal code should typically prefer this
|
||||||
/// function over `eval_stmts`.
|
/// function over `eval_stmts`.
|
||||||
fn eval_stmts_hs(&mut self, stmts: &[Stmt]) -> Result<HaltStatus, Error> {
|
fn eval_stmts_hs(&mut self, stmts: &[Stmt], stackframe: bool) -> Result<HaltStatus, Error> {
|
||||||
let init_depth = self.stack.len();
|
let init_depth = self.stack.len();
|
||||||
|
|
||||||
self.stack.push(Default::default());
|
if stackframe {
|
||||||
|
self.stack.push(Default::default());
|
||||||
|
}
|
||||||
|
|
||||||
let mut final_result = Ok(HaltStatus::Finished);
|
let mut final_result = Ok(HaltStatus::Finished);
|
||||||
for stmt in stmts {
|
for stmt in stmts {
|
||||||
final_result = self.eval_stmt(stmt);
|
final_result = self.eval_stmt(stmt);
|
||||||
|
@ -96,7 +101,10 @@ impl ExecEnv {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.stack.pop();
|
|
||||||
|
if stackframe {
|
||||||
|
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);
|
||||||
|
@ -211,7 +219,7 @@ impl ExecEnv {
|
||||||
// }
|
// }
|
||||||
StmtKind::If { cond, body } => {
|
StmtKind::If { cond, body } => {
|
||||||
if self.eval_expr(cond)?.to_bool() {
|
if self.eval_expr(cond)?.to_bool() {
|
||||||
return self.eval_stmts_hs(&body.block);
|
return self.eval_stmts_hs(&body.block, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Call { iden, args } => {
|
StmtKind::Call { iden, args } => {
|
||||||
|
@ -254,7 +262,7 @@ impl ExecEnv {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
StmtKind::Loop { body } => loop {
|
StmtKind::Loop { body } => loop {
|
||||||
let res = self.eval_stmts_hs(&body.block)?;
|
let res = self.eval_stmts_hs(&body.block, true)?;
|
||||||
match res {
|
match res {
|
||||||
HaltStatus::Finished => {}
|
HaltStatus::Finished => {}
|
||||||
HaltStatus::Break(_) => break,
|
HaltStatus::Break(_) => break,
|
||||||
|
|
Loading…
Reference in a new issue