ablescript/
interpret.rs

1//! Expression evaluator and statement interpreter.
2//!
3//! To interpret a piece of AbleScript code, you first need to
4//! construct an [ExecEnv], which is responsible for storing the stack
5//! of local variable and function definitions accessible from an
6//! AbleScript snippet. You can then call [ExecEnv::eval_stmts] to
7//! evaluate or execute any number of expressions or statements.
8
9#![deny(missing_docs)]
10
11use crate::{
12    ast::{Assignable, AssignableKind, Block, Expr, Spanned, Stmt},
13    consts::ablescript_consts,
14    error::{Error, ErrorKind},
15    host_interface::HostInterface,
16    value::{Functio, Value, ValueRef, Variable},
17};
18use rand::random;
19use std::{
20    cmp::Ordering,
21    collections::{HashMap, VecDeque},
22    mem::take,
23    ops::Range,
24};
25
26/// An environment for executing AbleScript code.
27pub struct ExecEnv<H> {
28    /// The stack, ordered such that `stack[stack.len() - 1]` is the
29    /// top-most (newest) stack frame, and `stack[0]` is the
30    /// bottom-most (oldest) stack frame.
31    stack: Vec<Scope>,
32
33    /// The `read` statement maintains a buffer of up to 7 bits,
34    /// because input comes from the operating system 8 bits at a time
35    /// (via stdin) but gets delivered to AbleScript 3 bits at a time
36    /// (via the `read` statement). We store each of those bits as
37    /// booleans to facilitate easy manipulation.
38    read_buf: VecDeque<bool>,
39
40    /// Interface to interact with the host interface
41    host_interface: H,
42
43    /// Vector of blocks to be executed at the end of the program
44    finalisers: Vec<Block>,
45}
46
47/// A set of visible variable and function definitions in a single
48/// stack frame.
49struct Scope {
50    /// The mapping from variable names to values.
51    variables: HashMap<String, Variable>,
52}
53
54impl<H> Default for ExecEnv<H>
55where
56    H: Default + HostInterface,
57{
58    fn default() -> Self {
59        Self::with_host_interface(H::default())
60    }
61}
62
63impl Default for Scope {
64    fn default() -> Self {
65        Self {
66            variables: ablescript_consts(),
67        }
68    }
69}
70
71/// The reason a successful series of statements halted.
72enum HaltStatus {
73    /// We ran out of statements to execute.
74    Finished,
75
76    /// An `enough` statement occurred at the given span, and was not
77    /// caught by a `loop` statement up to this point.
78    Enough(Range<usize>),
79
80    /// A `and again` statement occurred at the given span, and was not
81    /// caught by a `loop` statement up to this point.
82    AndAgain(Range<usize>),
83}
84
85/// The number of bits the `read` statement reads at once from
86/// standard input.
87pub const READ_BITS: u8 = 3;
88
89impl<H: HostInterface> ExecEnv<H> {
90    /// Create a new Scope with no predefined variable definitions or
91    /// other information.
92    pub fn with_host_interface(mut host_interface: H) -> Self {
93        Self {
94            stack: vec![
95                Default::default(),
96                Scope {
97                    variables: host_interface.initial_vars(),
98                },
99            ],
100            read_buf: Default::default(),
101            finalisers: vec![],
102            host_interface,
103        }
104    }
105
106    /// Create a new Scope with predefined variables
107    pub fn new_with_vars<I>(mut host_interface: H, vars: I) -> Self
108    where
109        I: IntoIterator<Item = (String, Variable)>,
110    {
111        Self {
112            stack: vec![
113                Scope {
114                    variables: ablescript_consts().into_iter().chain(vars).collect(),
115                },
116                Scope {
117                    variables: host_interface.initial_vars(),
118                },
119            ],
120            read_buf: Default::default(),
121            finalisers: vec![],
122            host_interface,
123        }
124    }
125
126    /// Execute a set of Statements in the root stack frame. Return an
127    /// error if one or more of the Stmts failed to evaluate, or if a
128    /// `enough` or `and again` statement occurred at the top level.
129    pub fn eval_stmts(&mut self, stmts: &[Spanned<Stmt>]) -> Result<(), Error> {
130        match self.eval_stmts_hs(stmts, false)? {
131            HaltStatus::Finished => Ok(()),
132            HaltStatus::Enough(span) | HaltStatus::AndAgain(span) => Err(Error {
133                // It's an error to issue a `enough` outside of a
134                // `loop` statement.
135                kind: ErrorKind::LoopOpOutsideLoop,
136                span,
137            }),
138        }?;
139
140        while !self.finalisers.is_empty() {
141            for block in std::mem::take(&mut self.finalisers) {
142                self.eval_stmts_hs(&block, true)?;
143            }
144        }
145
146        Ok(())
147    }
148
149    /// The same as `eval_stmts`, but report "enough" and "and again"
150    /// exit codes as normal conditions in a HaltStatus enum, and
151    /// create a new stack frame if `stackframe` is true.
152    ///
153    /// `interpret`-internal code should typically prefer this
154    /// function over `eval_stmts`.
155    fn eval_stmts_hs(
156        &mut self,
157        stmts: &[Spanned<Stmt>],
158        stackframe: bool,
159    ) -> Result<HaltStatus, Error> {
160        let init_depth = self.stack.len();
161
162        if stackframe {
163            self.stack.push(Default::default());
164        }
165
166        let mut final_result = Ok(HaltStatus::Finished);
167        for stmt in stmts {
168            final_result = self.eval_stmt(stmt);
169            if !matches!(final_result, Ok(HaltStatus::Finished)) {
170                break;
171            }
172        }
173
174        if stackframe {
175            self.stack.pop();
176        }
177
178        // Invariant: stack size must have net 0 change.
179        debug_assert_eq!(self.stack.len(), init_depth);
180        final_result
181    }
182
183    /// Evaluate an Expr, returning its value or an error.
184    fn eval_expr(&self, expr: &Spanned<Expr>) -> Result<Value, Error> {
185        use crate::ast::BinOpKind::*;
186        use crate::ast::Expr::*;
187
188        Ok(match &expr.item {
189            BinOp { lhs, rhs, kind } => {
190                let lhs = self.eval_expr(lhs)?;
191                let rhs = self.eval_expr(rhs)?;
192                match kind {
193                    Add => lhs + rhs,
194                    Subtract => lhs - rhs,
195                    Multiply => lhs * rhs,
196                    Divide => lhs / rhs,
197                    Greater => Value::Abool((lhs > rhs).into()),
198                    Less => Value::Abool((lhs < rhs).into()),
199                    Equal => Value::Abool((lhs == rhs).into()),
200                    NotEqual => Value::Abool((lhs != rhs).into()),
201                }
202            }
203            Aint(expr) => !self.eval_expr(expr)?,
204            Literal(lit) => lit.clone().into(),
205            Expr::Cart(members) => Value::Cart(
206                members
207                    .iter()
208                    .map(|(value, key)| {
209                        self.eval_expr(value).and_then(|value| {
210                            self.eval_expr(key).map(|key| (key, ValueRef::new(value)))
211                        })
212                    })
213                    .collect::<Result<HashMap<_, _>, _>>()?,
214            ),
215            Index { expr, index } => {
216                let value = self.eval_expr(expr)?;
217                let index = self.eval_expr(index)?;
218
219                value
220                    .into_cart()
221                    .get(&index)
222                    .map(|x| x.borrow().clone())
223                    .unwrap_or(Value::Nul)
224            }
225            Len(expr) => Value::Int(self.eval_expr(expr)?.length()),
226            Keys(expr) => Value::Cart(
227                self.eval_expr(expr)?
228                    .into_cart()
229                    .into_keys()
230                    .enumerate()
231                    .map(|(i, k)| (Value::Int(i as isize + 1), ValueRef::new(k)))
232                    .collect(),
233            ),
234            // TODO: not too happy with constructing an artificial
235            // Ident here.
236            Variable(name) => {
237                self.get_var_value(&Spanned::new(name.to_owned(), expr.span.clone()))?
238            }
239        })
240    }
241
242    /// Perform the action indicated by a statement.
243    fn eval_stmt(&mut self, stmt: &Spanned<Stmt>) -> Result<HaltStatus, Error> {
244        match &stmt.item {
245            Stmt::Print { expr, newline } => {
246                let value = self.eval_expr(expr)?;
247                self.host_interface
248                    .print(&value.to_string(), *newline)
249                    .map_err(|e| Error::new(e.into(), stmt.span.clone()))?;
250            }
251            Stmt::Dim { ident, init } => {
252                let init = match init {
253                    Some(e) => self.eval_expr(e)?,
254                    None => Value::Nul,
255                };
256
257                self.decl_var(&ident.item, init);
258            }
259            Stmt::Functio {
260                ident,
261                params,
262                body,
263            } => {
264                self.decl_var(
265                    &ident.item,
266                    Value::Functio(Functio::Able {
267                        params: params.iter().map(|ident| ident.item.to_owned()).collect(),
268                        body: body.to_owned(),
269                    }),
270                );
271            }
272            Stmt::BfFunctio {
273                ident,
274                tape_len,
275                code,
276            } => {
277                self.decl_var(
278                    &ident.item,
279                    Value::Functio(Functio::Bf {
280                        instructions: code.to_owned(),
281                        tape_len: tape_len
282                            .as_ref()
283                            .map(|tape_len| {
284                                self.eval_expr(tape_len).map(|v| v.into_isize() as usize)
285                            })
286                            .unwrap_or(Ok(crate::brian::DEFAULT_TAPE_SIZE_LIMIT))?,
287                    }),
288                );
289            }
290            Stmt::Unless { cond, body } => {
291                if !self.eval_expr(cond)?.into_abool().to_bool() {
292                    return self.eval_stmts_hs(body, true);
293                }
294            }
295            Stmt::Call { expr, args } => {
296                let func = self.eval_expr(expr)?.into_functio();
297                return self.fn_call(func, args, &stmt.span);
298            }
299            Stmt::Loop { body } => loop {
300                let res = self.eval_stmts_hs(body, true)?;
301                match res {
302                    HaltStatus::Finished => (),
303                    HaltStatus::Enough(_) => break,
304                    HaltStatus::AndAgain(_) => continue,
305                }
306            },
307            Stmt::Assign { assignable, value } => {
308                self.assign(assignable, self.eval_expr(value)?)?;
309            }
310            Stmt::Enough => {
311                return Ok(HaltStatus::Enough(stmt.span.clone()));
312            }
313            Stmt::AndAgain => {
314                return Ok(HaltStatus::AndAgain(stmt.span.clone()));
315            }
316            Stmt::Melo(ident) => match self.get_var_mut(ident)? {
317                var @ Variable::Ref(_) => *var = Variable::Melo,
318                Variable::Melo => {
319                    for s in &mut self.stack {
320                        if s.variables.remove(&ident.item).is_some() {
321                            break;
322                        }
323                    }
324                }
325            },
326            Stmt::Finally(block) => self.finalisers.push(block.clone()),
327            Stmt::Rlyeh => {
328                // Maybe print a creepy error message or something
329                // here at some point. ~~Alex
330                let code = random();
331                self.host_interface.exit(code);
332                return Err(Error::new(
333                    ErrorKind::NonExitingRlyeh(code),
334                    stmt.span.clone(),
335                ));
336            }
337            Stmt::Rickroll => {
338                self.host_interface
339                    .print(include_str!("rickroll"), false)
340                    .map_err(|e| Error::new(e.into(), stmt.span.clone()))?;
341            }
342            Stmt::Read(assignable) => {
343                let mut value = 0;
344                for _ in 0..READ_BITS {
345                    value <<= 1;
346                    value += self
347                        .get_bit()
348                        .map_err(|e| Error::new(e, stmt.span.clone()))?
349                        as isize;
350                }
351
352                self.assign(assignable, Value::Int(value))?;
353            }
354        }
355
356        Ok(HaltStatus::Finished)
357    }
358
359    /// Assign a value to an Assignable.
360    fn assign(&mut self, dest: &Assignable, value: Value) -> Result<(), Error> {
361        match dest.kind {
362            AssignableKind::Variable => {
363                self.get_var_rc_mut(&dest.ident)?.replace(value);
364            }
365            AssignableKind::Index { ref indices } => {
366                let mut cell = self.get_var_rc_mut(&dest.ident)?.clone();
367                for index in indices {
368                    let index = self.eval_expr(index)?;
369
370                    let next_cell = match &mut *cell.borrow_mut() {
371                        Value::Cart(c) => {
372                            // cell is a cart, so we can do simple
373                            // indexing.
374                            if let Some(x) = c.get(&index) {
375                                // cell[index] exists, get a shared
376                                // reference to it.
377                                ValueRef::clone(x)
378                            } else {
379                                // cell[index] does not exist, so we
380                                // insert an empty cart by default
381                                // instead.
382                                let next_cell = ValueRef::new(Value::Cart(Default::default()));
383                                c.insert(index, ValueRef::clone(&next_cell));
384                                next_cell
385                            }
386                        }
387                        x => {
388                            // cell is not a cart; `take` it, convert
389                            // it into a cart, and write the result
390                            // back into it.
391                            let mut cart = take(x).into_cart();
392                            let next_cell = ValueRef::new(Value::Cart(Default::default()));
393                            cart.insert(index, ValueRef::clone(&next_cell));
394                            *x = Value::Cart(cart);
395                            next_cell
396                        }
397                    };
398                    cell = next_cell;
399                }
400                cell.replace(value);
401            }
402        }
403
404        Ok(())
405    }
406
407    /// Call a function with the given arguments (i.e., actual
408    /// parameters). If the function invocation fails for some reason,
409    /// report the error at `span`.
410    fn fn_call(
411        &mut self,
412        func: Functio,
413        args: &[Spanned<Expr>],
414        span: &Range<usize>,
415    ) -> Result<HaltStatus, Error> {
416        // Arguments that are ExprKind::Variable are pass by
417        // reference; all other expressions are pass by value.
418        let args = args
419            .iter()
420            .map(|arg| {
421                if let Expr::Variable(name) = &arg.item {
422                    self.get_var_rc_mut(&Spanned::new(name.to_owned(), arg.span.clone()))
423                        .cloned()
424                } else {
425                    self.eval_expr(arg).map(ValueRef::new)
426                }
427            })
428            .collect::<Result<Vec<_>, Error>>()?;
429
430        self.fn_call_with_values(func, &args, span)
431    }
432
433    fn fn_call_with_values(
434        &mut self,
435        func: Functio,
436        args: &[ValueRef],
437        span: &Range<usize>,
438    ) -> Result<HaltStatus, Error> {
439        match func {
440            Functio::Bf {
441                instructions,
442                tape_len,
443            } => {
444                let mut input: Vec<u8> = vec![];
445                for arg in args {
446                    arg.borrow().bf_write(&mut input);
447                }
448
449                let mut output = vec![];
450
451                crate::brian::Interpreter::from_ascii_with_tape_limit(
452                    &instructions,
453                    &input as &[_],
454                    tape_len,
455                )
456                .interpret_with_output(&mut output)
457                .map_err(|e| Error {
458                    kind: ErrorKind::Brian(e),
459                    span: span.to_owned(),
460                })?;
461
462                match String::from_utf8(output) {
463                    Ok(string) => self.host_interface.print(&string, false),
464                    Err(e) => self
465                        .host_interface
466                        .print(&format!("{:?}", e.as_bytes()), true),
467                }
468                .map_err(|e| Error::new(e.into(), span.clone()))?;
469
470                Ok(HaltStatus::Finished)
471            }
472            Functio::Able { params, body } => {
473                self.stack.push(Default::default());
474
475                for (param, arg) in params.iter().zip(args.iter()) {
476                    self.decl_var_shared(param, arg.to_owned());
477                }
478
479                let res = self.eval_stmts_hs(&body, false);
480
481                self.stack.pop();
482                res
483            }
484            Functio::Builtin(b) => {
485                b.call(args).map_err(|e| Error::new(e, span.clone()))?;
486                Ok(HaltStatus::Finished)
487            }
488            Functio::Chain { functios, kind } => {
489                use crate::value::functio::FunctioChainKind;
490                let (left_functio, right_functio) = *functios;
491                Ok(
492                    match match kind {
493                        FunctioChainKind::Equal => {
494                            let (l, r) = args.split_at(args.len() / 2);
495
496                            (
497                                self.fn_call_with_values(left_functio, l, span)?,
498                                self.fn_call_with_values(right_functio, r, span)?,
499                            )
500                        }
501                        FunctioChainKind::ByArity => {
502                            let (l, r) = Self::deinterlace(
503                                args,
504                                (left_functio.arity(), right_functio.arity()),
505                            );
506
507                            (
508                                self.fn_call_with_values(left_functio, &l, span)?,
509                                self.fn_call_with_values(right_functio, &r, span)?,
510                            )
511                        }
512                    } {
513                        (s, HaltStatus::Finished) => s,
514                        (HaltStatus::Finished, s) => s,
515                        (_, r) => r,
516                    },
517                )
518            }
519            Functio::Eval(code) => self.eval_stmts_hs(&crate::parser::parse(&code)?, false),
520        }
521    }
522
523    fn deinterlace(args: &[ValueRef], arities: (usize, usize)) -> (Vec<ValueRef>, Vec<ValueRef>) {
524        let n_alternations = usize::min(arities.0, arities.1);
525        let (extra_l, extra_r) = match Ord::cmp(&arities.0, &arities.1) {
526            Ordering::Less => (0, arities.1 - arities.0),
527            Ordering::Equal => (0, 0),
528            Ordering::Greater => (arities.0 - arities.1, 0),
529        };
530
531        (
532            args.chunks(2)
533                .take(n_alternations)
534                .map(|chunk| ValueRef::clone(&chunk[0]))
535                .chain(
536                    args.get(2 * n_alternations..)
537                        .iter()
538                        .copied()
539                        .flatten()
540                        .map(ValueRef::clone)
541                        .take(extra_l),
542                )
543                .collect(),
544            args.chunks(2)
545                .take(n_alternations)
546                .flat_map(|chunk| chunk.get(1))
547                .map(ValueRef::clone)
548                .chain(
549                    args.get(2 * n_alternations..)
550                        .iter()
551                        .copied()
552                        .flatten()
553                        .map(ValueRef::clone)
554                        .take(extra_r),
555                )
556                .collect(),
557        )
558    }
559
560    /// Get a single bit from the bit buffer, or refill it from
561    /// standard input if it is empty.
562    fn get_bit(&mut self) -> Result<bool, ErrorKind> {
563        const BITS_PER_BYTE: u8 = 8;
564
565        if self.read_buf.is_empty() {
566            let byte = self.host_interface.read_byte()?;
567
568            for n in (0..BITS_PER_BYTE).rev() {
569                self.read_buf.push_back(((byte >> n) & 1) != 0);
570            }
571        }
572
573        Ok(self
574            .read_buf
575            .pop_front()
576            .expect("We just pushed to the buffer if it was empty"))
577    }
578
579    /// Get the value of a variable. Throw an error if the variable is
580    /// inaccessible or banned.
581    fn get_var_value(&self, name: &Spanned<String>) -> Result<Value, Error> {
582        // Search for the name in the stack from top to bottom.
583        match self
584            .stack
585            .iter()
586            .rev()
587            .find_map(|scope| scope.variables.get(&name.item))
588        {
589            Some(Variable::Ref(r)) => Ok(r.borrow().clone()),
590            Some(Variable::Melo) => Err(Error {
591                kind: ErrorKind::MeloVariable(name.item.to_owned()),
592                span: name.span.clone(),
593            }),
594            None => Ok(Value::Undefined),
595        }
596    }
597
598    /// Get a mutable reference to a variable.
599    fn get_var_mut(&mut self, name: &Spanned<String>) -> Result<&mut Variable, Error> {
600        // This function has a lot of duplicated code with `get_var`,
601        // which I feel like is a bad sign...
602        match self
603            .stack
604            .iter_mut()
605            .rev()
606            .find_map(|scope| scope.variables.get_mut(&name.item))
607        {
608            Some(var) => Ok(var),
609            None => Err(Error {
610                kind: ErrorKind::UnknownVariable(name.item.to_owned()),
611                span: name.span.clone(),
612            }),
613        }
614    }
615
616    /// Get an reference to an Rc'd pointer to the value of a variable. Throw an error
617    /// if the variable is inaccessible or banned.
618    fn get_var_rc_mut(&mut self, name: &Spanned<String>) -> Result<&mut ValueRef, Error> {
619        match self.get_var_mut(name)? {
620            Variable::Ref(r) => Ok(r),
621            Variable::Melo => Err(Error {
622                kind: ErrorKind::MeloVariable(name.item.to_owned()),
623                span: name.span.clone(),
624            }),
625        }
626    }
627
628    /// Declare a new variable, with the given initial value.
629    fn decl_var(&mut self, name: &str, value: Value) {
630        self.decl_var_shared(name, ValueRef::new(value));
631    }
632
633    /// Declare a new variable, with the given shared initial value.
634    fn decl_var_shared(&mut self, name: &str, value: ValueRef) {
635        self.stack
636            .iter_mut()
637            .last()
638            .expect("Declaring variable on empty stack")
639            .variables
640            .insert(name.to_owned(), Variable::Ref(value));
641    }
642}
643
644#[cfg(test)]
645mod tests {
646    use super::*;
647    use crate::{
648        ast::{Expr, Literal},
649        host_interface::Standard,
650    };
651
652    #[test]
653    fn basic_expression_test() {
654        // Check that 2 + 2 = 4.
655        let env = ExecEnv::<Standard>::default();
656        assert_eq!(
657            env.eval_expr(&Spanned {
658                item: Expr::BinOp {
659                    lhs: Box::new(Spanned {
660                        item: Expr::Literal(Literal::Int(2)),
661                        span: 1..1,
662                    }),
663                    rhs: Box::new(Spanned {
664                        item: Expr::Literal(Literal::Int(2)),
665                        span: 1..1,
666                    }),
667                    kind: crate::ast::BinOpKind::Add,
668                },
669                span: 1..1
670            })
671            .unwrap(),
672            Value::Int(4)
673        )
674    }
675
676    #[test]
677    fn type_coercions() {
678        // The sum of an integer and an aboolean causes an aboolean
679        // coercion.
680
681        let env = ExecEnv::<Standard>::default();
682        assert_eq!(
683            env.eval_expr(&Spanned {
684                item: Expr::BinOp {
685                    lhs: Box::new(Spanned {
686                        item: Expr::Literal(Literal::Int(2)),
687                        span: 1..1,
688                    }),
689                    rhs: Box::new(Spanned {
690                        item: Expr::Variable("always".to_owned()),
691                        span: 1..1,
692                    }),
693                    kind: crate::ast::BinOpKind::Add,
694                },
695                span: 1..1
696            })
697            .unwrap(),
698            Value::Int(3)
699        );
700    }
701
702    #[test]
703    fn overflow_should_not_panic() {
704        // Integer overflow should throw a recoverable error instead
705        // of panicking.
706        let env = ExecEnv::<Standard>::default();
707        assert_eq!(
708            env.eval_expr(&Spanned {
709                item: Expr::BinOp {
710                    lhs: Box::new(Spanned {
711                        item: Expr::Literal(Literal::Int(isize::MAX)),
712                        span: 1..1,
713                    }),
714                    rhs: Box::new(Spanned {
715                        item: Expr::Literal(Literal::Int(1)),
716                        span: 1..1,
717                    }),
718                    kind: crate::ast::BinOpKind::Add,
719                },
720                span: 1..1
721            })
722            .unwrap(),
723            Value::Int(-9223372036854775808)
724        );
725
726        // And the same for divide by zero.
727        assert_eq!(
728            env.eval_expr(&Spanned {
729                item: Expr::BinOp {
730                    lhs: Box::new(Spanned {
731                        item: Expr::Literal(Literal::Int(84)),
732                        span: 1..1,
733                    }),
734                    rhs: Box::new(Spanned {
735                        item: Expr::Literal(Literal::Int(0)),
736                        span: 1..1,
737                    }),
738                    kind: crate::ast::BinOpKind::Divide,
739                },
740                span: 1..1
741            })
742            .unwrap(),
743            Value::Int(2)
744        );
745    }
746
747    // From here on out, I'll use this function to parse and run
748    // expressions, because writing out abstract syntax trees by hand
749    // takes forever and is error-prone.
750    fn eval(env: &mut ExecEnv<Standard>, src: &str) -> Result<Value, Error> {
751        // We can assume there won't be any syntax errors in the
752        // interpreter tests.
753        let ast = crate::parser::parse(src).unwrap();
754        env.eval_stmts(&ast).map(|()| Value::Nul)
755    }
756
757    #[test]
758    fn variable_decl_and_assignment() {
759        // Functions have no return values, so use some
760        // pass-by-reference hacks to detect the correct
761        // functionality.
762        let mut env = ExecEnv::<Standard>::default();
763
764        // Declaring and reading from a variable.
765        eval(&mut env, "foo dim 32; bar dim foo + 1;").unwrap();
766        assert_eq!(
767            env.get_var_value(&Spanned {
768                item: "bar".to_owned(),
769                span: 1..1,
770            })
771            .unwrap(),
772            Value::Int(33)
773        );
774
775        // Assigning an existing variable.
776        eval(&mut env, "/*hi*/ =: foo;").unwrap();
777        assert_eq!(
778            env.get_var_value(&Spanned {
779                item: "foo".to_owned(),
780                span: 1..1,
781            })
782            .unwrap(),
783            Value::Str("hi".to_owned())
784        );
785
786        // But variable assignment should be illegal when the variable
787        // hasn't been declared in advance.
788        eval(&mut env, "bar + 1 =: invalid;").unwrap_err();
789    }
790
791    #[test]
792    fn scope_visibility_rules() {
793        // Declaration and assignment of variables declared in an `if`
794        // statement should have no effect on those declared outside
795        // of it.
796        let mut env = ExecEnv::<Standard>::default();
797        eval(
798            &mut env,
799            "foo dim 1; 2 =: foo; unless (never) { foo dim 3; 4 =: foo; }",
800        )
801        .unwrap();
802
803        assert_eq!(
804            env.get_var_value(&Spanned {
805                item: "foo".to_owned(),
806                span: 1..1,
807            })
808            .unwrap(),
809            Value::Int(2)
810        );
811    }
812}