2021-05-20 18:18:01 -05:00
|
|
|
//! Expression evaluator and statement interpreter.
|
|
|
|
//!
|
|
|
|
//! To interpret a piece of AbleScript code, you first need to
|
2021-05-25 13:26:01 -05:00
|
|
|
//! construct an [ExecEnv], which is responsible for storing the stack
|
|
|
|
//! of local variable and function definitions accessible from an
|
2021-06-07 16:18:07 -05:00
|
|
|
//! AbleScript snippet. You can then call [ExecEnv::eval_stmts] to
|
2021-05-25 13:26:01 -05:00
|
|
|
//! evaluate or execute any number of expressions or statements.
|
2021-05-20 18:18:01 -05:00
|
|
|
|
2021-06-13 12:06:38 -05:00
|
|
|
#![deny(missing_docs)]
|
2021-06-02 15:29:31 -05:00
|
|
|
use std::{
|
2021-12-14 15:56:40 -06:00
|
|
|
cmp::Ordering,
|
2021-06-18 16:05:50 -05:00
|
|
|
collections::{HashMap, VecDeque},
|
|
|
|
io::{stdin, stdout, Read, Write},
|
2021-11-27 11:02:41 -06:00
|
|
|
mem::take,
|
2021-06-07 19:57:44 -05:00
|
|
|
ops::Range,
|
2021-06-07 16:18:07 -05:00
|
|
|
process::exit,
|
2021-06-02 15:29:31 -05:00
|
|
|
};
|
2021-05-20 18:18:01 -05:00
|
|
|
|
2021-06-07 16:18:07 -05:00
|
|
|
use rand::random;
|
|
|
|
|
2021-05-20 18:18:01 -05:00
|
|
|
use crate::{
|
2022-04-01 18:34:25 -05:00
|
|
|
ast::{Assignable, AssignableKind, Expr, Spanned, Stmt},
|
2021-07-13 14:22:06 -05:00
|
|
|
base_55,
|
2021-09-01 10:46:17 -05:00
|
|
|
consts::ablescript_consts,
|
2021-05-20 18:18:01 -05:00
|
|
|
error::{Error, ErrorKind},
|
2022-03-01 15:13:49 -06:00
|
|
|
variables::{Functio, Value, ValueRef, Variable},
|
2021-05-20 18:18:01 -05:00
|
|
|
};
|
|
|
|
|
2021-05-25 13:26:01 -05:00
|
|
|
/// An environment for executing AbleScript code.
|
|
|
|
pub struct ExecEnv {
|
|
|
|
/// The stack, ordered such that `stack[stack.len() - 1]` is the
|
|
|
|
/// top-most (newest) stack frame, and `stack[0]` is the
|
|
|
|
/// bottom-most (oldest) stack frame.
|
|
|
|
stack: Vec<Scope>,
|
2021-06-18 16:05:50 -05:00
|
|
|
|
|
|
|
/// The `read` statement maintains a buffer of up to 7 bits,
|
|
|
|
/// because input comes from the operating system 8 bits at a time
|
|
|
|
/// (via stdin) but gets delivered to AbleScript 3 bits at a time
|
|
|
|
/// (via the `read` statement). We store each of those bits as
|
|
|
|
/// booleans to facilitate easy manipulation.
|
|
|
|
read_buf: VecDeque<bool>,
|
2021-05-25 13:26:01 -05:00
|
|
|
}
|
|
|
|
|
2021-05-25 21:55:02 -05:00
|
|
|
/// A set of visible variable and function definitions in a single
|
|
|
|
/// stack frame.
|
2021-05-25 13:26:01 -05:00
|
|
|
struct Scope {
|
2021-05-20 18:18:01 -05:00
|
|
|
/// The mapping from variable names to values.
|
|
|
|
variables: HashMap<String, Variable>,
|
2021-07-13 14:22:06 -05:00
|
|
|
}
|
|
|
|
|
2022-03-01 14:53:58 -06:00
|
|
|
impl Default for ExecEnv {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
stack: vec![Default::default()],
|
|
|
|
read_buf: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-13 14:22:06 -05:00
|
|
|
impl Default for Scope {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self {
|
|
|
|
variables: ablescript_consts(),
|
|
|
|
}
|
|
|
|
}
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
|
|
|
|
2021-05-25 21:55:02 -05:00
|
|
|
/// The reason a successful series of statements halted.
|
|
|
|
enum HaltStatus {
|
2021-06-07 19:57:44 -05:00
|
|
|
/// We ran out of statements to execute.
|
|
|
|
Finished,
|
2021-05-25 21:22:38 -05:00
|
|
|
|
2021-06-07 19:57:44 -05:00
|
|
|
/// A `break` statement occurred at the given span, and was not
|
|
|
|
/// caught by a `loop` statement up to this point.
|
|
|
|
Break(Range<usize>),
|
2021-05-25 21:22:38 -05:00
|
|
|
|
2021-06-07 19:57:44 -05:00
|
|
|
/// A `hopback` statement occurred at the given span, and was not
|
|
|
|
/// caught by a `loop` statement up to this point.
|
|
|
|
Hopback(Range<usize>),
|
2021-05-25 21:22:38 -05:00
|
|
|
}
|
|
|
|
|
2021-06-18 16:05:50 -05:00
|
|
|
/// The number of bits the `read` statement reads at once from
|
|
|
|
/// standard input.
|
|
|
|
pub const READ_BITS: u8 = 3;
|
|
|
|
|
2021-05-25 13:26:01 -05:00
|
|
|
impl ExecEnv {
|
2021-05-20 18:18:01 -05:00
|
|
|
/// Create a new Scope with no predefined variable definitions or
|
|
|
|
/// other information.
|
|
|
|
pub fn new() -> Self {
|
2022-03-01 14:53:58 -06:00
|
|
|
Self::default()
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
|
|
|
|
2022-02-13 17:08:48 -06:00
|
|
|
/// Create a new Scope with predefined variables
|
|
|
|
pub fn new_with_vars<I>(vars: I) -> Self
|
|
|
|
where
|
|
|
|
I: IntoIterator<Item = (String, Variable)>,
|
|
|
|
{
|
|
|
|
let scope = Scope {
|
|
|
|
variables: ablescript_consts().into_iter().chain(vars).collect(),
|
|
|
|
};
|
|
|
|
|
|
|
|
Self {
|
|
|
|
stack: vec![scope],
|
|
|
|
read_buf: Default::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-08 19:21:33 -05:00
|
|
|
/// Execute a set of Statements in the root stack frame. Return an
|
|
|
|
/// error if one or more of the Stmts failed to evaluate, or if a
|
|
|
|
/// `break` or `hopback` statement occurred at the top level.
|
2022-04-01 18:34:25 -05:00
|
|
|
pub fn eval_stmts(&mut self, stmts: &[Spanned<Stmt>]) -> Result<(), Error> {
|
2021-06-08 19:21:33 -05:00
|
|
|
match self.eval_stmts_hs(stmts, false)? {
|
2021-06-07 19:57:44 -05:00
|
|
|
HaltStatus::Finished => Ok(()),
|
|
|
|
HaltStatus::Break(span) | HaltStatus::Hopback(span) => Err(Error {
|
2021-05-25 21:22:38 -05:00
|
|
|
// It's an error to issue a `break` outside of a
|
|
|
|
// `loop` statement.
|
|
|
|
kind: ErrorKind::TopLevelBreak,
|
2021-06-12 22:07:58 -05:00
|
|
|
span,
|
2021-05-25 21:22:38 -05:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 16:18:07 -05:00
|
|
|
/// The same as `eval_stmts`, but report "break" and "hopback"
|
2021-06-08 19:21:33 -05:00
|
|
|
/// exit codes as normal conditions in a HaltStatus enum, and
|
|
|
|
/// create a new stack frame if `stackframe` is true.
|
2021-05-25 21:55:02 -05:00
|
|
|
///
|
|
|
|
/// `interpret`-internal code should typically prefer this
|
2021-06-07 16:18:07 -05:00
|
|
|
/// function over `eval_stmts`.
|
2022-04-01 18:22:46 -05:00
|
|
|
fn eval_stmts_hs(
|
|
|
|
&mut self,
|
2022-04-01 18:34:25 -05:00
|
|
|
stmts: &[Spanned<Stmt>],
|
2022-04-01 18:22:46 -05:00
|
|
|
stackframe: bool,
|
|
|
|
) -> Result<HaltStatus, Error> {
|
2021-05-25 13:26:01 -05:00
|
|
|
let init_depth = self.stack.len();
|
|
|
|
|
2021-06-08 19:21:33 -05:00
|
|
|
if stackframe {
|
|
|
|
self.stack.push(Default::default());
|
|
|
|
}
|
|
|
|
|
2021-06-07 19:57:44 -05:00
|
|
|
let mut final_result = Ok(HaltStatus::Finished);
|
2021-06-07 16:18:07 -05:00
|
|
|
for stmt in stmts {
|
|
|
|
final_result = self.eval_stmt(stmt);
|
2021-06-07 19:57:44 -05:00
|
|
|
if !matches!(final_result, Ok(HaltStatus::Finished)) {
|
2021-05-25 21:22:38 -05:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 19:21:33 -05:00
|
|
|
|
|
|
|
if stackframe {
|
|
|
|
self.stack.pop();
|
|
|
|
}
|
2021-05-25 13:26:01 -05:00
|
|
|
|
|
|
|
// Invariant: stack size must have net 0 change.
|
|
|
|
debug_assert_eq!(self.stack.len(), init_depth);
|
2021-05-25 21:22:38 -05:00
|
|
|
final_result
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Evaluate an Expr, returning its value or an error.
|
2022-04-01 18:34:25 -05:00
|
|
|
fn eval_expr(&self, expr: &Spanned<Expr>) -> Result<Value, Error> {
|
2021-06-07 16:18:07 -05:00
|
|
|
use crate::ast::BinOpKind::*;
|
2022-04-01 18:34:25 -05:00
|
|
|
use crate::ast::Expr::*;
|
2021-05-20 18:18:01 -05:00
|
|
|
|
2022-04-01 18:22:46 -05:00
|
|
|
Ok(match &expr.item {
|
2021-06-07 16:18:07 -05:00
|
|
|
BinOp { lhs, rhs, kind } => {
|
2021-07-29 16:22:38 -05:00
|
|
|
let lhs = self.eval_expr(lhs)?;
|
|
|
|
let rhs = self.eval_expr(rhs)?;
|
2021-06-07 16:18:07 -05:00
|
|
|
match kind {
|
2021-08-28 16:27:35 -05:00
|
|
|
Add => lhs + rhs,
|
2021-08-28 16:59:04 -05:00
|
|
|
Subtract => lhs - rhs,
|
|
|
|
Multiply => lhs * rhs,
|
|
|
|
Divide => lhs / rhs,
|
2022-03-30 13:55:05 -05:00
|
|
|
Greater => Value::Abool((lhs > rhs).into()),
|
|
|
|
Less => Value::Abool((lhs < rhs).into()),
|
|
|
|
Equal => Value::Abool((lhs == rhs).into()),
|
|
|
|
NotEqual => Value::Abool((lhs != rhs).into()),
|
2021-06-02 18:41:20 -05:00
|
|
|
}
|
2021-05-23 18:46:42 -05:00
|
|
|
}
|
2022-04-08 10:39:14 -05:00
|
|
|
Aint(expr) => !self.eval_expr(expr)?,
|
2021-05-20 18:18:01 -05:00
|
|
|
Literal(value) => value.clone(),
|
2022-04-01 18:34:25 -05:00
|
|
|
Expr::Cart(members) => Value::Cart(
|
2021-07-29 13:29:23 -05:00
|
|
|
members
|
|
|
|
.iter()
|
|
|
|
.map(|(value, key)| {
|
|
|
|
self.eval_expr(value).and_then(|value| {
|
2022-03-01 15:13:49 -06:00
|
|
|
self.eval_expr(key).map(|key| (key, ValueRef::new(value)))
|
2021-07-29 13:29:23 -05:00
|
|
|
})
|
|
|
|
})
|
|
|
|
.collect::<Result<HashMap<_, _>, _>>()?,
|
|
|
|
),
|
2021-08-28 16:27:35 -05:00
|
|
|
Index { expr, index } => {
|
|
|
|
let value = self.eval_expr(expr)?;
|
2021-07-29 13:29:23 -05:00
|
|
|
let index = self.eval_expr(index)?;
|
|
|
|
|
2021-08-28 16:27:35 -05:00
|
|
|
value
|
|
|
|
.into_cart()
|
|
|
|
.get(&index)
|
|
|
|
.map(|x| x.borrow().clone())
|
|
|
|
.unwrap_or(Value::Nul)
|
2021-07-29 13:29:23 -05:00
|
|
|
}
|
2022-03-01 14:53:58 -06:00
|
|
|
Len(expr) => Value::Int(self.eval_expr(expr)?.length()),
|
2021-06-07 19:57:44 -05:00
|
|
|
|
|
|
|
// TODO: not too happy with constructing an artificial
|
2021-10-04 16:00:18 -05:00
|
|
|
// Ident here.
|
2022-04-01 18:34:25 -05:00
|
|
|
Variable(name) => self.get_var(&Spanned::new(name.to_owned(), expr.span.clone()))?,
|
2021-05-20 18:18:01 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform the action indicated by a statement.
|
2022-04-01 18:34:25 -05:00
|
|
|
fn eval_stmt(&mut self, stmt: &Spanned<Stmt>) -> Result<HaltStatus, Error> {
|
2022-04-01 18:22:46 -05:00
|
|
|
match &stmt.item {
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Print(expr) => {
|
2021-05-20 18:18:01 -05:00
|
|
|
println!("{}", self.eval_expr(expr)?);
|
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Var { ident, init } => {
|
2021-05-25 13:26:01 -05:00
|
|
|
let init = match init {
|
|
|
|
Some(e) => self.eval_expr(e)?,
|
|
|
|
None => Value::Nul,
|
|
|
|
};
|
|
|
|
|
2022-04-01 18:34:25 -05:00
|
|
|
self.decl_var(&ident.item, init);
|
2021-05-23 18:46:42 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Functio {
|
2021-10-12 15:14:20 -05:00
|
|
|
ident,
|
|
|
|
params,
|
|
|
|
body,
|
|
|
|
} => {
|
2021-06-18 16:05:50 -05:00
|
|
|
self.decl_var(
|
2022-04-01 18:34:25 -05:00
|
|
|
&ident.item,
|
2021-12-08 15:56:12 -06:00
|
|
|
Value::Functio(Functio::Able {
|
2022-04-01 18:34:25 -05:00
|
|
|
params: params.iter().map(|ident| ident.item.to_owned()).collect(),
|
|
|
|
body: body.to_owned(),
|
2021-06-18 16:05:50 -05:00
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::BfFunctio {
|
2021-10-04 16:00:18 -05:00
|
|
|
ident,
|
2021-06-12 09:26:21 -05:00
|
|
|
tape_len,
|
|
|
|
code,
|
|
|
|
} => {
|
|
|
|
self.decl_var(
|
2022-04-01 18:34:25 -05:00
|
|
|
&ident.item,
|
2021-12-08 15:56:12 -06:00
|
|
|
Value::Functio(Functio::Bf {
|
2021-06-12 09:26:21 -05:00
|
|
|
instructions: code.to_owned(),
|
|
|
|
tape_len: tape_len
|
|
|
|
.as_ref()
|
2022-02-13 17:08:48 -06:00
|
|
|
.map(|tape_len| {
|
|
|
|
self.eval_expr(tape_len).map(|v| v.into_isize() as usize)
|
|
|
|
})
|
2021-06-12 09:26:21 -05:00
|
|
|
.unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
|
|
|
|
}),
|
|
|
|
);
|
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::If { cond, body } => {
|
2022-03-30 13:55:05 -05:00
|
|
|
if self.eval_expr(cond)?.into_abool().to_bool() {
|
2022-04-01 18:34:25 -05:00
|
|
|
return self.eval_stmts_hs(body, true);
|
2021-05-23 18:46:42 -05:00
|
|
|
}
|
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Call { expr, args } => {
|
2021-08-28 16:27:35 -05:00
|
|
|
let func = self.eval_expr(expr)?.into_functio();
|
2021-06-12 22:07:58 -05:00
|
|
|
|
2021-08-28 16:27:35 -05:00
|
|
|
self.fn_call(func, args, &stmt.span)?;
|
2021-06-02 15:29:31 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Loop { body } => loop {
|
|
|
|
let res = self.eval_stmts_hs(body, true)?;
|
2021-05-25 21:22:38 -05:00
|
|
|
match res {
|
2021-06-07 19:57:44 -05:00
|
|
|
HaltStatus::Finished => {}
|
|
|
|
HaltStatus::Break(_) => break,
|
|
|
|
HaltStatus::Hopback(_) => continue,
|
2021-05-23 18:46:42 -05:00
|
|
|
}
|
2021-05-25 21:22:38 -05:00
|
|
|
},
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Assign { assignable, value } => {
|
2021-11-05 18:09:53 -05:00
|
|
|
self.assign(assignable, self.eval_expr(value)?)?;
|
2021-06-11 10:05:48 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Break => {
|
2021-06-07 19:57:44 -05:00
|
|
|
return Ok(HaltStatus::Break(stmt.span.clone()));
|
2021-05-25 21:22:38 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::HopBack => {
|
2021-06-07 19:57:44 -05:00
|
|
|
return Ok(HaltStatus::Hopback(stmt.span.clone()));
|
2021-05-25 21:22:38 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Melo(ident) => {
|
2021-10-04 16:00:18 -05:00
|
|
|
self.get_var_mut(ident)?.melo = true;
|
2021-06-07 16:18:07 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Rlyeh => {
|
2021-06-07 16:18:07 -05:00
|
|
|
// Maybe print a creepy error message or something
|
|
|
|
// here at some point. ~~Alex
|
|
|
|
exit(random());
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Rickroll => {
|
2021-06-15 10:26:44 -05:00
|
|
|
stdout()
|
|
|
|
.write_all(include_str!("rickroll").as_bytes())
|
|
|
|
.expect("Failed to write to stdout");
|
|
|
|
}
|
2022-04-01 18:34:25 -05:00
|
|
|
Stmt::Read(assignable) => {
|
2021-06-18 16:05:50 -05:00
|
|
|
let mut value = 0;
|
|
|
|
for _ in 0..READ_BITS {
|
|
|
|
value <<= 1;
|
2022-02-12 17:58:50 -06:00
|
|
|
value += self.get_bit()? as isize;
|
2021-06-18 16:05:50 -05:00
|
|
|
}
|
|
|
|
|
2021-11-05 18:09:53 -05:00
|
|
|
self.assign(assignable, Value::Int(value))?;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(HaltStatus::Finished)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Assign a value to an Assignable.
|
|
|
|
fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
|
|
|
|
match dest.kind {
|
|
|
|
AssignableKind::Variable => {
|
|
|
|
self.get_var_mut(&dest.ident)?.value.replace(value);
|
|
|
|
}
|
|
|
|
AssignableKind::Index { ref indices } => {
|
|
|
|
let mut cell = self.get_var_rc(&dest.ident)?;
|
|
|
|
for index in indices {
|
|
|
|
let index = self.eval_expr(index)?;
|
|
|
|
|
2021-11-27 11:02:41 -06:00
|
|
|
let next_cell = match &mut *cell.borrow_mut() {
|
2021-11-05 18:09:53 -05:00
|
|
|
Value::Cart(c) => {
|
2021-11-27 11:02:41 -06:00
|
|
|
// cell is a cart, so we can do simple
|
|
|
|
// indexing.
|
2021-11-05 18:09:53 -05:00
|
|
|
if let Some(x) = c.get(&index) {
|
2021-11-27 11:02:41 -06:00
|
|
|
// cell[index] exists, get a shared
|
|
|
|
// reference to it.
|
2022-03-01 15:13:49 -06:00
|
|
|
ValueRef::clone(x)
|
2021-11-05 18:09:53 -05:00
|
|
|
} else {
|
2021-11-27 11:02:41 -06:00
|
|
|
// cell[index] does not exist, so we
|
|
|
|
// insert an empty cart by default
|
|
|
|
// instead.
|
2022-03-01 15:13:49 -06:00
|
|
|
let next_cell = ValueRef::new(Value::Cart(Default::default()));
|
|
|
|
c.insert(index, ValueRef::clone(&next_cell));
|
2021-11-27 11:02:41 -06:00
|
|
|
next_cell
|
2021-11-05 18:09:53 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
x => {
|
2021-11-27 11:02:41 -06:00
|
|
|
// cell is not a cart; `take` it, convert
|
|
|
|
// it into a cart, and write the result
|
|
|
|
// back into it.
|
|
|
|
let mut cart = take(x).into_cart();
|
2022-03-01 15:13:49 -06:00
|
|
|
let next_cell = ValueRef::new(Value::Cart(Default::default()));
|
|
|
|
cart.insert(index, ValueRef::clone(&next_cell));
|
2021-11-27 11:02:41 -06:00
|
|
|
*x = Value::Cart(cart);
|
|
|
|
next_cell
|
2021-11-05 18:09:53 -05:00
|
|
|
}
|
2021-11-27 11:02:41 -06:00
|
|
|
};
|
2021-11-05 18:09:53 -05:00
|
|
|
cell = next_cell;
|
2021-10-23 14:53:21 -05:00
|
|
|
}
|
2021-11-05 18:09:53 -05:00
|
|
|
cell.replace(value);
|
2021-06-18 16:05:50 -05:00
|
|
|
}
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
2021-05-23 18:46:42 -05:00
|
|
|
|
2021-11-05 18:09:53 -05:00
|
|
|
Ok(())
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
2021-05-25 13:26:01 -05:00
|
|
|
|
2021-06-12 22:07:58 -05:00
|
|
|
/// Call a function with the given arguments (i.e., actual
|
|
|
|
/// parameters). If the function invocation fails for some reason,
|
|
|
|
/// report the error at `span`.
|
2022-04-01 18:22:46 -05:00
|
|
|
fn fn_call(
|
|
|
|
&mut self,
|
|
|
|
func: Functio,
|
2022-04-01 18:34:25 -05:00
|
|
|
args: &[Spanned<Expr>],
|
2022-04-01 18:22:46 -05:00
|
|
|
span: &Range<usize>,
|
|
|
|
) -> Result<(), Error> {
|
2021-06-14 16:19:56 -05:00
|
|
|
// Arguments that are ExprKind::Variable are pass by
|
|
|
|
// reference; all other expressions are pass by value.
|
2021-06-13 12:06:38 -05:00
|
|
|
let args = args
|
|
|
|
.iter()
|
2021-06-14 16:19:56 -05:00
|
|
|
.map(|arg| {
|
2022-04-01 18:34:25 -05:00
|
|
|
if let Expr::Variable(name) = &arg.item {
|
|
|
|
self.get_var_rc(&Spanned::new(name.to_owned(), arg.span.clone()))
|
2021-06-14 16:19:56 -05:00
|
|
|
} else {
|
2022-03-01 15:13:49 -06:00
|
|
|
self.eval_expr(arg).map(ValueRef::new)
|
2021-06-14 16:19:56 -05:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect::<Result<Vec<_>, Error>>()?;
|
2021-06-13 12:06:38 -05:00
|
|
|
|
2021-12-14 14:45:44 -06:00
|
|
|
self.fn_call_with_values(func, &args, span)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fn_call_with_values(
|
|
|
|
&mut self,
|
|
|
|
func: Functio,
|
2022-03-01 15:13:49 -06:00
|
|
|
args: &[ValueRef],
|
2021-12-14 14:45:44 -06:00
|
|
|
span: &Range<usize>,
|
|
|
|
) -> Result<(), Error> {
|
2021-06-12 22:07:58 -05:00
|
|
|
match func {
|
2021-12-08 15:56:12 -06:00
|
|
|
Functio::Bf {
|
2021-06-12 22:07:58 -05:00
|
|
|
instructions,
|
|
|
|
tape_len,
|
|
|
|
} => {
|
|
|
|
let mut input: Vec<u8> = vec![];
|
|
|
|
for arg in args {
|
2021-06-13 12:06:38 -05:00
|
|
|
arg.borrow().bf_write(&mut input);
|
2021-06-12 22:07:58 -05:00
|
|
|
}
|
2021-07-22 22:11:55 -05:00
|
|
|
|
2021-06-12 22:07:58 -05:00
|
|
|
let mut output = vec![];
|
|
|
|
|
|
|
|
crate::brian::Interpreter::from_ascii_with_tape_limit(
|
|
|
|
&instructions,
|
|
|
|
&input as &[_],
|
|
|
|
tape_len,
|
|
|
|
)
|
|
|
|
.interpret_with_output(&mut output)
|
|
|
|
.map_err(|e| Error {
|
|
|
|
kind: ErrorKind::BfInterpretError(e),
|
|
|
|
span: span.to_owned(),
|
|
|
|
})?;
|
|
|
|
|
|
|
|
stdout()
|
|
|
|
.write_all(&output)
|
|
|
|
.expect("Failed to write to stdout");
|
|
|
|
}
|
2021-12-08 15:56:12 -06:00
|
|
|
Functio::Able { params, body } => {
|
2021-06-12 22:07:58 -05:00
|
|
|
if params.len() != args.len() {
|
|
|
|
return Err(Error {
|
|
|
|
kind: ErrorKind::MismatchedArgumentError,
|
|
|
|
span: span.to_owned(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
self.stack.push(Default::default());
|
|
|
|
|
|
|
|
for (param, arg) in params.iter().zip(args.iter()) {
|
2021-06-13 12:06:38 -05:00
|
|
|
self.decl_var_shared(param, arg.to_owned());
|
2021-06-12 22:07:58 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
let res = self.eval_stmts_hs(&body, false);
|
|
|
|
|
|
|
|
self.stack.pop();
|
|
|
|
res?;
|
|
|
|
}
|
2022-02-12 14:14:55 -06:00
|
|
|
Functio::Builtin(b) => b.call(args).map_err(|e| Error::new(e, span.clone()))?,
|
2021-12-14 14:45:44 -06:00
|
|
|
Functio::Chain { functios, kind } => {
|
|
|
|
let (left_functio, right_functio) = *functios;
|
2021-12-14 16:05:21 -06:00
|
|
|
match kind {
|
2021-12-14 16:02:55 -06:00
|
|
|
crate::variables::FunctioChainKind::Equal => {
|
2021-12-14 15:56:40 -06:00
|
|
|
let (l, r) = args.split_at(args.len() / 2);
|
2021-12-14 16:05:21 -06:00
|
|
|
|
|
|
|
self.fn_call_with_values(left_functio, l, span)?;
|
|
|
|
self.fn_call_with_values(right_functio, r, span)?;
|
2021-12-14 15:56:40 -06:00
|
|
|
}
|
2021-12-14 16:02:55 -06:00
|
|
|
crate::variables::FunctioChainKind::ByArity => {
|
2021-12-14 16:05:21 -06:00
|
|
|
let (l, r) =
|
|
|
|
Self::deinterlace(args, (left_functio.arity(), right_functio.arity()));
|
|
|
|
|
|
|
|
self.fn_call_with_values(left_functio, &l, span)?;
|
|
|
|
self.fn_call_with_values(right_functio, &r, span)?;
|
2021-12-14 15:56:40 -06:00
|
|
|
}
|
2021-12-14 14:45:44 -06:00
|
|
|
};
|
|
|
|
}
|
2021-08-28 16:27:35 -05:00
|
|
|
Functio::Eval(code) => {
|
2021-09-01 10:46:17 -05:00
|
|
|
if !args.is_empty() {
|
2021-08-28 16:27:35 -05:00
|
|
|
return Err(Error {
|
|
|
|
kind: ErrorKind::MismatchedArgumentError,
|
|
|
|
span: span.to_owned(),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
let stmts = crate::parser::Parser::new(&code).init()?;
|
2021-08-28 17:26:40 -05:00
|
|
|
self.eval_stmts(&stmts)?;
|
2021-08-28 16:27:35 -05:00
|
|
|
}
|
2021-06-12 22:07:58 -05:00
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2022-03-30 13:55:05 -05:00
|
|
|
fn deinterlace(args: &[ValueRef], arities: (usize, usize)) -> (Vec<ValueRef>, Vec<ValueRef>) {
|
2021-12-14 15:56:40 -06:00
|
|
|
let n_alternations = usize::min(arities.0, arities.1);
|
|
|
|
let (extra_l, extra_r) = match Ord::cmp(&arities.0, &arities.1) {
|
|
|
|
Ordering::Less => (0, arities.1 - arities.0),
|
|
|
|
Ordering::Equal => (0, 0),
|
|
|
|
Ordering::Greater => (arities.0 - arities.1, 0),
|
|
|
|
};
|
|
|
|
|
|
|
|
(
|
|
|
|
args.chunks(2)
|
|
|
|
.take(n_alternations)
|
2022-03-01 15:13:49 -06:00
|
|
|
.map(|chunk| ValueRef::clone(&chunk[0]))
|
2021-12-14 15:56:40 -06:00
|
|
|
.chain(
|
|
|
|
args[2 * n_alternations..]
|
|
|
|
.iter()
|
2022-03-01 15:13:49 -06:00
|
|
|
.map(ValueRef::clone)
|
2021-12-14 15:56:40 -06:00
|
|
|
.take(extra_l),
|
|
|
|
)
|
|
|
|
.collect(),
|
|
|
|
args.chunks(2)
|
|
|
|
.take(n_alternations)
|
2022-03-01 15:13:49 -06:00
|
|
|
.map(|chunk| ValueRef::clone(&chunk[1]))
|
2021-12-14 15:56:40 -06:00
|
|
|
.chain(
|
|
|
|
args[2 * n_alternations..]
|
|
|
|
.iter()
|
2022-03-01 15:13:49 -06:00
|
|
|
.map(ValueRef::clone)
|
2021-12-14 15:56:40 -06:00
|
|
|
.take(extra_r),
|
|
|
|
)
|
|
|
|
.collect(),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2021-06-18 16:05:50 -05:00
|
|
|
/// Get a single bit from the bit buffer, or refill it from
|
|
|
|
/// standard input if it is empty.
|
|
|
|
fn get_bit(&mut self) -> Result<bool, Error> {
|
|
|
|
const BITS_PER_BYTE: u8 = 8;
|
|
|
|
|
|
|
|
if self.read_buf.is_empty() {
|
|
|
|
let mut data = [0];
|
|
|
|
stdin().read_exact(&mut data)?;
|
|
|
|
|
|
|
|
for n in (0..BITS_PER_BYTE).rev() {
|
|
|
|
self.read_buf.push_back(((data[0] >> n) & 1) != 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(self
|
|
|
|
.read_buf
|
|
|
|
.pop_front()
|
|
|
|
.expect("We just pushed to the buffer if it was empty"))
|
|
|
|
}
|
|
|
|
|
2021-05-26 21:30:12 -05:00
|
|
|
/// Get the value of a variable. Throw an error if the variable is
|
|
|
|
/// inaccessible or banned.
|
2022-04-01 18:34:25 -05:00
|
|
|
fn get_var(&self, name: &Spanned<String>) -> Result<Value, Error> {
|
2021-05-26 21:30:12 -05:00
|
|
|
// One-letter names are reserved as base55 numbers.
|
2022-04-01 18:34:25 -05:00
|
|
|
let mut chars = name.item.chars();
|
2021-05-26 21:30:12 -05:00
|
|
|
if let (Some(first), None) = (chars.next(), chars.next()) {
|
|
|
|
return Ok(Value::Int(base_55::char2num(first)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, search for the name in the stack from top to
|
|
|
|
// bottom.
|
2021-05-25 13:26:01 -05:00
|
|
|
match self
|
|
|
|
.stack
|
|
|
|
.iter()
|
|
|
|
.rev()
|
2022-04-01 18:34:25 -05:00
|
|
|
.find_map(|scope| scope.variables.get(&name.item))
|
2021-05-25 13:26:01 -05:00
|
|
|
{
|
|
|
|
Some(var) => {
|
|
|
|
if !var.melo {
|
2021-06-13 12:06:38 -05:00
|
|
|
Ok(var.value.borrow().clone())
|
2021-05-25 13:26:01 -05:00
|
|
|
} else {
|
|
|
|
Err(Error {
|
2022-04-01 18:34:25 -05:00
|
|
|
kind: ErrorKind::MeloVariable(name.item.to_owned()),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: name.span.clone(),
|
2021-05-25 13:26:01 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => Err(Error {
|
2022-04-01 18:34:25 -05:00
|
|
|
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: name.span.clone(),
|
2021-05-25 13:26:01 -05:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Get a mutable reference to a variable. Throw an error if the
|
|
|
|
/// variable is inaccessible or banned.
|
2022-04-01 18:34:25 -05:00
|
|
|
fn get_var_mut(&mut self, name: &Spanned<String>) -> Result<&mut Variable, Error> {
|
2021-05-26 21:30:12 -05:00
|
|
|
// This function has a lot of duplicated code with `get_var`,
|
|
|
|
// which I feel like is a bad sign...
|
2021-05-25 13:26:01 -05:00
|
|
|
match self
|
|
|
|
.stack
|
|
|
|
.iter_mut()
|
|
|
|
.rev()
|
2022-04-01 18:34:25 -05:00
|
|
|
.find_map(|scope| scope.variables.get_mut(&name.item))
|
2021-05-25 13:26:01 -05:00
|
|
|
{
|
|
|
|
Some(var) => {
|
|
|
|
if !var.melo {
|
|
|
|
Ok(var)
|
|
|
|
} else {
|
|
|
|
Err(Error {
|
2022-04-01 18:34:25 -05:00
|
|
|
kind: ErrorKind::MeloVariable(name.item.to_owned()),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: name.span.clone(),
|
2021-05-25 13:26:01 -05:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => Err(Error {
|
2022-04-01 18:34:25 -05:00
|
|
|
kind: ErrorKind::UnknownVariable(name.item.to_owned()),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: name.span.clone(),
|
2021-05-25 13:26:01 -05:00
|
|
|
}),
|
|
|
|
}
|
|
|
|
}
|
2021-06-02 15:29:31 -05:00
|
|
|
|
2021-06-13 12:06:38 -05:00
|
|
|
/// Get an Rc'd pointer to the value of a variable. Throw an error
|
|
|
|
/// if the variable is inaccessible or banned.
|
2022-04-01 18:34:25 -05:00
|
|
|
fn get_var_rc(&mut self, name: &Spanned<String>) -> Result<ValueRef, Error> {
|
2021-06-13 12:06:38 -05:00
|
|
|
Ok(self.get_var_mut(name)?.value.clone())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Declare a new variable, with the given initial value.
|
2021-06-02 15:29:31 -05:00
|
|
|
fn decl_var(&mut self, name: &str, value: Value) {
|
2022-03-01 15:13:49 -06:00
|
|
|
self.decl_var_shared(name, ValueRef::new(value));
|
2021-06-13 12:06:38 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Declare a new variable, with the given shared initial value.
|
2022-03-01 15:13:49 -06:00
|
|
|
fn decl_var_shared(&mut self, name: &str, value: ValueRef) {
|
2021-06-02 15:29:31 -05:00
|
|
|
self.stack
|
|
|
|
.iter_mut()
|
|
|
|
.last()
|
|
|
|
.expect("Declaring variable on empty stack")
|
|
|
|
.variables
|
|
|
|
.insert(name.to_owned(), Variable { melo: false, value });
|
|
|
|
}
|
2021-05-20 18:18:01 -05:00
|
|
|
}
|
2021-05-27 10:05:57 -05:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-04-01 18:34:25 -05:00
|
|
|
use crate::ast::Expr;
|
2021-06-07 16:18:07 -05:00
|
|
|
|
2021-05-27 10:05:57 -05:00
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn basic_expression_test() {
|
|
|
|
// Check that 2 + 2 = 4.
|
2021-06-07 17:35:49 -05:00
|
|
|
let env = ExecEnv::new();
|
2021-05-27 10:05:57 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.eval_expr(&Spanned {
|
|
|
|
item: Expr::BinOp {
|
|
|
|
lhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(2)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
2022-04-01 18:34:25 -05:00
|
|
|
rhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(2)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
|
|
|
kind: crate::ast::BinOpKind::Add,
|
|
|
|
},
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1
|
2021-06-07 16:18:07 -05:00
|
|
|
})
|
2021-05-27 10:05:57 -05:00
|
|
|
.unwrap(),
|
|
|
|
Value::Int(4)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2021-07-22 22:27:17 -05:00
|
|
|
fn type_coercions() {
|
2022-03-30 13:55:05 -05:00
|
|
|
// The sum of an integer and an aboolean causes an aboolean
|
2021-07-22 22:27:17 -05:00
|
|
|
// coercion.
|
2022-03-30 13:55:05 -05:00
|
|
|
use crate::variables::Abool;
|
|
|
|
|
2021-06-07 17:35:49 -05:00
|
|
|
let env = ExecEnv::new();
|
2021-07-22 22:27:17 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.eval_expr(&Spanned {
|
|
|
|
item: Expr::BinOp {
|
|
|
|
lhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(2)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
2022-04-01 18:34:25 -05:00
|
|
|
rhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Abool(Abool::Always)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
|
|
|
kind: crate::ast::BinOpKind::Add,
|
|
|
|
},
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1
|
2021-05-27 10:05:57 -05:00
|
|
|
})
|
2021-07-22 22:27:17 -05:00
|
|
|
.unwrap(),
|
|
|
|
Value::Int(3)
|
|
|
|
);
|
2021-05-27 10:05:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn overflow_should_not_panic() {
|
|
|
|
// Integer overflow should throw a recoverable error instead
|
|
|
|
// of panicking.
|
2021-06-07 17:35:49 -05:00
|
|
|
let env = ExecEnv::new();
|
2021-06-15 11:29:52 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.eval_expr(&Spanned {
|
|
|
|
item: Expr::BinOp {
|
|
|
|
lhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(isize::MAX)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
2022-04-01 18:34:25 -05:00
|
|
|
rhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(1)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
|
|
|
kind: crate::ast::BinOpKind::Add,
|
|
|
|
},
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1
|
2021-05-27 10:05:57 -05:00
|
|
|
})
|
2021-06-15 11:29:52 -05:00
|
|
|
.unwrap(),
|
2022-02-22 15:49:56 -06:00
|
|
|
Value::Int(-9223372036854775808)
|
2021-06-15 11:29:52 -05:00
|
|
|
);
|
2021-05-30 13:24:16 -05:00
|
|
|
|
|
|
|
// And the same for divide by zero.
|
2021-06-15 11:29:52 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.eval_expr(&Spanned {
|
|
|
|
item: Expr::BinOp {
|
|
|
|
lhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(84)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
2022-04-01 18:34:25 -05:00
|
|
|
rhs: Box::new(Spanned {
|
|
|
|
item: Expr::Literal(Value::Int(0)),
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1,
|
2021-06-07 16:18:07 -05:00
|
|
|
}),
|
2021-06-07 17:35:49 -05:00
|
|
|
kind: crate::ast::BinOpKind::Divide,
|
2021-06-07 16:18:07 -05:00
|
|
|
},
|
2021-06-07 19:57:44 -05:00
|
|
|
span: 1..1
|
2021-05-30 13:24:16 -05:00
|
|
|
})
|
2021-06-15 11:29:52 -05:00
|
|
|
.unwrap(),
|
2021-08-30 15:55:31 -05:00
|
|
|
Value::Int(2)
|
2021-06-15 11:29:52 -05:00
|
|
|
);
|
2021-05-27 10:05:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// From here on out, I'll use this function to parse and run
|
|
|
|
// expressions, because writing out abstract syntax trees by hand
|
|
|
|
// takes forever and is error-prone.
|
|
|
|
fn eval(env: &mut ExecEnv, src: &str) -> Result<Value, Error> {
|
|
|
|
let mut parser = crate::parser::Parser::new(src);
|
|
|
|
|
|
|
|
// We can assume there won't be any syntax errors in the
|
|
|
|
// interpreter tests.
|
|
|
|
let ast = parser.init().unwrap();
|
2021-06-07 19:57:44 -05:00
|
|
|
env.eval_stmts(&ast).map(|()| Value::Nul)
|
2021-05-27 10:05:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn variable_decl_and_assignment() {
|
2021-06-15 11:46:01 -05:00
|
|
|
// Functions have no return values, so use some
|
|
|
|
// pass-by-reference hacks to detect the correct
|
|
|
|
// functionality.
|
|
|
|
let mut env = ExecEnv::new();
|
|
|
|
|
2021-05-27 10:05:57 -05:00
|
|
|
// Declaring and reading from a variable.
|
2021-06-15 11:46:01 -05:00
|
|
|
eval(&mut env, "var foo = 32; var bar = foo + 1;").unwrap();
|
2021-05-27 10:05:57 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.get_var(&Spanned {
|
|
|
|
item: "bar".to_owned(),
|
2021-06-15 11:46:01 -05:00
|
|
|
span: 1..1,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
2021-05-27 10:05:57 -05:00
|
|
|
Value::Int(33)
|
|
|
|
);
|
|
|
|
|
2021-06-15 11:46:01 -05:00
|
|
|
// Assigning an existing variable.
|
2022-02-22 15:49:56 -06:00
|
|
|
eval(&mut env, "foo = /*hi*/;").unwrap();
|
2021-05-27 10:05:57 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.get_var(&Spanned {
|
|
|
|
item: "foo".to_owned(),
|
2021-06-15 11:46:01 -05:00
|
|
|
span: 1..1,
|
|
|
|
})
|
|
|
|
.unwrap(),
|
|
|
|
Value::Str("hi".to_owned())
|
2021-05-27 10:05:57 -05:00
|
|
|
);
|
2021-05-30 13:24:16 -05:00
|
|
|
|
|
|
|
// But variable assignment should be illegal when the variable
|
|
|
|
// hasn't been declared in advance.
|
2021-06-15 11:46:01 -05:00
|
|
|
eval(&mut env, "invalid = bar + 1;").unwrap_err();
|
2021-05-27 10:05:57 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn scope_visibility_rules() {
|
|
|
|
// Declaration and assignment of variables declared in an `if`
|
|
|
|
// statement should have no effect on those declared outside
|
|
|
|
// of it.
|
2021-06-15 11:46:01 -05:00
|
|
|
let mut env = ExecEnv::new();
|
|
|
|
eval(
|
|
|
|
&mut env,
|
2022-03-30 14:03:17 -05:00
|
|
|
"var foo = 1; foo = 2; if (always) { var foo = 3; foo = 4; }",
|
2021-06-15 11:46:01 -05:00
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
2021-05-27 10:05:57 -05:00
|
|
|
assert_eq!(
|
2022-04-01 18:34:25 -05:00
|
|
|
env.get_var(&Spanned {
|
|
|
|
item: "foo".to_owned(),
|
2021-06-15 11:46:01 -05:00
|
|
|
span: 1..1,
|
|
|
|
})
|
2021-05-27 10:05:57 -05:00
|
|
|
.unwrap(),
|
2021-06-15 11:46:01 -05:00
|
|
|
Value::Int(2)
|
2021-05-27 10:05:57 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|