molt_ng/
interp.rs

1//! The Molt Interpreter
2//!
3//! The [`Interp`] struct is the primary API for embedding Molt into a Rust application.
4//! Given an `Interp`, the application may:
5//!
6//! * Evaluate scripts and expressions
7//! * Check scripts for completeness
8//! * Extend the language by defining new Molt commands in Rust
9//! * Set and get Molt variables
10//! * Access application data via the context cache
11//!
12//! The following describes the features of the [`Interp`] in general; follow the links for
13//! specifics of the various types and methods. See also [The Molt Book] for a general
14//! introduction to Molt and its API.
15//!
16//! # Interp is not Sync!
17//!
18//! The [`Interp`] class (and the rest of Molt) is intended for use in a single thread.  It is
19//! safe to have `Interps` in different threads; but use `String` (or another `Sync`)
20//! when passing data between them.  In particular, [`Value`] is not `Sync`.
21//!
22//! # Creating an Interpreter
23//!
24//! There are two ways to create an interpreter.  The usual way is to call
25//! [`Interp::new`](struct.Interp.html#method.new), which creates an interpreter and populates
26//! it with all of the standard Molt commands.  The application can then add any
27//! application-specific commands.
28//!
29//! Alternatively, [`Interp::empty`](struct.Interp.html#method.empty) creates an interpreter
30//! with no built-in commands, allowing the application to define only those commands it needs.
31//! Such an empty interpreter can be configured as the parser for data and configuration files,
32//! or as the basis for a simple console command set.
33//!
34//! **TODO**: Define a way to add various subsets of the standard commands to an initially
35//! empty interpreter.
36//!
37//! ```
38//! use molt::Interp;
39//! let mut interp = Interp::new();
40//!
41//! // add commands, evaluate scripts, etc.
42//! ```
43//!
44//! # Evaluating Scripts
45//!
46//! There are a number of ways to evaluate Molt scripts.  The simplest is to pass the script
47//! as a string to `Interp::eval`.  The interpreter evaluates the string as a Molt script, and
48//! returns either a normal [`Value`] containing the result, or a Molt error. The script is
49//! evaluated in the caller's context: if called at the application level, the script will be
50//! evaluated in the interpreter's global scope; if called by a Molt command, it will be
51//! evaluated in the scope in which that command is executing.
52//!
53//! For example, the following snippet uses the Molt `expr` command to evaluate an expression.
54//!
55//! ```
56//! use molt::Interp;
57//! use molt::molt_ok;
58//! use molt::types::*;
59//!
60//! let _ = my_func();
61//!
62//! fn my_func() -> MoltResult {
63//! // FIRST, create the interpreter and add the needed command.
64//! let mut interp = Interp::new();
65//!
66//! // NEXT, evaluate a script containing an expression,
67//! // propagating errors back to the caller
68//! let val = interp.eval("expr {2 + 2}")?;
69//! assert_eq!(val.as_str(), "4");
70//! assert_eq!(val.as_int()?, 4);
71//!
72//! molt_ok!()
73//! }
74//! ```
75//!
76//! [`Interp::eval_value`](struct.Interp.html#method.eval_value) is equivalent to
77//! `Interp::eval` but takes the script as a `Value` instead of as a `&str`.  When
78//! called at the top level, both methods convert the `break` and `continue` return codes
79//! (and any user-defined return codes) to errors; otherwise they are propagated to the caller
80//! for handling.  It is preferred to use `Interp::eval_value` when possible, as `Interp::eval`
81//! will reparse its argument each time if called multiple times on the same input.
82//!
83//! All of these methods return [`MoltResult`]:
84//!
85//! ```ignore
86//! pub type MoltResult = Result<Value, Exception>;
87//! ```
88//!
89//! [`Value`] is the type of all Molt values (i.e., values that can be passed as parameters and
90//! stored in variables).  [`Exception`] is a struct that encompasses all of the kinds of
91//! exceptional return from Molt code, including errors, `return`, `break`, and `continue`.
92//!
93//! # Evaluating Expressions
94//!
95//! In Molt, as in Standard Tcl, algebraic expressions are evaluated by the `expr` command.  At
96//! the Rust level this feature is provided by the
97//! [`Interp::expr`](struct.Interp.html#method.expr) method, which takes the expression as a
98//! [`Value`] and returns the computed `Value` or an error.
99//!
100//! There are three convenience methods,
101//! [`Interp::expr_bool`](struct.Interp.html#method.expr_bool),
102//! [`Interp::expr_int`](struct.Interp.html#method.expr_int), and
103//! [`Interp::expr_float`](struct.Interp.html#method.expr_float), which streamline the computation
104//! of a particular kind of value, and return an error if the computed result is not of that type.
105//!
106//! For example, the following code shows how a command can evaluate a string as a boolean value,
107//! as in the `if` or `while` commands:
108//!
109//! ```
110//! use molt::Interp;
111//! use molt::molt_ok;
112//! use molt::types::*;
113//!
114//! # let _ = dummy();
115//! # fn dummy() -> MoltResult {
116//! // FIRST, create the interpreter
117//! let mut interp = Interp::new();
118//!
119//! // NEXT, get an expression as a Value.  In a command body it would
120//! // usually be passed in as a Value.
121//! let expr = Value::from("1 < 2");
122//!
123//! // NEXT, evaluate it!
124//! assert!(interp.expr_bool(&expr)?);
125//! # molt_ok!()
126//! # }
127//! ```
128//!
129//! These methods will return an error if the string cannot be interpreted
130//! as an expression of the relevant type.
131//!
132//! # Defining New Commands
133//!
134//! The usual reason for embedding Molt in an application is to extend it with
135//! application-specific commands.  There are several ways to do this.
136//!
137//! The simplest method, and the one used by most of Molt's built-in commands, is to define a
138//! [`CommandFunc`] and register it with the interpreter using the
139//! [`Interp::add_command`](struct.Interp.html#method.add_command) method. A `CommandFunc` is
140//! simply a Rust function that returns a [`MoltResult`] given an interpreter and a slice of Molt
141//! [`Value`] objects representing the command name and its arguments. The function may interpret
142//! the array of arguments in any way it likes.
143//!
144//! The following example defines a command called `square` that squares an integer value.
145//!
146//! ```
147//! use molt::Interp;
148//! use molt::check_args;
149//! use molt::molt_ok;
150//! use molt::types::*;
151//!
152//! # let _ = dummy();
153//! # fn dummy() -> MoltResult {
154//! // FIRST, create the interpreter and add the needed command.
155//! let mut interp = Interp::new();
156//! interp.add_command("square", cmd_square);
157//!
158//! // NEXT, try using the new command.
159//! let val = interp.eval("square 5")?;
160//! assert_eq!(val.as_str(), "25");
161//! # molt_ok!()
162//! # }
163//!
164//! // The command: square intValue
165//! fn cmd_square(_: &mut Interp, _: ContextID, argv: &[Value]) -> MoltResult {
166//!     // FIRST, check the number of arguments.  Returns an appropriate error
167//!     // for the wrong number of arguments.
168//!     check_args(1, argv, 2, 2, "intValue")?;
169//!
170//!     // NEXT, get the intValue argument as an int.  Returns an appropriate error
171//!     // if the argument can't be interpreted as an integer.
172//!     let intValue = argv[1].as_int()?;
173//!
174//!     // NEXT, return the product.
175//!     molt_ok!(intValue * intValue)
176//! }
177//! ```
178//!
179//! The new command can then be used in a Molt interpreter:
180//!
181//! ```tcl
182//! % square 5
183//! 25
184//! % set a [square 6]
185//! 36
186//! % puts "a=$a"
187//! a=36
188//! ```
189//!
190//! # Accessing Variables
191//!
192//! Molt defines two kinds of variables, scalars and arrays.  A scalar variable is a named holder
193//! for a [`Value`].  An array variable is a named hash table whose elements are named holders
194//! for `Values`.  Each element in an array is like a scalar in its own right.  In Molt code
195//! the two kinds of variables are accessed as follows:
196//!
197//! ```tcl
198//! % set myScalar 1
199//! 1
200//! % set myArray(myElem) 2
201//! 2
202//! % puts "$myScalar $myArray(myElem)"
203//! 1 2
204//! ```
205//!
206//! In theory, any string can be a valid variable or array index string.  In practice, variable
207//! names usually follow the normal rules for identifiers: letters, digits and underscores,
208//! beginning with a letter, while array index strings usually don't contain parentheses and
209//! so forth.  But array index strings can be arbitrarily complex, and so a single TCL array can
210//! contain a vast variety of data structures.
211//!
212//! Molt commands will usually use the
213//! [`Interp::var`](struct.Interp.html#method.var),
214//! [`Interp::set_var`](struct.Interp.html#method.set_var), and
215//! [`Interp::set_var_return`](struct.Interp.html#method.set_var_return) methods to set and
216//! retrieve variables.  Each takes a variable reference as a `Value`.  `Interp::var` retrieves
217//! the variable's value as a `Value`, return an error if the variable doesn't exist.
218//! `Interp::set_var` and `Interp::set_var_return` set the variable's value, creating the
219//! variable or array element if it doesn't exist.
220//!
221//! `Interp::set_var_return` returns the value assigned to the variable, which is convenient
222//! for commands that return the value assigned to the variable.  The standard `set` command,
223//! for example, returns the assigned or retrieved value; it is defined like this:
224//!
225//! ```
226//! use molt::Interp;
227//! use molt::check_args;
228//! use molt::molt_ok;
229//! use molt::types::*;
230//!
231//! pub fn cmd_set(interp: &mut Interp, _: ContextID, argv: &[Value]) -> MoltResult {
232//!    check_args(1, argv, 2, 3, "varName ?newValue?")?;
233//!
234//!    if argv.len() == 3 {
235//!        interp.set_var_return(&argv[1], argv[2].clone())
236//!    } else {
237//!        molt_ok!(interp.var(&argv[1])?)
238//!    }
239//!}
240//! ```
241//!
242//! At times it can be convenient to explicitly access a scalar variable or array element by
243//! by name.  The methods
244//! [`Interp::scalar`](struct.Interp.html#method.scalar),
245//! [`Interp::set_scalar`](struct.Interp.html#method.set_scalar),
246//! [`Interp::set_scalar_return`](struct.Interp.html#method.set_scalar_return),
247//! [`Interp::element`](struct.Interp.html#method.element),
248//! [`Interp::set_element`](struct.Interp.html#method.set_element), and
249//! [`Interp::set_element_return`](struct.Interp.html#method.set_element_return)
250//! provide this access.
251//!
252//! # Managing Application or Library-Specific Data
253//!
254//! Molt provides a number of data types out of the box: strings, numbers, and lists.  However,
255//! any data type that can be unambiguously converted to and from a string can be easily
256//! integrated into Molt. See the [`value`] module for details.
257//!
258//! Other data types _cannot_ be represented as strings in this way, e.g., file handles,
259//! database handles, or keys into complex application data structures.  Such types can be
260//! represented as _key strings_ or as _object commands_.  In Standard TCL/TK, for example,
261//! open files are represented as strings like `file1`, `file2`, etc.  The commands for
262//! reading and writing to files know how to look these keys up in the relevant data structure.
263//! TK widgets, on the other hand, are presented as object commands: a command with subcommands
264//! where the command itself knows how to access the relevant data structure.
265//!
266//! Application-specific commands often need access to the application's data structure.
267//! Often many commands will need access to the same data structure.  This is often the case
268//! for complex binary extensions as well (families of Molt commands implemented as a reusable
269//! crate), where all of the commands in the extension need access to some body of
270//! extension-specific data.
271//!
272//! All of these patterns (and others) are implemented by means of the interpreter's
273//! _context cache_, which is a means of relating mutable data to a particular command or
274//! family of commands.  See below.
275//!
276//! # Commands and the Context Cache
277//!
278//! Most Molt commands require access only to the Molt interpreter in order to do their
279//! work.  Some need mutable or immutable access to command-specific data (which is often
280//! application-specific data).  This is provided by means of the interpreter's
281//! _context cache_:
282//!
283//! * The interpreter is asked for a new `ContextID`, an ID that is unique in that interpreter.
284//!
285//! * The client associates the context ID with a new instance of a context data structure,
286//!   usually a struct.  This data structure is added to the context cache.
287//!
288//!   * This struct may contain the data required by the command(s), or keys allowing it
289//!     to access the data elsewhere.
290//!
291//! * The `ContextID` is provided to the interpreter when adding commands that require that
292//!   context.
293//!
294//! * A command can mutably access its context data when it is executed.
295//!
296//! * The cached data is dropped when the last command referencing a `ContextID` is removed
297//!   from the interpreter.
298//!
299//! This mechanism supports all of the patterns described above.  For example, Molt's
300//! test harness provides a `test` command that defines a single test.  When it executes, it must
301//! increment a number of statistics: the total number of tests, the number of successes, the
302//! number of failures, etc.  This can be implemented as follows:
303//!
304//! ```
305//! use molt::Interp;
306//! use molt::check_args;
307//! use molt::molt_ok;
308//! use molt::types::*;
309//!
310//! // The context structure to hold the stats
311//! struct Stats {
312//!     num_tests: usize,
313//!     num_passed: usize,
314//!     num_failed: usize,
315//! }
316//!
317//! // Whatever methods the app needs
318//! impl Stats {
319//!     fn new() -> Self {
320//!         Self { num_tests: 0, num_passed: 0, num_failed: 0}
321//!     }
322//! }
323//!
324//! # let _ = dummy();
325//! # fn dummy() -> MoltResult {
326//! // Create the interpreter.
327//! let mut interp = Interp::new();
328//!
329//! // Create the context struct, assigning a context ID
330//! let context_id = interp.save_context(Stats::new());
331//!
332//! // Add the `test` command with the given context.
333//! interp.add_context_command("test", cmd_test, context_id);
334//!
335//! // Try using the new command.  It should increment the `num_passed` statistic.
336//! let val = interp.eval("test ...")?;
337//! assert_eq!(interp.context::<Stats>(context_id).num_passed, 1);
338//! # molt_ok!()
339//! # }
340//!
341//! // A stub test command.  It ignores its arguments, and
342//! // increments the `num_passed` statistic in its context.
343//! fn cmd_test(interp: &mut Interp, context_id: ContextID, argv: &[Value]) -> MoltResult {
344//!     // Pretend it passed
345//!     interp.context::<Stats>(context_id).num_passed += 1;
346//!
347//!     molt_ok!()
348//! }
349//! ```
350//!
351//! # Ensemble Commands
352//!
353//! An _ensemble command_ is simply a command with subcommands, like the standard Molt `info`
354//! and `array` commands.  At the Rust level, it is simply a command that looks up its subcommand
355//! (e.g., `argv[1]`) in an array of `Subcommand` structs and executes it as a command.
356//!
357//! The [`Interp::call_subcommand`](struct.Interp.html#method.call_subcommand) method is used
358//! to look up and call the relevant command function, handling all relevant errors in the
359//! TCL-standard way.
360//!
361//! For example, the `array` command is defined as follows.
362//!
363//! ```ignore
364//! const ARRAY_SUBCOMMANDS: [Subcommand; 6] = [
365//!     Subcommand("exists", cmd_array_exists),
366//!     Subcommand("get", cmd_array_get),
367//!     // ...
368//! ];
369//!
370//! pub fn cmd_array(interp: &mut Interp, context_id: ContextID, argv: &[Value]) -> MoltResult {
371//!     interp.call_subcommand(context_id, argv, 1, &ARRAY_SUBCOMMANDS)
372//! }
373//!
374//! pub fn cmd_array_exists(interp: &mut Interp, _: ContextID, argv: &[Value]) -> MoltResult {
375//!     check_args(2, argv, 3, 3, "arrayName")?;
376//!     molt_ok!(Value::from(interp.array_exists(argv[2].as_str())))
377//! }
378//!
379//! // ...
380//! ```
381//!
382//! The `cmd_array` and `cmd_array_exists` functions are just normal Molt `CommandFunc`
383//! functions.  The `array` command is added to the interpreter using `Interp::add_command`
384//! in the usual way. Note that the `context_id` is passed to the subcommand functions, though
385//! in this case it isn't needed.
386//!
387//! Also, notice that the call to `check_args` in `cmd_array_exists` has `2` as its first
388//! argument, rather than `1`.  That indicates that the first two arguments represent the
389//! command being called, e.g., `array exists`.
390//!
391//! # Object Commands
392//!
393//! An _object command_ is an _ensemble command_ that represents an object; the classic TCL
394//! examples are the TK widgets.  The pattern for defining object commands is as follows:
395//!
396//! * A constructor command that creates instances of the given object type.  (We use the word
397//!   *type* rather than *class* because inheritance is usually neither involved or available.)
398//!
399//! * An instance is an ensemble command:
400//!   * Whose name is provided to the constructor
401//!   * That has an associated context structure, initialized by the constructor, that belongs
402//!     to it alone.
403//!
404//! * Each of the object's subcommand functions is passed the object's context ID, so that all
405//!   can access the object's data.
406//!
407//! Thus, the constructor command will do the following:
408//!
409//! * Create and initialize a context structure, assigning it a `ContextID` via
410//!   `Interp::save_context`.
411//!   * The context structure may be initialized with default values, or configured further
412//!     based on the constructor command's arguments.
413//!
414//! * Determine a name for the new instance.
415//!   * The name is usually passed in as an argument, but can be computed.
416//!
417//! * Create the instance using `Interp::add_context_command` and the instance's ensemble
418//!   `CommandFunc`.
419//!
420//! * Usually, return the name of the newly created command.
421//!
422//! Note that there's no real difference between defining a simple ensemble like `array`, as
423//! shown above, and defining an object command as described here, except that:
424//!
425//! * The instance is usually created "on the fly" rather than at interpreter initialization.
426//! * The instance will always have data in the context cache.
427//!
428//! # Checking Scripts for Completeness
429//!
430//! The [`Interp::complete`](struct.Interp.html#method.complete) method checks whether a Molt
431//! script is complete: e.g., that it contains no unterminated quoted or braced strings,
432//! that would prevent it from being evaluated as Molt code.  This is useful when
433//! implementing a Read-Eval-Print-Loop, as it allows the REPL to easily determine whether it
434//! should evaluate the input immediately or ask for an additional line of input.
435//!
436//! [The Molt Book]: https://wduquette.github.io/molt/
437//! [`MoltResult`]: ../types/type.MoltResult.html
438//! [`Exception`]: ../types/enum.Exception.html
439//! [`CommandFunc`]: ../types/type.CommandFunc.html
440//! [`Value`]: ../value/index.html
441//! [`Interp`]: struct.Interp.html
442
443use crate::check_args;
444use crate::commands;
445use crate::dict::dict_new;
446use crate::expr;
447use crate::list::list_to_string;
448use crate::molt_err;
449use crate::molt_ok;
450use crate::parser;
451use crate::parser::Script;
452use crate::parser::Word;
453use crate::scope::ScopeStack;
454use crate::types::*;
455use crate::value::Value;
456use std::any::Any;
457use std::collections::HashMap;
458use std::rc::Rc;
459use std::time::Instant;
460
461// Constants
462const OPT_CODE: &str = "-code";
463const OPT_LEVEL: &str = "-level";
464const OPT_ERRORCODE: &str = "-errorcode";
465const OPT_ERRORINFO: &str = "-errorinfo";
466const ZERO: &str = "0";
467
468/// The Molt Interpreter.
469///
470/// The `Interp` struct is the primary API for
471/// embedding Molt into a Rust application.  The application creates an instance
472/// of `Interp`, configures with it the required set of application-specific
473/// and standard Molt commands, and then uses it to evaluate Molt scripts and
474/// expressions.  See the
475/// [module level documentation](index.html)
476/// for an overview.
477///
478/// # Example
479///
480/// By default, the `Interp` comes configured with the full set of standard
481/// Molt commands.
482///
483/// ```
484/// use molt::types::*;
485/// use molt::Interp;
486/// use molt::molt_ok;
487/// # fn dummy() -> MoltResult {
488/// let mut interp = Interp::new();
489/// let four = interp.eval("expr {2 + 2}")?;
490/// assert_eq!(four, Value::from(4));
491/// # molt_ok!()
492/// # }
493/// ```
494#[derive(Default)]
495pub struct Interp {
496    // Command Table
497    commands: HashMap<String, Rc<Command>>,
498
499    // Variable Table
500    scopes: ScopeStack,
501
502    // Context ID Counter
503    last_context_id: u64,
504
505    // Context Map
506    context_map: HashMap<ContextID, ContextBox>,
507
508    // Defines the recursion limit for Interp::eval().
509    recursion_limit: usize,
510
511    // Current number of eval levels.
512    num_levels: usize,
513
514    // Profile Map
515    profile_map: HashMap<String, ProfileRecord>,
516}
517
518/// A command defined in the interpreter.
519enum Command {
520    /// A binary command implemented as a Rust CommandFunc.
521    Native(CommandFunc, ContextID),
522
523    /// A Molt procedure
524    Proc(Procedure),
525}
526
527impl Command {
528    /// Execute the command according to its kind.
529    fn execute(&self, interp: &mut Interp, argv: &[Value]) -> MoltResult {
530        match self {
531            Command::Native(func, context_id) => func(interp, *context_id, argv),
532            Command::Proc(proc) => proc.execute(interp, argv),
533        }
534    }
535
536    /// Returns a value naming the command type.
537    fn cmdtype(&self) -> Value {
538        match self {
539            Command::Native(_, _) => Value::from("native"),
540            Command::Proc(_) => Value::from("proc"),
541        }
542    }
543
544    /// Gets the command's context, or NULL_CONTEXT if none.
545    fn context_id(&self) -> ContextID {
546        match self {
547            Command::Native(_, context_id) => *context_id,
548            _ => NULL_CONTEXT,
549        }
550    }
551
552    /// Returns true if the command is a proc, and false otherwise.
553    fn is_proc(&self) -> bool {
554        if let Command::Proc(_) = self {
555            true
556        } else {
557            false
558        }
559    }
560}
561
562/// Sentinal value for command functions with no related context.
563///
564/// **NOTE**: it would make no sense to use `Option<ContextID>` instead of a sentinal
565/// value.  Whether or not a command has related context is known at compile
566/// time, and is an essential part of the command's definition; it never changes.
567/// Commands with context will access the function's context_id argument; and
568/// and commands without have no reason to do so.  Using a sentinel allows the same
569/// function type to be used for all binary Molt commands with minimal hassle to the
570/// client developer.
571const NULL_CONTEXT: ContextID = ContextID(0);
572
573/// A container for a command's context struct, containing the context in a box,
574/// and a reference count.
575///
576/// The reference count is incremented when the context's ID is used with a command,
577/// and decremented when the command is forgotten.  If the reference count is
578/// decremented to zero, the context is removed.
579struct ContextBox {
580    data: Box<dyn Any>,
581    ref_count: usize,
582}
583
584impl ContextBox {
585    /// Creates a new context box for the given data, and sets its reference count to 0.
586    fn new<T: 'static>(data: T) -> Self {
587        Self {
588            data: Box::new(data),
589            ref_count: 0,
590        }
591    }
592
593    /// Increments the context's reference count.
594    fn increment(&mut self) {
595        self.ref_count += 1;
596    }
597
598    /// Decrements the context's reference count.  Returns true if the count is now 0,
599    /// and false otherwise.
600    ///
601    /// Panics if the count is already 0.
602    fn decrement(&mut self) -> bool {
603        assert!(
604            self.ref_count != 0,
605            "attempted to decrement context ref count below zero"
606        );
607        self.ref_count -= 1;
608        self.ref_count == 0
609    }
610}
611
612struct ProfileRecord {
613    count: u128,
614    nanos: u128,
615}
616
617impl ProfileRecord {
618    fn new() -> Self {
619        Self { count: 0, nanos: 0 }
620    }
621}
622
623// NOTE: The order of methods in the generated RustDoc depends on the order in this block.
624// Consequently, methods are ordered pedagogically.
625impl Interp {
626    //--------------------------------------------------------------------------------------------
627    // Constructors
628
629    /// Creates a new Molt interpreter with no commands defined.  Use this when crafting
630    /// command languages that shouldn't include the normal TCL commands, or as a base
631    /// to which specific Molt command sets can be added.
632    ///
633    /// # Example
634    ///
635    /// ```
636    /// # use molt::interp::Interp;
637    /// let mut interp = Interp::empty();
638    /// assert!(interp.command_names().is_empty());
639    /// ```
640
641    pub fn empty() -> Self {
642        let mut interp = Self {
643            recursion_limit: 1000,
644            commands: HashMap::new(),
645            last_context_id: 0,
646            context_map: HashMap::new(),
647            scopes: ScopeStack::new(),
648            num_levels: 0,
649            profile_map: HashMap::new(),
650        };
651
652        interp.set_scalar("errorInfo", Value::empty()).unwrap();
653        interp
654    }
655
656    /// Creates a new Molt interpreter that is pre-populated with the standard Molt commands.
657    /// Use [`command_names`](#method.command_names) (or the `info commands` Molt command)
658    /// to retrieve the full list, and the [`add_command`](#method.add_command) family of
659    /// methods to extend the interpreter with new commands.
660    ///
661    /// TODO: Define command sets (sets of commands that go together, so that clients can
662    /// add or remove them in groups).
663    ///
664    /// ```
665    /// # use molt::types::*;
666    /// # use molt::Interp;
667    /// # use molt::molt_ok;
668    /// # fn dummy() -> MoltResult {
669    /// let mut interp = Interp::new();
670    /// let four = interp.eval("expr {2 + 2}")?;
671    /// assert_eq!(four, Value::from(4));
672    /// # molt_ok!()
673    /// # }
674    /// ```
675    ///
676    pub fn new() -> Self {
677        let mut interp = Interp::empty();
678
679        // TODO: These commands affect the interpreter only, not the external environment.
680        // It might be desirable to subdivide them further, into those that can cause
681        // denial-of-service kinds of problems, e.g., for, while, proc, rename, and those
682        // that can't.
683        interp.add_command("append", commands::cmd_append);
684        interp.add_command("array", commands::cmd_array);
685        interp.add_command("assert_eq", commands::cmd_assert_eq);
686        interp.add_command("break", commands::cmd_break);
687        interp.add_command("catch", commands::cmd_catch);
688        interp.add_command("continue", commands::cmd_continue);
689        interp.add_command("dict", commands::cmd_dict);
690        interp.add_command("error", commands::cmd_error);
691        interp.add_command("expr", commands::cmd_expr);
692        interp.add_command("for", commands::cmd_for);
693        interp.add_command("foreach", commands::cmd_foreach);
694        interp.add_command("global", commands::cmd_global);
695        interp.add_command("if", commands::cmd_if);
696        interp.add_command("incr", commands::cmd_incr);
697        interp.add_command("info", commands::cmd_info);
698        interp.add_command("join", commands::cmd_join);
699        interp.add_command("lappend", commands::cmd_lappend);
700        interp.add_command("lindex", commands::cmd_lindex);
701        interp.add_command("list", commands::cmd_list);
702        interp.add_command("llength", commands::cmd_llength);
703        interp.add_command("proc", commands::cmd_proc);
704        interp.add_command("puts", commands::cmd_puts);
705        interp.add_command("rename", commands::cmd_rename);
706        interp.add_command("return", commands::cmd_return);
707        interp.add_command("set", commands::cmd_set);
708        interp.add_command("string", commands::cmd_string);
709        interp.add_command("throw", commands::cmd_throw);
710        interp.add_command("time", commands::cmd_time);
711        interp.add_command("unset", commands::cmd_unset);
712        interp.add_command("while", commands::cmd_while);
713
714        // TODO: Requires file access.  Ultimately, might go in an extension crate if
715        // the necessary operations aren't available in core::.
716        interp.add_command("source", commands::cmd_source);
717
718        // TODO: Useful for entire programs written in Molt; but not necessarily wanted in
719        // extension scripts.
720        interp.add_command("exit", commands::cmd_exit);
721
722        // TODO: Developer Tools
723        interp.add_command("parse", parser::cmd_parse);
724        interp.add_command("pdump", commands::cmd_pdump);
725        interp.add_command("pclear", commands::cmd_pclear);
726
727        // Populate the environment variable.
728        // TODO: Really should be a "linked" variable, where sets to it are tracked and
729        // written back to the environment.
730        interp.populate_env();
731
732        interp
733    }
734
735    /// Populates the TCL `env()` array with the process's environment variables.
736    ///
737    /// # TCL Liens
738    ///
739    /// Changes to the variable are not mirrored back into the process's environment.
740    fn populate_env(&mut self) {
741        for (key, value) in std::env::vars() {
742            // Drop the result, as there's no good reason for this to ever throw an error.
743            let _ = self.set_element("env", &key, value.into());
744        }
745    }
746
747    //--------------------------------------------------------------------------------------------
748    // Script and Expression Evaluation
749
750    /// Evaluates a script one command at a time.  Returns the [`Value`](../value/index.html)
751    /// of the last command in the script, or the value of any explicit `return` call in the
752    /// script, or any error thrown by the script.  Other
753    /// [`Exception`](../types/enum.Exception.html) values are converted to normal errors.
754    ///
755    /// Use this method (or [`eval_value`](#method.eval_value)) to evaluate arbitrary scripts,
756    /// control structure bodies, and so forth.  Prefer `eval_value` if the script is already
757    /// stored in a `Value`, as it will be more efficient if the script is evaluated multiple
758    /// times.
759    ///
760    /// # Example
761    ///
762    /// The following code shows how to evaluate a script and handle the result, whether
763    /// it's a computed `Value` or an error message (which is also a `Value`).
764    ///
765    /// ```
766    /// # use molt::types::*;
767    /// # use molt::Interp;
768    ///
769    /// let mut interp = Interp::new();
770    ///
771    /// let input = "set a 1";
772    ///
773    /// match interp.eval(input) {
774    ///    Ok(val) => {
775    ///        // Computed a Value
776    ///        println!("Value: {}", val);
777    ///    }
778    ///    Err(exception) => {
779    ///        if exception.is_error() {
780    ///            // Got an error; print it out.
781    ///            println!("Error: {}", exception.value());
782    ///        } else {
783    ///            // It's a Return.
784    ///            println!("Value: {}", exception.value());
785    ///        }
786    ///    }
787    /// }
788    /// ```
789
790    pub fn eval(&mut self, script: &str) -> MoltResult {
791        let value = Value::from(script);
792        self.eval_value(&value)
793    }
794
795    /// Evaluates the string value of a [`Value`] as a script.  Returns the `Value`
796    /// of the last command in the script, or the value of any explicit `return` call in the
797    /// script, or any error thrown by the script.  Other
798    /// [`Exception`](../types/enum.Exception.html) values are converted to normal errors.
799    ///
800    /// This method is equivalent to [`eval`](#method.eval), but works on a `Value` rather
801    /// than on a string slice.  Use it or `eval` to evaluate arbitrary scripts,
802    /// control structure bodies, and so forth.  Prefer this to `eval` if the script is already
803    /// stored in a `Value`, as it will be more efficient if the script is evaluated multiple
804    /// times.
805    ///
806    /// [`Value`]: ../value/index.html
807    pub fn eval_value(&mut self, value: &Value) -> MoltResult {
808        // TODO: Could probably do better, here.  If the value is already a list, for
809        // example, can maybe evaluate it as a command without using as_script().
810        // Tricky, though.  Don't want to have to parse it as a list.  Need a quick way
811        // to determine if something is already a list.  (Might need two methods!)
812
813        // FIRST, check the number of nesting levels
814        self.num_levels += 1;
815
816        if self.num_levels > self.recursion_limit {
817            self.num_levels -= 1;
818            return molt_err!("too many nested calls to Interp::eval (infinite loop?)");
819        }
820
821        // NEXT, evaluate the script and translate the result to Ok or Error
822        let mut result = self.eval_script(&*value.as_script()?);
823
824        // NEXT, decrement the number of nesting levels.
825        self.num_levels -= 1;
826
827        // NEXT, translate and return the result.
828        if self.num_levels == 0 {
829            if let Err(mut exception) = result {
830                // FIRST, handle the return -code, -level protocol
831                if exception.code() == ResultCode::Return {
832                    exception.decrement_level();
833                }
834
835                result = match exception.code() {
836                    ResultCode::Okay => Ok(exception.value()),
837                    ResultCode::Error => Err(exception),
838                    ResultCode::Return => Err(exception), // -level > 0
839                    ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
840                    ResultCode::Continue => molt_err!("invoked \"continue\" outside of a loop"),
841                    // TODO: Better error message
842                    ResultCode::Other(_) => molt_err!("unexpected result code."),
843                };
844            }
845        }
846
847        if let Err(exception) = &result {
848            if exception.is_error() {
849                self.set_global_error_data(exception.error_data())?;
850            }
851        }
852
853        result
854    }
855
856    /// Saves the error exception data
857    fn set_global_error_data(&mut self, error_data: Option<&ErrorData>) -> Result<(), Exception> {
858        if let Some(data) = error_data {
859            // TODO: Might want a public method for this.  Or, if I implement namespaces, that's
860            // sufficient.
861            self.scopes.set_global("errorInfo", data.error_info())?;
862            self.scopes.set_global("errorCode", data.error_code())?;
863        }
864
865        Ok(())
866    }
867
868    /// Evaluates a parsed Script, producing a normal MoltResult.
869    /// Also used by expr.rs.
870    pub(crate) fn eval_script(&mut self, script: &Script) -> MoltResult {
871        let mut result_value = Value::empty();
872
873        for word_vec in script.commands() {
874            let words = self.eval_word_vec(word_vec.words())?;
875
876            if words.is_empty() {
877                break;
878            }
879
880            let name = words[0].as_str();
881
882            if let Some(cmd) = self.commands.get(name) {
883                // let start = Instant::now();
884                let cmd = Rc::clone(cmd);
885                let result = cmd.execute(self, words.as_slice());
886                // self.profile_save(&format!("cmd.execute({})", name), start);
887
888                if let Ok(v) = result {
889                    result_value = v;
890                } else if let Err(mut exception) = result {
891                    // TODO: I think this needs to be done up above.
892                    // // Handle the return -code, -level protocol
893                    // if exception.code() == ResultCode::Return {
894                    //     exception.decrement_level();
895                    // }
896
897                    match exception.code() {
898                        // ResultCode::Okay => result_value = exception.value(),
899                        ResultCode::Error => {
900                            // FIRST, new error, an error from within a proc, or an error from
901                            // within some other body (ignored).
902                            if exception.is_new_error() {
903                                exception.add_error_info("    while executing");
904                            } else if cmd.is_proc() {
905                                exception.add_error_info("    invoked from within");
906                                exception.add_error_info(&format!(
907                                    "    (procedure \"{}\" line TODO)",
908                                    name
909                                ));
910                            } else {
911                                return Err(exception);
912                            }
913
914                            // TODO: Add command.  In standard TCL, this is the text of the command
915                            // before interpolation; at present, we don't have that info in a
916                            // convenient form.  For now, just convert the final words to a string.
917                            exception.add_error_info(&format!("\"{}\"", &list_to_string(&words)));
918                            return Err(exception);
919                        }
920                        _ => return Err(exception),
921                    }
922                } else {
923                    unreachable!();
924                }
925            } else {
926                return molt_err!("invalid command name \"{}\"", name);
927            }
928        }
929
930        Ok(result_value)
931    }
932
933    /// Evaluates a WordVec, producing a list of Values.  The expansion operator is handled
934    /// as a special case.
935    fn eval_word_vec(&mut self, words: &[Word]) -> Result<MoltList, Exception> {
936        let mut list: MoltList = Vec::new();
937
938        for word in words {
939            if let Word::Expand(word_to_expand) = word {
940                let value = self.eval_word(word_to_expand)?;
941                for val in &*value.as_list()? {
942                    list.push(val.clone());
943                }
944            } else {
945                list.push(self.eval_word(word)?);
946            }
947        }
948
949        Ok(list)
950    }
951
952    /// Evaluates a single word, producing a value.  This is also used by expr.rs.
953    pub(crate) fn eval_word(&mut self, word: &Word) -> MoltResult {
954        match word {
955            Word::Value(val) => Ok(val.clone()),
956            Word::VarRef(name) => self.scalar(name),
957            Word::ArrayRef(name, index_word) => {
958                let index = self.eval_word(index_word)?;
959                self.element(name, index.as_str())
960            }
961            Word::Script(script) => self.eval_script(script),
962            Word::Tokens(tokens) => {
963                let tlist = self.eval_word_vec(tokens)?;
964                let string: String = tlist.iter().map(|i| i.as_str()).collect();
965                Ok(Value::from(string))
966            }
967            Word::Expand(_) => panic!("recursive Expand!"),
968            Word::String(str) => Ok(Value::from(str)),
969        }
970    }
971
972    /// Returns the `return` option dictionary for the given result as a dictionary value.
973    /// Used by the `catch` command.
974    pub(crate) fn return_options(&self, result: &MoltResult) -> Value {
975        let mut opts = dict_new();
976
977        match result {
978            Ok(_) => {
979                opts.insert(OPT_CODE.into(), ZERO.into());
980                opts.insert(OPT_LEVEL.into(), ZERO.into());
981            }
982            Err(exception) => {
983                // FIRST, set the -code
984                match exception.code() {
985                    ResultCode::Okay => unreachable!(), // TODO: Not in use yet
986                    ResultCode::Error => {
987                        let data = exception.error_data().expect("Error has no error data");
988                        opts.insert(OPT_CODE.into(), "1".into());
989                        opts.insert(OPT_ERRORCODE.into(), data.error_code());
990                        opts.insert(OPT_ERRORINFO.into(), data.error_info());
991                        // TODO: Standard TCL also sets -errorstack, -errorline.
992                    }
993                    ResultCode::Return => {
994                        opts.insert(OPT_CODE.into(), exception.next_code().as_int().into());
995                        if let Some(data) = exception.error_data() {
996                            opts.insert(OPT_ERRORCODE.into(), data.error_code());
997                            opts.insert(OPT_ERRORINFO.into(), data.error_info());
998                        }
999                    }
1000                    ResultCode::Break => {
1001                        opts.insert(OPT_CODE.into(), "3".into());
1002                    }
1003                    ResultCode::Continue => {
1004                        opts.insert(OPT_CODE.into(), "4".into());
1005                    }
1006                    ResultCode::Other(num) => {
1007                        opts.insert(OPT_CODE.into(), num.into());
1008                    }
1009                }
1010
1011                // NEXT, set the -level
1012                opts.insert(OPT_LEVEL.into(), Value::from(exception.level() as MoltInt));
1013            }
1014        }
1015
1016        Value::from(opts)
1017    }
1018
1019    /// Determines whether or not the script is syntactically complete,
1020    /// e.g., has no unmatched quotes, brackets, or braces.
1021    ///
1022    /// REPLs use this to determine whether or not to ask for another line of
1023    /// input.
1024    ///
1025    /// # Example
1026    ///
1027    /// ```
1028    /// # use molt::types::*;
1029    /// # use molt::interp::Interp;
1030    /// let mut interp = Interp::new();
1031    /// assert!(interp.complete("set a [expr {1+1}]"));
1032    /// assert!(!interp.complete("set a [expr {1+1"));
1033    /// ```
1034
1035    pub fn complete(&mut self, script: &str) -> bool {
1036        parser::parse(script).is_ok()
1037    }
1038
1039    /// Evaluates a [Molt expression](https://wduquette.github.io/molt/ref/expr.html) and
1040    /// returns its value.  The expression is passed as a `Value` which is interpreted as a
1041    /// `String`.
1042    ///
1043    /// # Example
1044    /// ```
1045    /// use molt::Interp;
1046    /// use molt::types::*;
1047    /// # fn dummy() -> Result<String,Exception> {
1048    /// let mut interp = Interp::new();
1049    /// let expr = Value::from("2 + 2");
1050    /// let sum = interp.expr(&expr)?.as_int()?;
1051    ///
1052    /// assert_eq!(sum, 4);
1053    /// # Ok("dummy".to_string())
1054    /// # }
1055    /// ```
1056    pub fn expr(&mut self, expr: &Value) -> MoltResult {
1057        // Evaluate the expression and set the errorInfo/errorCode.
1058        let result = expr::expr(self, expr);
1059
1060        if let Err(exception) = &result {
1061            self.set_global_error_data(exception.error_data())?;
1062        }
1063
1064        result
1065    }
1066
1067    /// Evaluates a boolean [Molt expression](https://wduquette.github.io/molt/ref/expr.html)
1068    /// and returns its value, or an error if it couldn't be interpreted as a boolean.
1069    ///
1070    /// # Example
1071    ///
1072    /// ```
1073    /// use molt::Interp;
1074    /// use molt::types::*;
1075    /// # fn dummy() -> Result<String,Exception> {
1076    /// let mut interp = Interp::new();
1077    ///
1078    /// let expr = Value::from("1 < 2");
1079    /// let flag: bool = interp.expr_bool(&expr)?;
1080    ///
1081    /// assert!(flag);
1082    /// # Ok("dummy".to_string())
1083    /// # }
1084    /// ```
1085    pub fn expr_bool(&mut self, expr: &Value) -> Result<bool, Exception> {
1086        self.expr(expr)?.as_bool()
1087    }
1088
1089    /// Evaluates a [Molt expression](https://wduquette.github.io/molt/ref/expr.html)
1090    /// and returns its value as an integer, or an error if it couldn't be interpreted as an
1091    /// integer.
1092    ///
1093    /// # Example
1094    ///
1095    /// ```
1096    /// use molt::Interp;
1097    /// use molt::types::*;
1098    /// # fn dummy() -> Result<String,Exception> {
1099    /// let mut interp = Interp::new();
1100    ///
1101    /// let expr = Value::from("1 + 2");
1102    /// let val: MoltInt = interp.expr_int(&expr)?;
1103    ///
1104    /// assert_eq!(val, 3);
1105    /// # Ok("dummy".to_string())
1106    /// # }
1107    /// ```
1108    pub fn expr_int(&mut self, expr: &Value) -> Result<MoltInt, Exception> {
1109        self.expr(expr)?.as_int()
1110    }
1111
1112    /// Evaluates a [Molt expression](https://wduquette.github.io/molt/ref/expr.html)
1113    /// and returns its value as a float, or an error if it couldn't be interpreted as a
1114    /// float.
1115    ///
1116    /// # Example
1117    ///
1118    /// ```
1119    /// use molt::Interp;
1120    /// use molt::types::*;
1121    /// # fn dummy() -> Result<String,Exception> {
1122    /// let mut interp = Interp::new();
1123    ///
1124    /// let expr = Value::from("1.1 + 2.2");
1125    /// let val: MoltFloat = interp.expr_float(&expr)?;
1126    ///
1127    /// assert_eq!(val, 3.3);
1128    /// # Ok("dummy".to_string())
1129    /// # }
1130    /// ```
1131    pub fn expr_float(&mut self, expr: &Value) -> Result<MoltFloat, Exception> {
1132        self.expr(expr)?.as_float()
1133    }
1134
1135    //--------------------------------------------------------------------------------------------
1136    // Variable Handling
1137
1138    /// Retrieves the value of the named variable in the current scope.  The `var_name` may
1139    /// name a scalar variable or an array element.  This is the normal way to retrieve the
1140    /// value of a variable named by a command argument.
1141    ///
1142    /// Returns an error if the variable is a scalar and the name names an array element,
1143    /// and vice versa.
1144    ///
1145    /// # Example
1146    ///
1147    /// ```
1148    /// use molt::types::*;
1149    /// use molt::Interp;
1150    /// use molt::molt_ok;
1151    /// # fn dummy() -> MoltResult {
1152    /// let mut interp = Interp::new();
1153    ///
1154    /// // Set the value of the scalar variable "a" using a script.
1155    /// interp.eval("set a 1")?;
1156    ///
1157    /// // The value of the scalar variable "a".
1158    /// let val = interp.var(&Value::from("a"))?;
1159    /// assert_eq!(val.as_str(), "1");
1160    ///
1161    /// // Set the value of the array element "b(1)" using a script.
1162    /// interp.eval("set b(1) Howdy")?;
1163    ///
1164    /// // The value of the array element "b(1)":
1165    /// let val = interp.var(&Value::from("b(1)"))?;
1166    /// assert_eq!(val.as_str(), "Howdy");
1167    /// # molt_ok!()
1168    /// # }
1169    /// ```
1170    pub fn var(&self, var_name: &Value) -> MoltResult {
1171        let var_name = &*var_name.as_var_name();
1172        match var_name.index() {
1173            Some(index) => self.element(var_name.name(), index),
1174            None => self.scalar(var_name.name()),
1175        }
1176    }
1177
1178    /// Returns 1 if the named variable is defined and exists, and 0 otherwise.
1179    pub fn var_exists(&self, var_name: &Value) -> bool {
1180        let var_name = &*var_name.as_var_name();
1181        match var_name.index() {
1182            Some(index) => self.scopes.elem_exists(var_name.name(), index),
1183            None => self.scopes.exists(var_name.name()),
1184        }
1185    }
1186
1187    /// Sets the value of the variable in the current scope.  The `var_name` may name a
1188    /// scalar variable or an array element.  This is the usual way to assign a value to
1189    /// a variable named by a command argument.
1190    ///
1191    /// Returns an error if the variable is scalar and the name names an array element,
1192    /// and vice-versa.
1193    ///
1194    /// # Example
1195    ///
1196    /// ```
1197    /// use molt::types::*;
1198    /// use molt::Interp;
1199    /// use molt::molt_ok;
1200    /// # fn dummy() -> MoltResult {
1201    /// let mut interp = Interp::new();
1202    ///
1203    /// // Set the value of the scalar variable "a"
1204    /// let scalar = Value::from("a");  // The variable name
1205    /// interp.set_var(&scalar, Value::from("1"))?;
1206    /// assert_eq!(interp.var(&scalar)?.as_str(), "1");
1207    ///
1208    /// // Set the value of the array element "b(1)":
1209    /// let element = Value::from("b(1)");  // The variable name
1210    /// interp.set_var(&element, Value::from("howdy"))?;
1211    /// assert_eq!(interp.var(&element)?.as_str(), "howdy");
1212    /// # molt_ok!()
1213    /// # }
1214    /// ```
1215    pub fn set_var(&mut self, var_name: &Value, value: Value) -> Result<(), Exception> {
1216        let var_name = &*var_name.as_var_name();
1217        match var_name.index() {
1218            Some(index) => self.set_element(var_name.name(), index, value),
1219            None => self.set_scalar(var_name.name(), value),
1220        }
1221    }
1222
1223    /// Sets the value of the variable in the current scope, return its value.  The `var_name`
1224    /// may name a
1225    /// scalar variable or an array element.  This is the usual way to assign a value to
1226    /// a variable named by a command argument when the command is expected to return the
1227    /// value.
1228    ///
1229    /// Returns an error if the variable is scalar and the name names an array element,
1230    /// and vice-versa.
1231    ///
1232    /// # Example
1233    ///
1234    /// ```
1235    /// use molt::types::*;
1236    /// use molt::Interp;
1237    /// use molt::molt_ok;
1238    /// # fn dummy() -> MoltResult {
1239    /// let mut interp = Interp::new();
1240    ///
1241    /// // Set the value of the scalar variable "a"
1242    /// let scalar = Value::from("a");  // The variable name
1243    /// assert_eq!(interp.set_var_return(&scalar, Value::from("1"))?.as_str(), "1");
1244    ///
1245    /// // Set the value of the array element "b(1)":
1246    /// let element = Value::from("b(1)");  // The variable name
1247    /// interp.set_var(&element, Value::from("howdy"))?;
1248    /// assert_eq!(interp.set_var_return(&element, Value::from("1"))?.as_str(), "1");
1249    /// # molt_ok!()
1250    /// # }
1251    /// ```
1252    pub fn set_var_return(&mut self, var_name: &Value, value: Value) -> MoltResult {
1253        let var_name = &*var_name.as_var_name();
1254        match var_name.index() {
1255            Some(index) => self.set_element_return(var_name.name(), index, value),
1256            None => self.set_scalar_return(var_name.name(), value),
1257        }
1258    }
1259
1260    /// Retrieves the value of the named scalar variable in the current scope.
1261    ///
1262    /// Returns an error if the variable is not found, or if the variable is an array variable.
1263    ///
1264    /// # Example
1265    ///
1266    /// ```
1267    /// use molt::types::*;
1268    /// use molt::Interp;
1269    /// use molt::molt_ok;
1270    /// # fn dummy() -> MoltResult {
1271    /// let mut interp = Interp::new();
1272    ///
1273    /// // Set the value of the scalar variable "a" using a script.
1274    /// interp.eval("set a 1")?;
1275    ///
1276    /// // The value of the scalar variable "a".
1277    /// let val = interp.scalar("a")?;
1278    /// assert_eq!(val.as_str(), "1");
1279    /// # molt_ok!()
1280    /// # }
1281    /// ```
1282    pub fn scalar(&self, name: &str) -> MoltResult {
1283        self.scopes.get(name)
1284    }
1285
1286    /// Sets the value of the named scalar variable in the current scope, creating the variable
1287    /// if necessary.
1288    ///
1289    /// Returns an error if the variable exists and is an array variable.
1290    ///
1291    /// # Example
1292    ///
1293    /// ```
1294    /// use molt::types::*;
1295    /// use molt::Interp;
1296    /// use molt::molt_ok;
1297    /// # fn dummy() -> MoltResult {
1298    /// let mut interp = Interp::new();
1299    ///
1300    /// // Set the value of the scalar variable "a"
1301    /// interp.set_scalar("a", Value::from("1"))?;
1302    /// assert_eq!(interp.scalar("a")?.as_str(), "1");
1303    /// # molt_ok!()
1304    /// # }
1305    /// ```
1306    pub fn set_scalar(&mut self, name: &str, value: Value) -> Result<(), Exception> {
1307        self.scopes.set(name, value)
1308    }
1309
1310    /// Sets the value of the named scalar variable in the current scope, creating the variable
1311    /// if necessary, and returning the value.
1312    ///
1313    /// Returns an error if the variable exists and is an array variable.
1314    ///
1315    /// # Example
1316    ///
1317    /// ```
1318    /// use molt::types::*;
1319    /// use molt::Interp;
1320    /// use molt::molt_ok;
1321    /// # fn dummy() -> MoltResult {
1322    /// let mut interp = Interp::new();
1323    ///
1324    /// // Set the value of the scalar variable "a"
1325    /// assert_eq!(interp.set_scalar_return("a", Value::from("1"))?.as_str(), "1");
1326    /// # molt_ok!()
1327    /// # }
1328    pub fn set_scalar_return(&mut self, name: &str, value: Value) -> MoltResult {
1329        // Clone the value, since we'll be returning it out again.
1330        self.scopes.set(name, value.clone())?;
1331        Ok(value)
1332    }
1333
1334    /// Retrieves the value of the named array element in the current scope.
1335    ///
1336    /// Returns an error if the element is not found, or the variable is not an
1337    /// array variable.
1338    ///
1339    /// # Example
1340    ///
1341    /// ```
1342    /// use molt::types::*;
1343    /// use molt::Interp;
1344    /// use molt::molt_ok;
1345    /// # fn dummy() -> MoltResult {
1346    /// let mut interp = Interp::new();
1347    ///
1348    /// // Set the value of the array element variable "a(1)" using a script.
1349    /// interp.eval("set a(1) Howdy")?;
1350    ///
1351    /// // The value of the array element "a(1)".
1352    /// let val = interp.element("a", "1")?;
1353    /// assert_eq!(val.as_str(), "Howdy");
1354    /// # molt_ok!()
1355    /// # }
1356    /// ```
1357    pub fn element(&self, name: &str, index: &str) -> MoltResult {
1358        self.scopes.get_elem(name, index)
1359    }
1360
1361    /// Sets the value of an array element in the current scope, creating the variable
1362    /// if necessary.
1363    ///
1364    /// Returns an error if the variable exists and is not an array variable.
1365    ///
1366    /// # Example
1367    ///
1368    /// ```
1369    /// use molt::types::*;
1370    /// use molt::Interp;
1371    /// use molt::molt_ok;
1372    /// # fn dummy() -> MoltResult {
1373    /// let mut interp = Interp::new();
1374    ///
1375    /// // Set the value of the scalar variable "a"
1376    /// interp.set_element("b", "1", Value::from("xyz"))?;
1377    /// assert_eq!(interp.element("b", "1")?.as_str(), "xyz");
1378    /// # molt_ok!()
1379    /// # }
1380    /// ```
1381    pub fn set_element(&mut self, name: &str, index: &str, value: Value) -> Result<(), Exception> {
1382        self.scopes.set_elem(name, index, value)
1383    }
1384
1385    /// Sets the value of an array element in the current scope, creating the variable
1386    /// if necessary, and returning the value.
1387    ///
1388    /// Returns an error if the variable exists and is not an array variable.
1389    ///
1390    /// # Example
1391    ///
1392    /// ```
1393    /// use molt::types::*;
1394    /// use molt::Interp;
1395    /// use molt::molt_ok;
1396    /// # fn dummy() -> MoltResult {
1397    /// let mut interp = Interp::new();
1398    ///
1399    /// // Set the value of the scalar variable "a"
1400    /// assert_eq!(interp.set_element_return("b", "1", Value::from("xyz"))?.as_str(), "xyz");
1401    /// # molt_ok!()
1402    /// # }
1403    /// ```
1404    pub fn set_element_return(&mut self, name: &str, index: &str, value: Value) -> MoltResult {
1405        // Clone the value, since we'll be returning it out again.
1406        self.scopes.set_elem(name, index, value.clone())?;
1407        Ok(value)
1408    }
1409
1410    /// Unsets a variable, whether scalar or array, given its name in the current scope.  For
1411    /// arrays this is the name of the array proper, e.g., `myArray`, not the name of an
1412    /// element, e.g., `myArray(1)`.
1413    ///
1414    /// It is _not_ an error to unset a variable that doesn't exist.
1415    ///
1416    /// # Example
1417    ///
1418    /// ```
1419    /// use molt::types::*;
1420    /// use molt::Interp;
1421    /// use molt::molt_ok;
1422    /// # fn dummy() -> MoltResult {
1423    /// let mut interp = Interp::new();
1424    ///
1425    /// interp.set_scalar("a", Value::from("1"))?;
1426    /// interp.set_element("b", "1", Value::from("2"))?;
1427    ///
1428    /// interp.unset("a"); // Unset scalar
1429    /// interp.unset("b"); // Unset entire array
1430    /// # molt_ok!()
1431    /// # }
1432    /// ```
1433    pub fn unset(&mut self, name: &str) {
1434        self.scopes.unset(name);
1435    }
1436
1437    /// Unsets the value of the named variable or array element in the current scope.
1438    ///
1439    /// It is _not_ an error to unset a variable that doesn't exist.
1440    ///
1441    /// # Example
1442    ///
1443    /// ```
1444    /// use molt::types::*;
1445    /// use molt::Interp;
1446    /// use molt::molt_ok;
1447    /// # fn dummy() -> MoltResult {
1448    /// let mut interp = Interp::new();
1449    ///
1450    /// let scalar = Value::from("a");
1451    /// let array = Value::from("b");
1452    /// let elem = Value::from("b(1)");
1453    ///
1454    /// interp.unset_var(&scalar); // Unset scalar
1455    /// interp.unset_var(&elem);   // Unset array element
1456    /// interp.unset_var(&array);  // Unset entire array
1457    /// # molt_ok!()
1458    /// # }
1459    /// ```
1460    pub fn unset_var(&mut self, name: &Value) {
1461        let var_name = name.as_var_name();
1462
1463        if let Some(index) = var_name.index() {
1464            self.unset_element(var_name.name(), index);
1465        } else {
1466            self.unset(var_name.name());
1467        }
1468    }
1469
1470    /// Unsets a single element in an array given the array name and index.
1471    ///
1472    /// It is _not_ an error to unset an array element that doesn't exist.
1473    ///
1474    /// # Example
1475    ///
1476    /// ```
1477    /// use molt::types::*;
1478    /// use molt::Interp;
1479    /// use molt::molt_ok;
1480    /// # fn dummy() -> MoltResult {
1481    /// let mut interp = Interp::new();
1482    ///
1483    /// interp.set_element("b", "1", Value::from("2"))?;
1484    ///
1485    /// interp.unset_element("b", "1");
1486    /// # molt_ok!()
1487    /// # }
1488    /// ```
1489    pub fn unset_element(&mut self, array_name: &str, index: &str) {
1490        self.scopes.unset_element(array_name, index);
1491    }
1492
1493    /// Gets a list of the names of the variables that are visible in the current scope.
1494    /// The list includes the names of array variables but not elements within them.
1495    ///
1496    /// # Example
1497    ///
1498    /// ```
1499    /// use molt::Interp;
1500    /// use molt::types::*;
1501    ///
1502    /// # let mut interp = Interp::new();
1503    /// for name in interp.vars_in_scope() {
1504    ///     println!("Found variable: {}", name);
1505    /// }
1506    /// ```
1507    pub fn vars_in_scope(&self) -> MoltList {
1508        self.scopes.vars_in_scope()
1509    }
1510
1511    /// Gets a list of the names of the variables defined in the global scope.
1512    /// The list includes the names of array variables but not elements within them.
1513    ///
1514    /// # Example
1515    ///
1516    /// ```
1517    /// use molt::Interp;
1518    /// use molt::types::*;
1519    ///
1520    /// # let mut interp = Interp::new();
1521    /// for name in interp.vars_in_global_scope() {
1522    ///     println!("Found variable: {}", name);
1523    /// }
1524    /// ```
1525    pub fn vars_in_global_scope(&self) -> MoltList {
1526        self.scopes.vars_in_global_scope()
1527    }
1528
1529    /// Gets a list of the names of the variables defined in the local scope.
1530    /// This does not include variables brought into scope via `global` or `upvar`, or any
1531    /// variables defined in the global scope.
1532    /// The list includes the names of array variables but not elements within them.
1533    ///
1534    /// # Example
1535    ///
1536    /// ```
1537    /// use molt::Interp;
1538    /// use molt::types::*;
1539    ///
1540    /// # let mut interp = Interp::new();
1541    /// for name in interp.vars_in_local_scope() {
1542    ///     println!("Found variable: {}", name);
1543    /// }
1544    /// ```
1545    pub fn vars_in_local_scope(&self) -> MoltList {
1546        self.scopes.vars_in_local_scope()
1547    }
1548
1549    /// Links the variable name in the current scope to the given scope.
1550    /// Note: the level is the absolute level, not the level relative to the
1551    /// current stack level, i.e., level=0 is the global scope.
1552    ///
1553    /// This method is used to implement the `upvar` command, which allows variables to be
1554    /// passed by name; client code should rarely need to access it directly.
1555    pub fn upvar(&mut self, level: usize, name: &str) {
1556        assert!(level <= self.scopes.current(), "Invalid scope level");
1557        self.scopes.upvar(level, name);
1558    }
1559
1560    /// Pushes a variable scope (i.e., a stack level) onto the scope stack.
1561    ///
1562    /// Procs use this to define their local scope.  Client code should seldom need to call
1563    /// this directly, but it can be useful in a few cases.  For example, the Molt
1564    /// test harness's `test` command runs its body in a local scope as an aid to test
1565    /// cleanup.
1566    ///
1567    /// **Note:** a command that pushes a scope must also call `Interp::pop_scope` before it
1568    /// exits!
1569    pub fn push_scope(&mut self) {
1570        self.scopes.push();
1571    }
1572
1573    /// Pops a variable scope (i.e., a stack level) off of the scope stack.  Calls to
1574    /// `Interp::push_scope` and `Interp::pop_scope` must exist in pairs.
1575    pub fn pop_scope(&mut self) {
1576        self.scopes.pop();
1577    }
1578
1579    /// Return the current scope level.  The global scope is level `0`; each call to
1580    /// `Interp::push_scope` adds a level, and each call to `Interp::pop_scope` removes it.
1581    /// This method is used with `Interp::upvar` to access the caller's scope when a variable
1582    /// is passed by name.
1583    pub fn scope_level(&self) -> usize {
1584        self.scopes.current()
1585    }
1586
1587    ///-----------------------------------------------------------------------------------
1588    /// Array Manipulation Methods
1589    ///
1590    /// These provide the infrastructure for the `array` command.
1591
1592    /// Unsets an array variable givee its name.  Nothing happens if the variable doesn't
1593    /// exist, or if the variable is not an array variable.
1594    pub(crate) fn array_unset(&mut self, array_name: &str) {
1595        self.scopes.array_unset(array_name);
1596    }
1597
1598    /// Determines whether or not the name is the name of an array variable.
1599    ///
1600    /// # Example
1601    ///
1602    /// ```
1603    /// # use molt::Interp;
1604    /// # use molt::types::*;
1605    /// # use molt::molt_ok;
1606    /// # fn dummy() -> MoltResult {
1607    /// # let mut interp = Interp::new();
1608    /// interp.set_scalar("a", Value::from(1))?;
1609    /// interp.set_element("b", "1", Value::from(2));
1610    ///
1611    /// assert!(!interp.array_exists("a"));
1612    /// assert!(interp.array_exists("b"));
1613    /// # molt_ok!()
1614    /// # }
1615    /// ```
1616    pub fn array_exists(&self, array_name: &str) -> bool {
1617        self.scopes.array_exists(array_name)
1618    }
1619
1620    /// Gets a flat vector of the keys and values from the named array.  This is used to
1621    /// implement the `array get` command.
1622    ///
1623    /// # Example
1624    ///
1625    /// ```
1626    /// use molt::Interp;
1627    /// use molt::types::*;
1628    ///
1629    /// # let mut interp = Interp::new();
1630    /// for txt in interp.array_get("myArray") {
1631    ///     println!("Found index or value: {}", txt);
1632    /// }
1633    /// ```
1634    pub fn array_get(&self, array_name: &str) -> MoltList {
1635        self.scopes.array_get(array_name)
1636    }
1637
1638    /// Merges a flat vector of keys and values into the named array.
1639    /// It's an error if the vector has an odd number of elements, or if the named variable
1640    /// is a scalar.  This method is used to implement the `array set` command.
1641    ///
1642    /// # Example
1643    ///
1644    /// For example, the following Rust code is equivalent to the following Molt code:
1645    ///
1646    /// ```tcl
1647    /// # Set individual elements
1648    /// set myArray(a) 1
1649    /// set myArray(b) 2
1650    ///
1651    /// # Set all at once
1652    /// array set myArray { a 1 b 2 }
1653    /// ```
1654    ///
1655    /// ```
1656    /// use molt::Interp;
1657    /// use molt::types::*;
1658    /// # use molt::molt_ok;
1659    ///
1660    /// # fn dummy() -> MoltResult {
1661    /// # let mut interp = Interp::new();
1662    /// interp.array_set("myArray", &vec!["a".into(), "1".into(), "b".into(), "2".into()])?;
1663    /// # molt_ok!()
1664    /// # }
1665    /// ```
1666    pub fn array_set(&mut self, array_name: &str, kvlist: &[Value]) -> MoltResult {
1667        if kvlist.len() % 2 == 0 {
1668            self.scopes.array_set(array_name, kvlist)?;
1669            molt_ok!()
1670        } else {
1671            molt_err!("list must have an even number of elements")
1672        }
1673    }
1674
1675    /// Gets a list of the indices of the given array.  This is used to implement the
1676    /// `array names` command.  If the variable does not exist (or is not an array variable),
1677    /// the method returns the empty list.
1678    ///
1679    /// # Example
1680    ///
1681    /// ```
1682    /// use molt::Interp;
1683    /// use molt::types::*;
1684    ///
1685    /// # let mut interp = Interp::new();
1686    /// for name in interp.array_names("myArray") {
1687    ///     println!("Found index : {}", name);
1688    /// }
1689    /// ```
1690    pub fn array_names(&self, array_name: &str) -> MoltList {
1691        self.scopes.array_indices(array_name)
1692    }
1693
1694    /// Gets the number of elements in the named array.  Returns 0 if the variable doesn't exist
1695    /// (or isn't an array variable).
1696    ///
1697    /// # Example
1698    ///
1699    /// ```
1700    /// use molt::Interp;
1701    /// use molt::types::*;
1702    ///
1703    /// # use molt::molt_ok;
1704    /// # fn dummy() -> MoltResult {
1705    /// let mut interp = Interp::new();
1706    ///
1707    /// assert_eq!(interp.array_size("a"), 0);
1708    ///
1709    /// interp.set_element("a", "1", Value::from("xyz"))?;
1710    /// assert_eq!(interp.array_size("a"), 1);
1711    /// # molt_ok!()
1712    /// # }
1713    /// ```
1714    pub fn array_size(&self, array_name: &str) -> usize {
1715        self.scopes.array_size(array_name)
1716    }
1717
1718    //--------------------------------------------------------------------------------------------
1719    // Command Definition and Handling
1720
1721    /// Adds a binary command with no related context to the interpreter.  This is the normal
1722    /// way to add most commands.
1723    ///
1724    /// If the command needs access to some form of application or context data,
1725    /// use [`add_context_command`](#method.add_context_command) instead.  See the
1726    /// [module level documentation](index.html) for an overview and examples.
1727    pub fn add_command(&mut self, name: &str, func: CommandFunc) {
1728        self.add_context_command(name, func, NULL_CONTEXT);
1729    }
1730
1731    /// Adds a binary command with related context data to the interpreter.
1732    ///
1733    /// This is the normal way to add commands requiring application context.  See the
1734    /// [module level documentation](index.html) for an overview and examples.
1735    pub fn add_context_command(&mut self, name: &str, func: CommandFunc, context_id: ContextID) {
1736        if context_id != NULL_CONTEXT {
1737            self.context_map
1738                .get_mut(&context_id)
1739                .expect("unknown context ID")
1740                .increment();
1741        }
1742
1743        self.commands
1744            .insert(name.into(), Rc::new(Command::Native(func, context_id)));
1745    }
1746
1747    /// Adds a procedure to the interpreter.
1748    ///
1749    /// This is how to add a Molt `proc` to the interpreter.  The arguments are the same
1750    /// as for the `proc` command and the `commands::cmd_proc` function.
1751    ///
1752    /// TODO: If this method is ever made public, the parameter list validation done
1753    /// in cmd_proc should be moved here.
1754    pub(crate) fn add_proc(&mut self, name: &str, parms: &[Value], body: &Value) {
1755        let proc = Procedure {
1756            parms: parms.to_owned(),
1757            body: body.clone(),
1758        };
1759
1760        self.commands
1761            .insert(name.into(), Rc::new(Command::Proc(proc)));
1762    }
1763
1764    /// Determines whether or not the interpreter contains a command with the given
1765    /// name.
1766    pub fn has_command(&self, name: &str) -> bool {
1767        self.commands.contains_key(name)
1768    }
1769
1770    /// Renames the command.
1771    ///
1772    /// **Note:** This does not update procedures that reference the command under the old
1773    /// name.  This is intentional: it is a common TCL programming technique to wrap an
1774    /// existing command by renaming it and defining a new command with the old name that
1775    /// calls the original command at its new name.
1776    ///
1777    /// # Example
1778    ///
1779    /// ```
1780    /// use molt::Interp;
1781    /// use molt::types::*;
1782    /// use molt::molt_ok;
1783    /// # fn dummy() -> MoltResult {
1784    /// let mut interp = Interp::new();
1785    ///
1786    /// interp.rename_command("expr", "=");
1787    ///
1788    /// let sum = interp.eval("= {1 + 1}")?.as_int()?;
1789    ///
1790    /// assert_eq!(sum, 2);
1791    /// # molt_ok!()
1792    /// # }
1793    /// ```
1794    pub fn rename_command(&mut self, old_name: &str, new_name: &str) {
1795        if let Some(cmd) = self.commands.get(old_name) {
1796            let cmd = Rc::clone(cmd);
1797            self.commands.remove(old_name);
1798            self.commands.insert(new_name.into(), cmd);
1799        }
1800    }
1801
1802    /// Removes the command with the given name.
1803    ///
1804    /// This would typically be done when destroying an object command.
1805    ///
1806    /// # Example
1807    ///
1808    /// ```
1809    /// use molt::Interp;
1810    /// use molt::types::*;
1811    /// use molt::molt_ok;
1812    ///
1813    /// let mut interp = Interp::new();
1814    ///
1815    /// interp.remove_command("set");  // You'll be sorry....
1816    ///
1817    /// assert!(!interp.has_command("set"));
1818    /// ```
1819    pub fn remove_command(&mut self, name: &str) {
1820        // FIRST, get the command's context ID, if any.
1821        let context_id = self
1822            .commands
1823            .get(name)
1824            .expect("undefined command")
1825            .context_id();
1826
1827        // NEXT, If it has a context ID, decrement its reference count; and if the reference
1828        // is zero, remove the context.
1829        if context_id != NULL_CONTEXT
1830            && self
1831                .context_map
1832                .get_mut(&context_id)
1833                .expect("unknown context ID")
1834                .decrement()
1835        {
1836            self.context_map.remove(&context_id);
1837        }
1838
1839        // FINALLY, remove the command itself.
1840        self.commands.remove(name);
1841    }
1842
1843    /// Gets a vector of the names of the existing commands.
1844    ///
1845    /// # Example
1846    ///
1847    /// ```
1848    /// use molt::Interp;
1849    /// use molt::types::*;
1850    /// use molt::molt_ok;
1851    ///
1852    /// let mut interp = Interp::new();
1853    ///
1854    /// for name in interp.command_names() {
1855    ///     println!("Found command: {}", name);
1856    /// }
1857    /// ```
1858    pub fn command_names(&self) -> MoltList {
1859        let vec: MoltList = self
1860            .commands
1861            .keys()
1862            .cloned()
1863            .map(|x| Value::from(&x))
1864            .collect();
1865
1866        vec
1867    }
1868
1869    /// Returns the body of the named procedure, or an error if the name doesn't
1870    /// name a procedure.
1871    pub fn command_type(&self, command: &str) -> MoltResult {
1872        if let Some(cmd) = self.commands.get(command) {
1873            molt_ok!(cmd.cmdtype())
1874        } else {
1875            molt_err!("\"{}\" isn't a command", command)
1876        }
1877    }
1878
1879    /// Gets a vector of the names of the existing procedures.
1880    ///
1881    /// # Example
1882    ///
1883    /// ```
1884    /// use molt::Interp;
1885    /// use molt::types::*;
1886    /// use molt::molt_ok;
1887    ///
1888    /// let mut interp = Interp::new();
1889    ///
1890    /// for name in interp.proc_names() {
1891    ///     println!("Found procedure: {}", name);
1892    /// }
1893    /// ```
1894    pub fn proc_names(&self) -> MoltList {
1895        let vec: MoltList = self
1896            .commands
1897            .iter()
1898            .filter(|(_, cmd)| cmd.is_proc())
1899            .map(|(name, _)| Value::from(name))
1900            .collect();
1901
1902        vec
1903    }
1904
1905    /// Returns the body of the named procedure, or an error if the name doesn't
1906    /// name a procedure.
1907    pub fn proc_body(&self, procname: &str) -> MoltResult {
1908        if let Some(cmd) = self.commands.get(procname) {
1909            if let Command::Proc(proc) = &**cmd {
1910                return molt_ok!(proc.body.clone());
1911            }
1912        }
1913
1914        molt_err!("\"{}\" isn't a procedure", procname)
1915    }
1916
1917    /// Returns a list of the names of the arguments of the named procedure, or an
1918    /// error if the name doesn't name a procedure.
1919    pub fn proc_args(&self, procname: &str) -> MoltResult {
1920        if let Some(cmd) = self.commands.get(procname) {
1921            if let Command::Proc(proc) = &**cmd {
1922                // Note: the item is guaranteed to be parsible as a list of 1 or 2 elements.
1923                let vec: MoltList = proc
1924                    .parms
1925                    .iter()
1926                    .map(|item| item.as_list().expect("invalid proc parms")[0].clone())
1927                    .collect();
1928                return molt_ok!(Value::from(vec));
1929            }
1930        }
1931
1932        molt_err!("\"{}\" isn't a procedure", procname)
1933    }
1934
1935    /// Returns the default value of the named argument of the named procedure, if it has one.
1936    /// Returns an error if the procedure has no such argument, or the `procname` doesn't name
1937    /// a procedure.
1938    pub fn proc_default(&self, procname: &str, arg: &str) -> Result<Option<Value>, Exception> {
1939        if let Some(cmd) = self.commands.get(procname) {
1940            if let Command::Proc(proc) = &**cmd {
1941                for argvec in &proc.parms {
1942                    let argvec = argvec.as_list()?; // Should never fail
1943                    if argvec[0].as_str() == arg {
1944                        if argvec.len() == 2 {
1945                            return Ok(Some(argvec[1].clone()));
1946                        } else {
1947                            return Ok(None);
1948                        }
1949                    }
1950                }
1951                return molt_err!(
1952                    "procedure \"{}\" doesn't have an argument \"{}\"",
1953                    procname,
1954                    arg
1955                );
1956            }
1957        }
1958
1959        molt_err!("\"{}\" isn't a procedure", procname)
1960    }
1961
1962    /// Calls a subcommand of the current command, looking up its name in an array of
1963    /// `Subcommand` tuples.
1964    ///
1965    /// The subcommand, if found, is called with the same `context_id` and `argv` as its
1966    /// parent ensemble.  `subc` is the index of the subcommand's name in the `argv` array;
1967    /// in most cases it will be `1`, but it is possible to define subcommands with
1968    /// subcommands of their own.  The `subcommands` argument is a borrow of an array of
1969    /// `Subcommand` records, each defining a subcommand's name and `CommandFunc`.
1970    ///
1971    /// If the subcommand name is found in the array, the matching `CommandFunc` is called.
1972    /// otherwise, the error message gives the ensemble syntax.  If an invalid subcommand
1973    /// name was provided, the error message includes the valid options.
1974    ///
1975    /// See the implementation of the `array` command in `commands.rs` and the
1976    /// [module level documentation](index.html) for examples.
1977    pub fn call_subcommand(
1978        &mut self,
1979        context_id: ContextID,
1980        argv: &[Value],
1981        subc: usize,
1982        subcommands: &[Subcommand],
1983    ) -> MoltResult {
1984        check_args(subc, argv, subc + 1, 0, "subcommand ?arg ...?")?;
1985        let rec = Subcommand::find(subcommands, argv[subc].as_str())?;
1986        (rec.1)(self, context_id, argv)
1987    }
1988
1989    //--------------------------------------------------------------------------------------------
1990    // Interpreter Configuration
1991
1992    /// Gets the interpreter's recursion limit: how deep the stack of script evaluations may be.
1993    ///
1994    /// A script stack level is added by each nested script evaluation (i.e., by each call)
1995    /// to [`eval`](#method.eval) or [`eval_value`](#method.eval_value).
1996    ///
1997    /// # Example
1998    /// ```
1999    /// # use molt::types::*;
2000    /// # use molt::interp::Interp;
2001    /// let mut interp = Interp::new();
2002    /// assert_eq!(interp.recursion_limit(), 1000);
2003    /// ```
2004    pub fn recursion_limit(&self) -> usize {
2005        self.recursion_limit
2006    }
2007
2008    /// Sets the interpreter's recursion limit: how deep the stack of script evaluations may
2009    /// be.  The default is 1000.
2010    ///
2011    /// A script stack level is added by each nested script evaluation (i.e., by each call)
2012    /// to [`eval`](#method.eval) or [`eval_value`](#method.eval_value).
2013    ///
2014    /// # Example
2015    /// ```
2016    /// # use molt::types::*;
2017    /// # use molt::interp::Interp;
2018    /// let mut interp = Interp::new();
2019    /// interp.set_recursion_limit(100);
2020    /// assert_eq!(interp.recursion_limit(), 100);
2021    /// ```
2022    pub fn set_recursion_limit(&mut self, limit: usize) {
2023        self.recursion_limit = limit;
2024    }
2025
2026    //--------------------------------------------------------------------------------------------
2027    // Context Cache
2028
2029    /// Saves the client's context data in the interpreter's context cache,
2030    /// returning a generated context ID.  Client commands can retrieve the data
2031    /// given the ID.
2032    ///
2033    /// See the [module level documentation](index.html) for an overview and examples.
2034    ///
2035    /// # Example
2036    ///
2037    /// ```
2038    /// use molt::types::*;
2039    /// use molt::interp::Interp;
2040    ///
2041    /// let mut interp = Interp::new();
2042    /// let data: Vec<String> = Vec::new();
2043    /// let id = interp.save_context(data);
2044    /// ```
2045    pub fn save_context<T: 'static>(&mut self, data: T) -> ContextID {
2046        let id = self.context_id();
2047        self.context_map.insert(id, ContextBox::new(data));
2048        id
2049    }
2050
2051    /// Retrieves mutable client context data given the context ID.
2052    ///
2053    /// See the [module level documentation](index.html) for an overview and examples.
2054    ///
2055    /// # Example
2056    ///
2057    /// ```
2058    /// use molt::types::*;
2059    /// use molt::interp::Interp;
2060    ///
2061    /// let mut interp = Interp::new();
2062    /// let data: Vec<String> = Vec::new();
2063    /// let id = interp.save_context(data);
2064    ///
2065    /// // Later...
2066    /// let data: &mut Vec<String> = interp.context(id);
2067    /// data.push("New Value".into());
2068    ///
2069    /// // Or
2070    /// let data = interp.context::<Vec<String>>(id);
2071    /// data.push("New Value".into());
2072    /// ```
2073    ///
2074    /// # Panics
2075    ///
2076    /// This call panics if the context ID is unknown, or if the retrieved data
2077    /// has an unexpected type.
2078    pub fn context<T: 'static>(&mut self, id: ContextID) -> &mut T {
2079        self.context_map
2080            .get_mut(&id)
2081            .expect("unknown context ID")
2082            .data
2083            .downcast_mut::<T>()
2084            .expect("context type mismatch")
2085    }
2086
2087    /// Generates a unique context ID for command context data.
2088    ///
2089    /// Normally the client will use [`save_context`](#method.save_context) to
2090    /// save the context data and generate the client ID in one operation, rather than
2091    /// call this explicitly.
2092    ////
2093    /// # Example
2094    ///
2095    /// ```
2096    /// use molt::types::*;
2097    /// use molt::interp::Interp;
2098    ///
2099    /// let mut interp = Interp::new();
2100    /// let id1 = interp.context_id();
2101    /// let id2 = interp.context_id();
2102    /// assert_ne!(id1, id2);
2103    /// ```
2104    pub fn context_id(&mut self) -> ContextID {
2105        // TODO: Practically speaking we won't overflow u64; but practically speaking
2106        // we should check any.
2107        self.last_context_id += 1;
2108        ContextID(self.last_context_id)
2109    }
2110
2111    /// Saves a client context value in the interpreter for the given
2112    /// context ID.  Client commands can retrieve the data given the context ID.
2113    ///
2114    /// Normally the client will use [`save_context`](#method.save_context) to
2115    /// save the context data and generate the client ID in one operation, rather than
2116    /// call this explicitly.
2117    ///
2118    /// TODO: This method allows the user to generate a context ID and
2119    /// put data into the context cache as two separate steps; and to update the
2120    /// the data in the context cache for a given ID.  I'm not at all sure that
2121    /// either of those things is a good idea.  Waiting to see.
2122    ///
2123    /// # Example
2124    ///
2125    /// ```
2126    /// use molt::types::*;
2127    /// use molt::interp::Interp;
2128    ///
2129    /// let mut interp = Interp::new();
2130    /// let id = interp.context_id();
2131    /// let data: Vec<String> = Vec::new();
2132    /// interp.set_context(id, data);
2133    /// ```
2134    pub fn set_context<T: 'static>(&mut self, id: ContextID, data: T) {
2135        self.context_map.insert(id, ContextBox::new(data));
2136    }
2137
2138    //--------------------------------------------------------------------------------------------
2139    // Profiling
2140
2141    /// Unstable; use at own risk.
2142    pub fn profile_save(&mut self, name: &str, start: std::time::Instant) {
2143        let dur = Instant::now().duration_since(start).as_nanos();
2144        let rec = self
2145            .profile_map
2146            .entry(name.into())
2147            .or_insert_with(ProfileRecord::new);
2148
2149        rec.count += 1;
2150        rec.nanos += dur;
2151    }
2152
2153    /// Unstable; use at own risk.
2154    pub fn profile_clear(&mut self) {
2155        self.profile_map.clear();
2156    }
2157
2158    /// Unstable; use at own risk.
2159    pub fn profile_dump(&self) {
2160        if self.profile_map.is_empty() {
2161            println!("no profile data");
2162        } else {
2163            for (name, rec) in &self.profile_map {
2164                let avg = rec.nanos / rec.count;
2165                println!("{} nanos {}, count={}", avg, name, rec.count);
2166            }
2167        }
2168    }
2169}
2170
2171/// How a procedure is defined: as an argument list and a body script.
2172/// The argument list is a list of Values, and the body is a Value; each will
2173/// retain its parsed form.
2174///
2175/// NOTE: We do not save the procedure's name; the name exists only in the
2176/// commands table, and can be changed there freely.  The procedure truly doesn't
2177/// know what its name is except when it is being executed.
2178struct Procedure {
2179    /// The procedure's parameter list.  Each item in the list is a name or a
2180    /// name/default value pair.  (This is verified by the `proc` command.)
2181    parms: MoltList,
2182
2183    /// The procedure's body string, as a Value.  As such, it retains both its
2184    /// string value, as needed for introspection, and its parsed Script.
2185    body: Value,
2186}
2187
2188impl Procedure {
2189    fn execute(&self, interp: &mut Interp, argv: &[Value]) -> MoltResult {
2190        // FIRST, push the proc's local scope onto the stack.
2191        interp.push_scope();
2192
2193        // NEXT, process the proc's argument list.
2194        let mut argi = 1; // Skip the proc's name
2195
2196        for (speci, spec) in self.parms.iter().enumerate() {
2197            // FIRST, get the parameter as a vector.  It should be a list of
2198            // one or two elements.
2199            let vec = &*spec.as_list()?; // Should never fail
2200            assert!(vec.len() == 1 || vec.len() == 2);
2201
2202            // NEXT, if this is the args parameter, give the remaining args,
2203            // if any.  Note that "args" has special meaning only if it's the
2204            // final arg spec in the list.
2205            if vec[0].as_str() == "args" && speci == self.parms.len() - 1 {
2206                interp.set_scalar("args", Value::from(&argv[argi..]))?;
2207
2208                // We've processed all of the args
2209                argi = argv.len();
2210                break;
2211            }
2212
2213            // NEXT, do we have a matching argument?
2214            if argi < argv.len() {
2215                // Pair them up
2216                interp.set_scalar(vec[0].as_str(), argv[argi].clone())?;
2217                argi += 1;
2218                continue;
2219            }
2220
2221            // NEXT, do we have a default value?
2222            if vec.len() == 2 {
2223                interp.set_scalar(vec[0].as_str(), vec[1].clone())?;
2224            } else {
2225                // We don't; we're missing a required argument.
2226                return self.wrong_num_args(&argv[0]);
2227            }
2228        }
2229
2230        // NEXT, do we have any arguments left over?
2231
2232        if argi != argv.len() {
2233            return self.wrong_num_args(&argv[0]);
2234        }
2235
2236        // NEXT, evaluate the proc's body, getting the result.
2237        let result = interp.eval_value(&self.body);
2238
2239        // NEXT, pop the scope off of the stack; we're done with it.
2240        interp.pop_scope();
2241
2242        if let Err(mut exception) = result {
2243            // FIRST, handle the return -code, -level protocol
2244            if exception.code() == ResultCode::Return {
2245                exception.decrement_level();
2246            }
2247
2248            return match exception.code() {
2249                ResultCode::Okay => Ok(exception.value()),
2250                ResultCode::Error => Err(exception),
2251                ResultCode::Return => Err(exception), // -level > 0
2252                ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
2253                ResultCode::Continue => molt_err!("invoked \"continue\" outside of a loop"),
2254                // TODO: Better error message
2255                ResultCode::Other(_) => molt_err!("unexpected result code."),
2256            };
2257        }
2258
2259        // NEXT, return the computed result.
2260        // Note: no need for special handling for return, break, continue;
2261        // interp.eval() returns only Ok or a real error.
2262        result
2263    }
2264
2265    // Outputs the wrong # args message for the proc.  The name is passed in
2266    // because it can be changed via the `rename` command.
2267    fn wrong_num_args(&self, name: &Value) -> MoltResult {
2268        let mut msg = String::new();
2269        msg.push_str("wrong # args: should be \"");
2270        msg.push_str(name.as_str());
2271
2272        for (i, arg) in self.parms.iter().enumerate() {
2273            msg.push(' ');
2274
2275            // "args" has special meaning only in the last place.
2276            if arg.as_str() == "args" && i == self.parms.len() - 1 {
2277                msg.push_str("?arg ...?");
2278                break;
2279            }
2280
2281            let vec = arg.as_list().expect("error in proc arglist validation!");
2282
2283            if vec.len() == 1 {
2284                msg.push_str(vec[0].as_str());
2285            } else {
2286                msg.push('?');
2287                msg.push_str(vec[0].as_str());
2288                msg.push('?');
2289            }
2290        }
2291        msg.push_str("\"");
2292
2293        molt_err!(&msg)
2294    }
2295}
2296
2297#[cfg(test)]
2298mod tests {
2299    use super::*;
2300
2301    #[test]
2302    fn test_empty() {
2303        let interp = Interp::empty();
2304        // Interpreter is empty
2305        assert!(interp.command_names().is_empty());
2306    }
2307
2308    #[test]
2309    fn test_new() {
2310        let interp = Interp::new();
2311
2312        // Interpreter is not empty
2313        assert!(!interp.command_names().is_empty());
2314
2315        // Note: in theory, we should test here that the normal set of commands is present.
2316        // In fact, that should be tested by the `molt test` suite.
2317    }
2318
2319    #[test]
2320    fn test_eval() {
2321        let mut interp = Interp::new();
2322
2323        assert_eq!(interp.eval("set a 1"), Ok(Value::from("1")));
2324        assert!(ex_match(
2325            &interp.eval("error 2"),
2326            Exception::molt_err(Value::from("2"))
2327        ));
2328        assert_eq!(interp.eval("return 3"), Ok(Value::from("3")));
2329        assert!(ex_match(
2330            &interp.eval("break"),
2331            Exception::molt_err(Value::from("invoked \"break\" outside of a loop"))
2332        ));
2333        assert!(ex_match(
2334            &interp.eval("continue"),
2335            Exception::molt_err(Value::from("invoked \"continue\" outside of a loop"))
2336        ));
2337    }
2338
2339    // Shows that the result is matches the given exception.  Ignores the exception's
2340    // ErrorData, if any.
2341    fn ex_match(r: &MoltResult, expected: Exception) -> bool {
2342        // FIRST, if the results are of different types, there's no match.
2343        if let Err(e) = r {
2344            e.code() == expected.code() && e.value() == expected.value()
2345        } else {
2346            false
2347        }
2348    }
2349
2350    #[test]
2351    fn test_eval_value() {
2352        let mut interp = Interp::new();
2353
2354        assert_eq!(
2355            interp.eval_value(&Value::from("set a 1")),
2356            Ok(Value::from("1"))
2357        );
2358        assert!(ex_match(
2359            &interp.eval_value(&Value::from("error 2")),
2360            Exception::molt_err(Value::from("2"))
2361        ));
2362        assert_eq!(
2363            interp.eval_value(&Value::from("return 3")),
2364            Ok(Value::from("3"))
2365        );
2366        assert!(ex_match(
2367            &interp.eval_value(&Value::from("break")),
2368            Exception::molt_err(Value::from("invoked \"break\" outside of a loop"))
2369        ));
2370        assert!(ex_match(
2371            &interp.eval_value(&Value::from("continue")),
2372            Exception::molt_err(Value::from("invoked \"continue\" outside of a loop"))
2373        ));
2374    }
2375
2376    #[test]
2377    fn test_complete() {
2378        let mut interp = Interp::new();
2379
2380        assert!(interp.complete("abc"));
2381        assert!(interp.complete("a {bc} [def] \"ghi\" xyz"));
2382
2383        assert!(!interp.complete("a {bc"));
2384        assert!(!interp.complete("a [bc"));
2385        assert!(!interp.complete("a \"bc"));
2386    }
2387
2388    #[test]
2389    fn test_expr() {
2390        let mut interp = Interp::new();
2391        assert_eq!(interp.expr(&Value::from("1 + 2")), Ok(Value::from(3)));
2392        assert_eq!(
2393            interp.expr(&Value::from("a + b")),
2394            Err(Exception::molt_err(Value::from(
2395                "unknown math function \"a\""
2396            )))
2397        );
2398    }
2399
2400    #[test]
2401    fn test_expr_bool() {
2402        let mut interp = Interp::new();
2403        assert_eq!(interp.expr_bool(&Value::from("1")), Ok(true));
2404        assert_eq!(interp.expr_bool(&Value::from("0")), Ok(false));
2405        assert_eq!(
2406            interp.expr_bool(&Value::from("a")),
2407            Err(Exception::molt_err(Value::from(
2408                "unknown math function \"a\""
2409            )))
2410        );
2411    }
2412
2413    #[test]
2414    fn test_expr_int() {
2415        let mut interp = Interp::new();
2416        assert_eq!(interp.expr_int(&Value::from("1 + 2")), Ok(3));
2417        assert_eq!(
2418            interp.expr_int(&Value::from("a")),
2419            Err(Exception::molt_err(Value::from(
2420                "unknown math function \"a\""
2421            )))
2422        );
2423    }
2424
2425    #[test]
2426    fn test_expr_float() {
2427        let mut interp = Interp::new();
2428        let val = interp
2429            .expr_float(&Value::from("1.1 + 2.2"))
2430            .expect("floating point value");
2431
2432        assert!((val - 3.3).abs() < 0.001);
2433
2434        assert_eq!(
2435            interp.expr_float(&Value::from("a")),
2436            Err(Exception::molt_err(Value::from(
2437                "unknown math function \"a\""
2438            )))
2439        );
2440    }
2441
2442    #[test]
2443    fn test_recursion_limit() {
2444        let mut interp = Interp::new();
2445
2446        assert_eq!(interp.recursion_limit(), 1000);
2447        interp.set_recursion_limit(100);
2448        assert_eq!(interp.recursion_limit(), 100);
2449
2450        assert!(dbg!(interp.eval("proc myproc {} { myproc }")).is_ok());
2451        assert!(ex_match(
2452            &interp.eval("myproc"),
2453            Exception::molt_err(Value::from(
2454                "too many nested calls to Interp::eval (infinite loop?)"
2455            ))
2456        ));
2457    }
2458
2459    //-----------------------------------------------------------------------
2460    // Context Cache tests
2461
2462    #[test]
2463    fn context_basic_use() {
2464        let mut interp = Interp::new();
2465
2466        // Save a context object.
2467        let id = interp.save_context(String::from("ABC"));
2468
2469        // Retrieve it.
2470        let ctx = interp.context::<String>(id);
2471        assert_eq!(*ctx, "ABC");
2472        ctx.push_str("DEF");
2473
2474        let ctx = interp.context::<String>(id);
2475        assert_eq!(*ctx, "ABCDEF");
2476    }
2477
2478    #[test]
2479    fn context_advanced_use() {
2480        let mut interp = Interp::new();
2481
2482        // Save a context object.
2483        let id = interp.context_id();
2484        interp.set_context(id, String::from("ABC"));
2485
2486        // Retrieve it.
2487        let ctx = interp.context::<String>(id);
2488        assert_eq!(*ctx, "ABC");
2489    }
2490
2491    #[test]
2492    #[should_panic]
2493    fn context_unknown() {
2494        let mut interp = Interp::new();
2495
2496        // Valid ID Generated, but no context saved.
2497        let id = interp.context_id();
2498
2499        // Try to retrieve it.
2500        let _ctx = interp.context::<String>(id);
2501
2502        // Should panic!
2503    }
2504
2505    #[test]
2506    #[should_panic]
2507    fn context_wrong_type() {
2508        let mut interp = Interp::new();
2509
2510        // Save a context object.
2511        let id = interp.save_context(String::from("ABC"));
2512
2513        // Try to retrieve it as something else.
2514        let _ctx = interp.context::<Vec<String>>(id);
2515
2516        // Should panic!
2517    }
2518
2519    #[test]
2520    #[should_panic]
2521    fn context_forgotten_1_command() {
2522        let mut interp = Interp::new();
2523
2524        // Save a context object.
2525        let id = interp.save_context(String::from("ABC"));
2526
2527        // Use it with a command.
2528        interp.add_context_command("dummy", dummy_cmd, id);
2529
2530        // Remove the command.
2531        interp.remove_command("dummy");
2532
2533        // Try to retrieve it; this should panic.
2534        let _ctx = interp.context::<String>(id);
2535    }
2536
2537    #[test]
2538    #[should_panic(expected = "unknown context ID")]
2539    fn context_forgotten_2_commands() {
2540        let mut interp = Interp::new();
2541
2542        // Save a context object.
2543        let id = interp.save_context(String::from("ABC"));
2544
2545        // Use it with a command.
2546        interp.add_context_command("dummy", dummy_cmd, id);
2547        interp.add_context_command("dummy2", dummy_cmd, id);
2548
2549        // Remove the command.
2550        interp.remove_command("dummy");
2551        assert_eq!(interp.context::<String>(id), "ABC");
2552        interp.remove_command("dummy2");
2553
2554        // Try to retrieve it; this should panic.
2555        let _ctx = interp.context::<String>(id);
2556    }
2557
2558    fn dummy_cmd(_: &mut Interp, _: ContextID, _: &[Value]) -> MoltResult {
2559        molt_err!("Not really meant to be called")
2560    }
2561}