molt_forked/
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::default();
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::default();
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::default();
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::default();
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` (can be multiple, provided as a slice) is provided to the interpreter when adding commands that require that
292//!   context(s).
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::default();
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_ids: &[ContextID], argv: &[Value]) -> MoltResult {
344//!     // Pretend it passed
345//!     interp.context::<Stats>(context_ids[0]).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_ids: &[ContextID], argv: &[Value]) -> MoltResult {
371//!     interp.call_subcommand(context_ids, 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`s are 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
442use crate::dict::dict_new;
443use crate::expr;
444use crate::gen_command;
445use crate::list::list_to_string;
446use crate::molt_err;
447use crate::molt_ok;
448use crate::parser;
449use crate::parser::Script;
450use crate::parser::Word;
451use crate::scope::ScopeStack;
452use crate::types::*;
453use crate::value::Value;
454use std::collections::HashMap;
455use std::rc::Rc;
456cfg_if::cfg_if! {
457  if #[cfg(feature = "wasm")] {
458    use wasm_timer::Instant;
459  }else{
460    use std::time::Instant;
461  }
462}
463
464// Constants
465const OPT_CODE: &str = "-code";
466const OPT_LEVEL: &str = "-level";
467const OPT_ERRORCODE: &str = "-errorcode";
468const OPT_ERRORINFO: &str = "-errorinfo";
469const ZERO: &str = "0";
470
471pub enum CommandType {
472    Native,
473    Embedded,
474    Proc,
475}
476pub struct Command<Ctx: 'static> {
477    fn_execute: fn(&str, &mut Interp<Ctx>, &[Value]) -> MoltResult,
478    fn_type: fn(&str, &Interp<Ctx>) -> Option<CommandType>,
479    native_names: &'static [&'static str],
480    embedded_names: &'static [&'static str],
481}
482impl<Ctx> Command<Ctx> {
483    #[inline]
484    pub fn new(
485        fn_execute: fn(&str, &mut Interp<Ctx>, &[Value]) -> MoltResult,
486        fn_type: fn(&str, &Interp<Ctx>) -> Option<CommandType>,
487        native_names: &'static [&'static str],
488        embedded_names: &'static [&'static str],
489    ) -> Self {
490        Self { fn_execute, fn_type, native_names, embedded_names }
491    }
492}
493cfg_if::cfg_if! {
494  if #[cfg(feature = "std_buff")] {
495/// The Molt Interpreter.
496///
497/// The `Interp` struct is the primary API for
498/// embedding Molt into a Rust application.  The application creates an instance
499/// of `Interp`, configures with it the required set of application-specific
500/// and standard Molt commands, and then uses it to evaluate Molt scripts and
501/// expressions.  See the
502/// [module level documentation](index.html)
503/// for an overview.
504///
505/// # Example
506///
507/// By default, the `Interp` comes configured with the full set of standard
508/// Molt commands.
509///
510/// ```
511/// use molt::types::*;
512/// use molt::Interp;
513/// use molt::molt_ok;
514/// # fn dummy() -> MoltResult {
515/// let mut interp = Interp::default();
516/// let four = interp.eval("expr {2 + 2}")?;
517/// assert_eq!(four, Value::from(4));
518/// # molt_ok!()
519/// # }
520/// ```
521///
522/// The `Interp` can be associated with a lifetime. If so, it is allowed
523/// to create contexts consisting of references and mutable references
524/// within that lifetime. Under the hood, the references are stored as
525/// raw pointers.
526pub struct Interp<Ctx> where
527  Ctx: 'static,
528{
529  pub name: &'static str,
530  // Command Table
531  command: Command<Ctx>,
532  procs: HashMap<String, Rc<Procedure>>,
533  // Variable Table
534  scopes: ScopeStack,
535
536  /// Embedded context
537  pub context: Ctx,
538  pub std_buff: Vec<Result<Value,Exception>>,
539  // Defines the recursion limit for Interp::eval().
540  recursion_limit: usize,
541
542  // Current number of eval levels.
543  num_levels: usize,
544
545  // Profile Map
546  profile_map: HashMap<String, ProfileRecord>,
547
548  // Whether to continue execution in case of error.
549  continue_on_error: bool,
550}
551  }else{
552    /// The Molt Interpreter.
553///
554/// The `Interp` struct is the primary API for
555/// embedding Molt into a Rust application.  The application creates an instance
556/// of `Interp`, configures with it the required set of application-specific
557/// and standard Molt commands, and then uses it to evaluate Molt scripts and
558/// expressions.  See the
559/// [module level documentation](index.html)
560/// for an overview.
561///
562/// # Example
563///
564/// By default, the `Interp` comes configured with the full set of standard
565/// Molt commands.
566///
567/// ```
568/// use molt::types::*;
569/// use molt::Interp;
570/// use molt::molt_ok;
571/// # fn dummy() -> MoltResult {
572/// let mut interp = Interp::default();
573/// let four = interp.eval("expr {2 + 2}")?;
574/// assert_eq!(four, Value::from(4));
575/// # molt_ok!()
576/// # }
577/// ```
578///
579/// The `Interp` can be associated with a lifetime. If so, it is allowed
580/// to create contexts consisting of references and mutable references
581/// within that lifetime. Under the hood, the references are stored as
582/// raw pointers.
583pub struct Interp<Ctx> where
584  Ctx: 'static,
585{
586  pub name: &'static str,
587  // Command Table
588  command: Command<Ctx>,
589  procs: HashMap<String, Rc<Procedure>>,
590  // Variable Table
591  scopes: ScopeStack,
592
593  /// Embedded context
594  pub context: Ctx,
595
596  // Defines the recursion limit for Interp::eval().
597  recursion_limit: usize,
598
599  // Current number of eval levels.
600  num_levels: usize,
601
602  // Profile Map
603  profile_map: HashMap<String, ProfileRecord>,
604
605  // Whether to continue execution in case of error.
606  continue_on_error: bool,
607}
608  }
609}
610
611#[derive(Debug, Clone, Copy)]
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
623impl Interp<()> {
624    /// Creates a new Molt interpreter with no commands defined.  Use this when crafting
625    /// command languages that shouldn't include the normal TCL commands, or as a base
626    /// to which specific Molt command sets can be added.
627    ///
628    /// # Example
629    ///
630    /// ```
631    /// # use molt::interp::Interp;
632    /// let mut interp = Interp::default(());
633    /// ```
634    pub fn default() -> Self {
635        use crate::prelude::*;
636        let command = gen_command!(
637            (),
638            // native commands
639            [
640                // TODO: Requires file access.  Ultimately, might go in an extension crate if
641                // the necessary operations aren't available in core::).
642                (_SOURCE, cmd_source),
643                // TODO: Useful for entire programs written in Molt; but not necessarily wanted in
644                // extension scripts).
645                (_EXIT, cmd_exit),
646                // TODO: Developer Tools
647                (_PARSE, cmd_parse),
648                (_PDUMP, cmd_pdump),
649                (_PCLEAR, cmd_pclear)
650            ],
651            // embedded commands
652            []
653        );
654        Interp::new((), command, true, "default-app")
655    }
656}
657
658// NOTE: The order of methods in the generated RustDoc depends on the order in this block.
659// Consequently, methods are ordered pedagogically.
660impl<Ctx> Interp<Ctx>
661where
662    Ctx: 'static,
663{
664    #[inline]
665    pub fn contains_proc(&self, proc_name: &str) -> bool {
666        self.procs.contains_key(proc_name)
667    }
668    #[inline]
669    pub fn get_proc(&self, proc_name: &str) -> Option<&Rc<Procedure>> {
670        self.procs.get(proc_name)
671    }
672    /// Creates a new Molt interpreter that is pre-populated with the standard Molt commands.
673    /// Use [`command_names`](#method.command_names) (or the `info commands` Molt command)
674    /// to retrieve the full list, and the [`add_command`](#method.add_command) family of
675    /// methods to extend the interpreter with new commands.
676    ///
677    /// TODO: Define command sets (sets of commands that go together, so that clients can
678    /// add or remove them in groups).
679    ///
680    /// ```
681    /// # use molt::types::*;
682    /// # use molt::Interp;
683    /// # use molt::molt_ok;
684    /// # fn dummy() -> MoltResult {
685    /// let mut interp = Interp::default();
686    /// let four = interp.eval("expr {2 + 2}")?;
687    /// assert_eq!(four, Value::from(4));
688    /// # molt_ok!()
689    /// # }
690    /// ```
691    ///
692    #[inline]
693    pub fn new(
694        context: Ctx,
695        command: Command<Ctx>,
696        use_env: bool,
697        name: &'static str,
698    ) -> Self {
699        cfg_if::cfg_if! {
700          if #[cfg(feature = "std_buff")] {
701            let mut interp = Self {
702              name,
703              command,
704              recursion_limit: 1000,
705              procs: HashMap::new(),
706              context,
707              std_buff: Vec::new(),
708              scopes: ScopeStack::new(),
709              num_levels: 0,
710              profile_map: HashMap::new(),
711              continue_on_error: false,
712            };
713          } else {
714            let mut interp = Self {
715              name,
716              recursion_limit: 1000,
717              command,
718              procs: HashMap::new(),
719              context,
720              scopes: ScopeStack::new(),
721              num_levels: 0,
722              profile_map: HashMap::new(),
723              continue_on_error: false,
724            };
725          }
726        }
727
728        interp.set_scalar("errorInfo", Value::empty()).unwrap();
729        if use_env {
730            // Populate the environment variable.
731            // TODO: Really should be a "linked" variable, where sets to it are tracked and
732            // written back to the environment.
733            interp.populate_env();
734        }
735        interp
736    }
737
738    /// Populates the TCL `env()` array with the process's environment variables.
739    ///
740    /// # TCL Liens
741    ///
742    /// Changes to the variable are not mirrored back into the process's environment.
743    #[inline]
744    fn populate_env(&mut self) {
745        for (key, value) in std::env::vars() {
746            // Drop the result, as there's no good reason for this to ever throw an error.
747            let _ = self.set_element("env", &key, value.into());
748        }
749    }
750
751    //--------------------------------------------------------------------------------------------
752    // Script and Expression Evaluation
753
754    /// Evaluates a script one command at a time.  Returns the [`Value`](../value/index.html)
755    /// of the last command in the script, or the value of any explicit `return` call in the
756    /// script, or any error thrown by the script.  Other
757    /// [`Exception`](../types/enum.Exception.html) values are converted to normal errors.
758    ///
759    /// Use this method (or [`eval_value`](#method.eval_value)) to evaluate arbitrary scripts,
760    /// control structure bodies, and so forth.  Prefer `eval_value` if the script is already
761    /// stored in a `Value`, as it will be more efficient if the script is evaluated multiple
762    /// times.
763    ///
764    /// # Example
765    ///
766    /// The following code shows how to evaluate a script and handle the result, whether
767    /// it's a computed `Value` or an error message (which is also a `Value`).
768    ///
769    /// ```
770    /// # use molt::types::*;
771    /// # use molt::Interp;
772    ///
773    /// let mut interp = Interp::default();
774    ///
775    /// let input = "set a 1";
776    ///
777    /// match interp.eval(input) {
778    ///    Ok(val) => {
779    ///        // Computed a Value
780    ///        println!("Value: {}", val);
781    ///    }
782    ///    Err(exception) => {
783    ///        if exception.is_error() {
784    ///            // Got an error; print it out.
785    ///            println!("Error: {}", exception.value());
786    ///        } else {
787    ///            // It's a Return.
788    ///            println!("Value: {}", exception.value());
789    ///        }
790    ///    }
791    /// }
792    /// ```
793    #[inline]
794    pub fn eval(&mut self, script: &str) -> MoltResult {
795        let value = Value::from(script);
796        self.eval_value(&value)
797    }
798
799    /// Evaluates the string value of a [`Value`] as a script.  Returns the `Value`
800    /// of the last command in the script, or the value of any explicit `return` call in the
801    /// script, or any error thrown by the script.  Other
802    /// [`Exception`](../types/enum.Exception.html) values are converted to normal errors.
803    ///
804    /// This method is equivalent to [`eval`](#method.eval), but works on a `Value` rather
805    /// than on a string slice.  Use it or `eval` to evaluate arbitrary scripts,
806    /// control structure bodies, and so forth.  Prefer this to `eval` if the script is already
807    /// stored in a `Value`, as it will be more efficient if the script is evaluated multiple
808    /// times.
809    ///
810    /// [`Value`]: ../value/index.html
811    #[inline]
812    pub fn eval_value(&mut self, value: &Value) -> MoltResult {
813        // TODO: Could probably do better, here.  If the value is already a list, for
814        // example, can maybe evaluate it as a command without using as_script().
815        // Tricky, though.  Don't want to have to parse it as a list.  Need a quick way
816        // to determine if something is already a list.  (Might need two methods!)
817
818        // FIRST, check the number of nesting levels
819        self.num_levels += 1;
820
821        if self.num_levels > self.recursion_limit {
822            self.num_levels -= 1;
823            return molt_err!("too many nested calls to Interp::eval (infinite loop?)");
824        }
825
826        // NEXT, evaluate the script and translate the result to Ok or Error
827        let mut result = self.eval_script(&*value.as_script()?);
828
829        // NEXT, decrement the number of nesting levels.
830        self.num_levels -= 1;
831
832        // NEXT, translate and return the result.
833        if self.num_levels == 0 {
834            if let Err(mut exception) = result {
835                // FIRST, handle the return -code, -level protocol
836                if exception.code() == ResultCode::Return {
837                    exception.decrement_level();
838                }
839
840                result = match exception.code() {
841                    ResultCode::Okay => Ok(exception.value()),
842                    ResultCode::Error => Err(exception),
843                    ResultCode::Return => Err(exception), // -level > 0
844                    ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
845                    ResultCode::Continue => {
846                        molt_err!("invoked \"continue\" outside of a loop")
847                    }
848                    // TODO: Better error message
849                    ResultCode::Other(_) => molt_err!("unexpected result code."),
850                };
851            }
852        }
853
854        if let Err(exception) = &result {
855            if exception.is_error() {
856                self.set_global_error_data(exception.error_data())?;
857            }
858        }
859
860        result
861    }
862
863    /// Saves the error exception data
864    #[inline]
865    fn set_global_error_data(
866        &mut self,
867        error_data: Option<&ErrorData>,
868    ) -> Result<(), Exception> {
869        if let Some(data) = error_data {
870            // TODO: Might want a public method for this.  Or, if I implement namespaces, that's
871            // sufficient.
872            self.scopes.set_global("errorInfo", data.error_info())?;
873            self.scopes.set_global("errorCode", data.error_code())?;
874        }
875
876        Ok(())
877    }
878
879    /// Evaluates a parsed Script, producing a normal MoltResult.
880    /// Also used by expr.rs.
881    ///
882    /// When [`continue_on_error`](Interp::set_continue_on_error)
883    /// is set, the script will proceed even
884    /// if an error is encountered.
885    /// In this case, it will only return an error if the last command
886    /// emits one.
887    pub(crate) fn eval_script(&mut self, script: &Script) -> MoltResult {
888        let mut result_value: MoltResult = Ok(Value::empty());
889
890        for word_vec in script.commands() {
891            let words = match self.eval_word_vec(word_vec.words()) {
892                Ok(words) => words,
893                Err(e) => {
894                    if e.code() == ResultCode::Error && self.continue_on_error {
895                        if let Err(e) = result_value {
896                            // this intermediate error is going to be overwritten.
897                            // (due to `continue_on_error` being set).
898                            // we log it before heading over to next command.
899                            cfg_if::cfg_if! {
900                              if #[cfg(feature = "wasm")] {
901                                self.std_buff.push(Err(e.clone()));
902                              }
903                            }
904                        }
905                        result_value = Err(e);
906                        continue;
907                    }
908                    return Err(e);
909                }
910            };
911
912            if words.is_empty() {
913                break;
914            }
915
916            let name = words[0].as_str();
917
918            if let Err(e) = result_value {
919                // this intermediate error is going to be overwritten.
920                // (due to `continue_on_error` being set).
921                // we log it before heading over to next command.
922                cfg_if::cfg_if! {
923                  if #[cfg(feature = "wasm")] {
924                    self.std_buff.push(Err(e.clone()));
925                  }
926                }
927            }
928
929            // if let Some(cmd) = self.commands.get(name) {
930            // let start = Instant::now();
931            let result = (self.command.fn_execute)(name, self, words.as_slice());
932            // self.profile_save(&format!("cmd.execute({})", name), start);
933
934            if let Ok(v) = result {
935                result_value = Ok(v);
936            } else if let Err(mut exception) = result {
937                // TODO: I think this needs to be done up above.
938                // // Handle the return -code, -level protocol
939                // if exception.code() == ResultCode::Return {
940                //     exception.decrement_level();
941                // }
942
943                match exception.code() {
944                    // ResultCode::Okay => result_value = exception.value(),
945                    ResultCode::Error => {
946                        // FIRST, new error, an error from within a proc, or an error from
947                        // within some other body (ignored).
948                        if exception.is_new_error() {
949                            exception.add_error_info("while executing");
950                            // TODO: Add command.  In standard TCL, this is the text of the command
951                            // before interpolation; at present, we don't have that info in a
952                            // convenient form.  For now, just convert the final words to a string.
953                            exception.add_error_info(&format!(
954                                "  \"{}\"",
955                                &list_to_string(&words)
956                            ));
957                        }
958                        // else if cmd.is_proc() {
959                        //   exception.add_error_info("    invoked from within");
960                        //   exception
961                        //     .add_error_info(&format!("    (procedure \"{}\" line TODO)", name));
962                        //   // TODO: same as above.
963                        //   exception.add_error_info(&format!("\"{}\"", &list_to_string(&words)));
964                        // }
965                    }
966                    // return, continue, break, and custom logic
967                    // always exit the script and
968                    // are not affected by the error flag.
969                    _ => return Err(exception),
970                }
971                if !self.continue_on_error {
972                    return Err(exception);
973                } else {
974                    result_value = Err(exception);
975                }
976            } else {
977                unreachable!();
978            }
979            // } else {
980            //   let err = molt_err!("invalid command name \"{}\"", name);
981            //   if !self.continue_on_error {
982            //     return err;
983            //   } else {
984            //     result_value = err;
985            //   }
986            // }
987        }
988
989        result_value
990    }
991
992    /// Evaluates a WordVec, producing a list of Values.  The expansion operator is handled
993    /// as a special case.
994    #[inline]
995    fn eval_word_vec(&mut self, words: &[Word]) -> Result<MoltList, Exception> {
996        let mut list: MoltList = Vec::new();
997
998        for word in words {
999            if let Word::Expand(word_to_expand) = word {
1000                let value = self.eval_word(word_to_expand)?;
1001                for val in &*value.as_list()? {
1002                    list.push(val.clone());
1003                }
1004            } else {
1005                list.push(self.eval_word(word)?);
1006            }
1007        }
1008
1009        Ok(list)
1010    }
1011
1012    /// Evaluates a single word, producing a value.  This is also used by expr.rs.
1013    #[inline]
1014    pub(crate) fn eval_word(&mut self, word: &Word) -> MoltResult {
1015        match word {
1016            Word::Value(val) => Ok(val.clone()),
1017            Word::VarRef(name) => self.scalar(name),
1018            Word::ArrayRef(name, index_word) => {
1019                let index = self.eval_word(index_word)?;
1020                self.element(name, index.as_str())
1021            }
1022            Word::Script(script) => self.eval_script(script),
1023            Word::Tokens(tokens) => {
1024                let tlist = self.eval_word_vec(tokens)?;
1025                let string: String = tlist.iter().map(|i| i.as_str()).collect();
1026                Ok(Value::from(string))
1027            }
1028            Word::Expand(_) => panic!("recursive Expand!"),
1029            Word::String(str) => Ok(Value::from(str)),
1030        }
1031    }
1032
1033    /// Returns the `return` option dictionary for the given result as a dictionary value.
1034    /// Used by the `catch` command.
1035    pub(crate) fn return_options(&self, result: &MoltResult) -> Value {
1036        let mut opts = dict_new();
1037
1038        match result {
1039            Ok(_) => {
1040                opts.insert(OPT_CODE.into(), ZERO.into());
1041                opts.insert(OPT_LEVEL.into(), ZERO.into());
1042            }
1043            Err(exception) => {
1044                // FIRST, set the -code
1045                match exception.code() {
1046                    ResultCode::Okay => unreachable!(), // TODO: Not in use yet
1047                    ResultCode::Error => {
1048                        let data =
1049                            exception.error_data().expect("Error has no error data");
1050                        opts.insert(OPT_CODE.into(), "1".into());
1051                        opts.insert(OPT_ERRORCODE.into(), data.error_code());
1052                        opts.insert(OPT_ERRORINFO.into(), data.error_info());
1053                        // TODO: Standard TCL also sets -errorstack, -errorline.
1054                    }
1055                    ResultCode::Return => {
1056                        opts.insert(
1057                            OPT_CODE.into(),
1058                            exception.next_code().as_int().into(),
1059                        );
1060                        if let Some(data) = exception.error_data() {
1061                            opts.insert(OPT_ERRORCODE.into(), data.error_code());
1062                            opts.insert(OPT_ERRORINFO.into(), data.error_info());
1063                        }
1064                    }
1065                    ResultCode::Break => {
1066                        opts.insert(OPT_CODE.into(), "3".into());
1067                    }
1068                    ResultCode::Continue => {
1069                        opts.insert(OPT_CODE.into(), "4".into());
1070                    }
1071                    ResultCode::Other(num) => {
1072                        opts.insert(OPT_CODE.into(), num.into());
1073                    }
1074                }
1075
1076                // NEXT, set the -level
1077                opts.insert(OPT_LEVEL.into(), Value::from(exception.level() as MoltInt));
1078            }
1079        }
1080
1081        Value::from(opts)
1082    }
1083
1084    /// Determines whether or not the script is syntactically complete,
1085    /// e.g., has no unmatched quotes, brackets, or braces.
1086    ///
1087    /// REPLs use this to determine whether or not to ask for another line of
1088    /// input.
1089    ///
1090    /// # Example
1091    ///
1092    /// ```
1093    /// # use molt::types::*;
1094    /// # use molt::interp::Interp;
1095    /// let mut interp = Interp::default();
1096    /// assert!(interp.complete("set a [expr {1+1}]"));
1097    /// assert!(!interp.complete("set a [expr {1+1"));
1098    /// ```
1099    #[inline]
1100    pub fn complete(&mut self, script: &str) -> bool {
1101        parser::parse(script).is_ok()
1102    }
1103
1104    /// Evaluates a [Molt expression](https://wduquette.github.io/molt/ref/expr.html) and
1105    /// returns its value.  The expression is passed as a `Value` which is interpreted as a
1106    /// `String`.
1107    ///
1108    /// # Example
1109    /// ```
1110    /// use molt::Interp;
1111    /// use molt::types::*;
1112    /// # fn dummy() -> Result<String,Exception> {
1113    /// let mut interp = Interp::default();
1114    /// let expr = Value::from("2 + 2");
1115    /// let sum = interp.expr(&expr)?.as_int()?;
1116    ///
1117    /// assert_eq!(sum, 4);
1118    /// # Ok("dummy".to_string())
1119    /// # }
1120    /// ```
1121    #[inline]
1122    pub fn expr(&mut self, expr: &Value) -> MoltResult {
1123        // Evaluate the expression and set the errorInfo/errorCode.
1124        let result = expr::expr(self, expr);
1125
1126        if let Err(exception) = &result {
1127            self.set_global_error_data(exception.error_data())?;
1128        }
1129
1130        result
1131    }
1132
1133    /// Evaluates a boolean [Molt expression](https://wduquette.github.io/molt/ref/expr.html)
1134    /// and returns its value, or an error if it couldn't be interpreted as a boolean.
1135    ///
1136    /// # Example
1137    ///
1138    /// ```
1139    /// use molt::Interp;
1140    /// use molt::types::*;
1141    /// # fn dummy() -> Result<String,Exception> {
1142    /// let mut interp = Interp::default();
1143    ///
1144    /// let expr = Value::from("1 < 2");
1145    /// let flag: bool = interp.expr_bool(&expr)?;
1146    ///
1147    /// assert!(flag);
1148    /// # Ok("dummy".to_string())
1149    /// # }
1150    /// ```
1151    #[inline]
1152    pub fn expr_bool(&mut self, expr: &Value) -> Result<bool, Exception> {
1153        self.expr(expr)?.as_bool()
1154    }
1155
1156    /// Evaluates a [Molt expression](https://wduquette.github.io/molt/ref/expr.html)
1157    /// and returns its value as an integer, or an error if it couldn't be interpreted as an
1158    /// integer.
1159    ///
1160    /// # Example
1161    ///
1162    /// ```
1163    /// use molt::Interp;
1164    /// use molt::types::*;
1165    /// # fn dummy() -> Result<String,Exception> {
1166    /// let mut interp = Interp::default();
1167    ///
1168    /// let expr = Value::from("1 + 2");
1169    /// let val: MoltInt = interp.expr_int(&expr)?;
1170    ///
1171    /// assert_eq!(val, 3);
1172    /// # Ok("dummy".to_string())
1173    /// # }
1174    /// ```
1175    #[inline]
1176    pub fn expr_int(&mut self, expr: &Value) -> Result<MoltInt, Exception> {
1177        self.expr(expr)?.as_int()
1178    }
1179
1180    /// Evaluates a [Molt expression](https://wduquette.github.io/molt/ref/expr.html)
1181    /// and returns its value as a float, or an error if it couldn't be interpreted as a
1182    /// float.
1183    ///
1184    /// # Example
1185    ///
1186    /// ```
1187    /// use molt::Interp;
1188    /// use molt::types::*;
1189    /// # fn dummy() -> Result<String,Exception> {
1190    /// let mut interp = Interp::default();
1191    ///
1192    /// let expr = Value::from("1.1 + 2.2");
1193    /// let val: MoltFloat = interp.expr_float(&expr)?;
1194    ///
1195    /// assert_eq!(val, 3.3);
1196    /// # Ok("dummy".to_string())
1197    /// # }
1198    /// ```
1199    #[inline]
1200    pub fn expr_float(&mut self, expr: &Value) -> Result<MoltFloat, Exception> {
1201        self.expr(expr)?.as_float()
1202    }
1203
1204    //--------------------------------------------------------------------------------------------
1205    // Variable Handling
1206
1207    /// Retrieves the value of the named variable in the current scope.  The `var_name` may
1208    /// name a scalar variable or an array element.  This is the normal way to retrieve the
1209    /// value of a variable named by a command argument.
1210    ///
1211    /// Returns an error if the variable is a scalar and the name names an array element,
1212    /// and vice versa.
1213    ///
1214    /// # Example
1215    ///
1216    /// ```
1217    /// use molt::types::*;
1218    /// use molt::Interp;
1219    /// use molt::molt_ok;
1220    /// # fn dummy() -> MoltResult {
1221    /// let mut interp = Interp::default();
1222    ///
1223    /// // Set the value of the scalar variable "a" using a script.
1224    /// interp.eval("set a 1")?;
1225    ///
1226    /// // The value of the scalar variable "a".
1227    /// let val = interp.var(&Value::from("a"))?;
1228    /// assert_eq!(val.as_str(), "1");
1229    ///
1230    /// // Set the value of the array element "b(1)" using a script.
1231    /// interp.eval("set b(1) Howdy")?;
1232    ///
1233    /// // The value of the array element "b(1)":
1234    /// let val = interp.var(&Value::from("b(1)"))?;
1235    /// assert_eq!(val.as_str(), "Howdy");
1236    /// # molt_ok!()
1237    /// # }
1238    /// ```
1239    #[inline]
1240    pub fn var(&self, var_name: &Value) -> MoltResult {
1241        let var_name = &*var_name.as_var_name();
1242        match var_name.index() {
1243            Some(index) => self.element(var_name.name(), index),
1244            None => self.scalar(var_name.name()),
1245        }
1246    }
1247
1248    /// Returns 1 if the named variable is defined and exists, and 0 otherwise.
1249    #[inline]
1250    pub fn var_exists(&self, var_name: &Value) -> bool {
1251        let var_name = &*var_name.as_var_name();
1252        match var_name.index() {
1253            Some(index) => self.scopes.elem_exists(var_name.name(), index),
1254            None => self.scopes.exists(var_name.name()),
1255        }
1256    }
1257
1258    /// Sets the value of the variable in the current scope.  The `var_name` may name a
1259    /// scalar variable or an array element.  This is the usual way to assign a value to
1260    /// a variable named by a command argument.
1261    ///
1262    /// Returns an error if the variable is scalar and the name names an array element,
1263    /// and vice-versa.
1264    ///
1265    /// # Example
1266    ///
1267    /// ```
1268    /// use molt::types::*;
1269    /// use molt::Interp;
1270    /// use molt::molt_ok;
1271    /// # fn dummy() -> MoltResult {
1272    /// let mut interp = Interp::default();
1273    ///
1274    /// // Set the value of the scalar variable "a"
1275    /// let scalar = Value::from("a");  // The variable name
1276    /// interp.set_var(&scalar, Value::from("1"))?;
1277    /// assert_eq!(interp.var(&scalar)?.as_str(), "1");
1278    ///
1279    /// // Set the value of the array element "b(1)":
1280    /// let element = Value::from("b(1)");  // The variable name
1281    /// interp.set_var(&element, Value::from("howdy"))?;
1282    /// assert_eq!(interp.var(&element)?.as_str(), "howdy");
1283    /// # molt_ok!()
1284    /// # }
1285    /// ```
1286    #[inline]
1287    pub fn set_var(&mut self, var_name: &Value, value: Value) -> Result<(), Exception> {
1288        let var_name = &*var_name.as_var_name();
1289        match var_name.index() {
1290            Some(index) => self.set_element(var_name.name(), index, value),
1291            None => self.set_scalar(var_name.name(), value),
1292        }
1293    }
1294
1295    /// Sets the value of the variable in the current scope, return its value.  The `var_name`
1296    /// may name a
1297    /// scalar variable or an array element.  This is the usual way to assign a value to
1298    /// a variable named by a command argument when the command is expected to return the
1299    /// value.
1300    ///
1301    /// Returns an error if the variable is scalar and the name names an array element,
1302    /// and vice-versa.
1303    ///
1304    /// # Example
1305    ///
1306    /// ```
1307    /// use molt::types::*;
1308    /// use molt::Interp;
1309    /// use molt::molt_ok;
1310    /// # fn dummy() -> MoltResult {
1311    /// let mut interp = Interp::default();
1312    ///
1313    /// // Set the value of the scalar variable "a"
1314    /// let scalar = Value::from("a");  // The variable name
1315    /// assert_eq!(interp.set_var_return(&scalar, Value::from("1"))?.as_str(), "1");
1316    ///
1317    /// // Set the value of the array element "b(1)":
1318    /// let element = Value::from("b(1)");  // The variable name
1319    /// interp.set_var(&element, Value::from("howdy"))?;
1320    /// assert_eq!(interp.set_var_return(&element, Value::from("1"))?.as_str(), "1");
1321    /// # molt_ok!()
1322    /// # }
1323    /// ```
1324    #[inline]
1325    pub fn set_var_return(&mut self, var_name: &Value, value: Value) -> MoltResult {
1326        let var_name = &*var_name.as_var_name();
1327        match var_name.index() {
1328            Some(index) => self.set_element_return(var_name.name(), index, value),
1329            None => self.set_scalar_return(var_name.name(), value),
1330        }
1331    }
1332
1333    /// Retrieves the value of the named scalar variable in the current scope.
1334    ///
1335    /// Returns an error if the variable is not found, or if the variable is an array variable.
1336    ///
1337    /// # Example
1338    ///
1339    /// ```
1340    /// use molt::types::*;
1341    /// use molt::Interp;
1342    /// use molt::molt_ok;
1343    /// # fn dummy() -> MoltResult {
1344    /// let mut interp = Interp::default();
1345    ///
1346    /// // Set the value of the scalar variable "a" using a script.
1347    /// interp.eval("set a 1")?;
1348    ///
1349    /// // The value of the scalar variable "a".
1350    /// let val = interp.scalar("a")?;
1351    /// assert_eq!(val.as_str(), "1");
1352    /// # molt_ok!()
1353    /// # }
1354    /// ```
1355    #[inline]
1356    pub fn scalar(&self, name: &str) -> MoltResult {
1357        self.scopes.get(name)
1358    }
1359
1360    /// Sets the value of the named scalar variable in the current scope, creating the variable
1361    /// if necessary.
1362    ///
1363    /// Returns an error if the variable exists and is an array variable.
1364    ///
1365    /// # Example
1366    ///
1367    /// ```
1368    /// use molt::types::*;
1369    /// use molt::Interp;
1370    /// use molt::molt_ok;
1371    /// # fn dummy() -> MoltResult {
1372    /// let mut interp = Interp::default();
1373    ///
1374    /// // Set the value of the scalar variable "a"
1375    /// interp.set_scalar("a", Value::from("1"))?;
1376    /// assert_eq!(interp.scalar("a")?.as_str(), "1");
1377    /// # molt_ok!()
1378    /// # }
1379    /// ```
1380    #[inline]
1381    pub fn set_scalar(&mut self, name: &str, value: Value) -> Result<(), Exception> {
1382        self.scopes.set(name, value)
1383    }
1384
1385    /// Sets the value of the named scalar variable 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 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::default();
1398    ///
1399    /// // Set the value of the scalar variable "a"
1400    /// assert_eq!(interp.set_scalar_return("a", Value::from("1"))?.as_str(), "1");
1401    /// # molt_ok!()
1402    /// # }
1403    #[inline]
1404    pub fn set_scalar_return(&mut self, name: &str, value: Value) -> MoltResult {
1405        // Clone the value, since we'll be returning it out again.
1406        self.scopes.set(name, value.clone())?;
1407        Ok(value)
1408    }
1409
1410    /// Retrieves the value of the named array element in the current scope.
1411    ///
1412    /// Returns an error if the element is not found, or the variable is not an
1413    /// array variable.
1414    ///
1415    /// # Example
1416    ///
1417    /// ```
1418    /// use molt::types::*;
1419    /// use molt::Interp;
1420    /// use molt::molt_ok;
1421    /// # fn dummy() -> MoltResult {
1422    /// let mut interp = Interp::default();
1423    ///
1424    /// // Set the value of the array element variable "a(1)" using a script.
1425    /// interp.eval("set a(1) Howdy")?;
1426    ///
1427    /// // The value of the array element "a(1)".
1428    /// let val = interp.element("a", "1")?;
1429    /// assert_eq!(val.as_str(), "Howdy");
1430    /// # molt_ok!()
1431    /// # }
1432    /// ```
1433    #[inline]
1434    pub fn element(&self, name: &str, index: &str) -> MoltResult {
1435        self.scopes.get_elem(name, index)
1436    }
1437
1438    /// Sets the value of an array element in the current scope, creating the variable
1439    /// if necessary.
1440    ///
1441    /// Returns an error if the variable exists and is not an array variable.
1442    ///
1443    /// # Example
1444    ///
1445    /// ```
1446    /// use molt::types::*;
1447    /// use molt::Interp;
1448    /// use molt::molt_ok;
1449    /// # fn dummy() -> MoltResult {
1450    /// let mut interp = Interp::default();
1451    ///
1452    /// // Set the value of the scalar variable "a"
1453    /// interp.set_element("b", "1", Value::from("xyz"))?;
1454    /// assert_eq!(interp.element("b", "1")?.as_str(), "xyz");
1455    /// # molt_ok!()
1456    /// # }
1457    /// ```
1458    #[inline]
1459    pub fn set_element(
1460        &mut self,
1461        name: &str,
1462        index: &str,
1463        value: Value,
1464    ) -> Result<(), Exception> {
1465        self.scopes.set_elem(name, index, value)
1466    }
1467
1468    /// Sets the value of an array element in the current scope, creating the variable
1469    /// if necessary, and returning the value.
1470    ///
1471    /// Returns an error if the variable exists and is not an array variable.
1472    ///
1473    /// # Example
1474    ///
1475    /// ```
1476    /// use molt::types::*;
1477    /// use molt::Interp;
1478    /// use molt::molt_ok;
1479    /// # fn dummy() -> MoltResult {
1480    /// let mut interp = Interp::default();
1481    ///
1482    /// // Set the value of the scalar variable "a"
1483    /// assert_eq!(interp.set_element_return("b", "1", Value::from("xyz"))?.as_str(), "xyz");
1484    /// # molt_ok!()
1485    /// # }
1486    /// ```
1487    #[inline]
1488    pub fn set_element_return(
1489        &mut self,
1490        name: &str,
1491        index: &str,
1492        value: Value,
1493    ) -> MoltResult {
1494        // Clone the value, since we'll be returning it out again.
1495        self.scopes.set_elem(name, index, value.clone())?;
1496        Ok(value)
1497    }
1498
1499    /// Unsets a variable, whether scalar or array, given its name in the current scope.  For
1500    /// arrays this is the name of the array proper, e.g., `myArray`, not the name of an
1501    /// element, e.g., `myArray(1)`.
1502    ///
1503    /// It is _not_ an error to unset a variable that doesn't exist.
1504    ///
1505    /// # Example
1506    ///
1507    /// ```
1508    /// use molt::types::*;
1509    /// use molt::Interp;
1510    /// use molt::molt_ok;
1511    /// # fn dummy() -> MoltResult {
1512    /// let mut interp = Interp::default();
1513    ///
1514    /// interp.set_scalar("a", Value::from("1"))?;
1515    /// interp.set_element("b", "1", Value::from("2"))?;
1516    ///
1517    /// interp.unset("a"); // Unset scalar
1518    /// interp.unset("b"); // Unset entire array
1519    /// # molt_ok!()
1520    /// # }
1521    /// ```
1522    #[inline]
1523    pub fn unset(&mut self, name: &str) {
1524        self.scopes.unset(name);
1525    }
1526
1527    /// Unsets the value of the named variable or array element in the current scope.
1528    ///
1529    /// It is _not_ an error to unset a variable that doesn't exist.
1530    ///
1531    /// # Example
1532    ///
1533    /// ```
1534    /// use molt::types::*;
1535    /// use molt::Interp;
1536    /// use molt::molt_ok;
1537    /// # fn dummy() -> MoltResult {
1538    /// let mut interp = Interp::default();
1539    ///
1540    /// let scalar = Value::from("a");
1541    /// let array = Value::from("b");
1542    /// let elem = Value::from("b(1)");
1543    ///
1544    /// interp.unset_var(&scalar); // Unset scalar
1545    /// interp.unset_var(&elem);   // Unset array element
1546    /// interp.unset_var(&array);  // Unset entire array
1547    /// # molt_ok!()
1548    /// # }
1549    /// ```
1550    #[inline]
1551    pub fn unset_var(&mut self, name: &Value) {
1552        let var_name = name.as_var_name();
1553
1554        if let Some(index) = var_name.index() {
1555            self.unset_element(var_name.name(), index);
1556        } else {
1557            self.unset(var_name.name());
1558        }
1559    }
1560
1561    /// Unsets a single element in an array given the array name and index.
1562    ///
1563    /// It is _not_ an error to unset an array element that doesn't exist.
1564    ///
1565    /// # Example
1566    ///
1567    /// ```
1568    /// use molt::types::*;
1569    /// use molt::Interp;
1570    /// use molt::molt_ok;
1571    /// # fn dummy() -> MoltResult {
1572    /// let mut interp = Interp::default();
1573    ///
1574    /// interp.set_element("b", "1", Value::from("2"))?;
1575    ///
1576    /// interp.unset_element("b", "1");
1577    /// # molt_ok!()
1578    /// # }
1579    /// ```
1580    #[inline]
1581    pub fn unset_element(&mut self, array_name: &str, index: &str) {
1582        self.scopes.unset_element(array_name, index);
1583    }
1584
1585    /// Gets a list of the names of the variables that are visible in the current scope.
1586    /// The list includes the names of array variables but not elements within them.
1587    ///
1588    /// # Example
1589    ///
1590    /// ```
1591    /// use molt::Interp;
1592    /// use molt::types::*;
1593    ///
1594    /// # let mut interp = Interp::default();
1595    /// for name in interp.vars_in_scope() {
1596    ///     println!("Found variable: {}", name);
1597    /// }
1598    /// ```
1599    #[inline]
1600    pub fn vars_in_scope(&self) -> MoltList {
1601        self.scopes.vars_in_scope()
1602    }
1603
1604    /// Gets a list of the names of the variables defined in the global scope.
1605    /// The list includes the names of array variables but not elements within them.
1606    ///
1607    /// # Example
1608    ///
1609    /// ```
1610    /// use molt::Interp;
1611    /// use molt::types::*;
1612    ///
1613    /// # let mut interp = Interp::default();
1614    /// for name in interp.vars_in_global_scope() {
1615    ///     println!("Found variable: {}", name);
1616    /// }
1617    /// ```
1618    #[inline]
1619    pub fn vars_in_global_scope(&self) -> MoltList {
1620        self.scopes.vars_in_global_scope()
1621    }
1622
1623    /// Gets a list of the names of the variables defined in the local scope.
1624    /// This does not include variables brought into scope via `global` or `upvar`, or any
1625    /// variables defined in the global scope.
1626    /// The list includes the names of array variables but not elements within them.
1627    ///
1628    /// # Example
1629    ///
1630    /// ```
1631    /// use molt::Interp;
1632    /// use molt::types::*;
1633    ///
1634    /// # let mut interp = Interp::default();
1635    /// for name in interp.vars_in_local_scope() {
1636    ///     println!("Found variable: {}", name);
1637    /// }
1638    /// ```
1639    #[inline]
1640    pub fn vars_in_local_scope(&self) -> MoltList {
1641        self.scopes.vars_in_local_scope()
1642    }
1643
1644    /// Links the variable name in the current scope to the given scope.
1645    /// Note: the level is the absolute level, not the level relative to the
1646    /// current stack level, i.e., level=0 is the global scope.
1647    ///
1648    /// This method is used to implement the `upvar` command, which allows variables to be
1649    /// passed by name; client code should rarely need to access it directly.
1650    #[inline]
1651    pub fn upvar(&mut self, level: usize, name: &str) {
1652        assert!(level <= self.scopes.current(), "Invalid scope level");
1653        self.scopes.upvar(level, name);
1654    }
1655
1656    /// Pushes a variable scope (i.e., a stack level) onto the scope stack.
1657    ///
1658    /// Procs use this to define their local scope.  Client code should seldom need to call
1659    /// this directly, but it can be useful in a few cases.  For example, the Molt
1660    /// test harness's `test` command runs its body in a local scope as an aid to test
1661    /// cleanup.
1662    ///
1663    /// **Note:** a command that pushes a scope must also call `Interp::pop_scope` before it
1664    /// exits!
1665    #[inline]
1666    pub fn push_scope(&mut self) {
1667        self.scopes.push();
1668    }
1669
1670    /// Pops a variable scope (i.e., a stack level) off of the scope stack.  Calls to
1671    /// `Interp::push_scope` and `Interp::pop_scope` must exist in pairs.
1672    #[inline]
1673    pub fn pop_scope(&mut self) {
1674        self.scopes.pop();
1675    }
1676
1677    /// Return the current scope level.  The global scope is level `0`; each call to
1678    /// `Interp::push_scope` adds a level, and each call to `Interp::pop_scope` removes it.
1679    /// This method is used with `Interp::upvar` to access the caller's scope when a variable
1680    /// is passed by name.
1681    #[inline]
1682    pub fn scope_level(&self) -> usize {
1683        self.scopes.current()
1684    }
1685
1686    ///-----------------------------------------------------------------------------------
1687    /// Array Manipulation Methods
1688    ///
1689    /// These provide the infrastructure for the `array` command.
1690
1691    /// Unsets an array variable givee its name.  Nothing happens if the variable doesn't
1692    /// exist, or if the variable is not an array variable.
1693    #[inline]
1694    pub(crate) fn array_unset(&mut self, array_name: &str) {
1695        self.scopes.array_unset(array_name);
1696    }
1697
1698    /// Determines whether or not the name is the name of an array variable.
1699    ///
1700    /// # Example
1701    ///
1702    /// ```
1703    /// # use molt::Interp;
1704    /// # use molt::types::*;
1705    /// # use molt::molt_ok;
1706    /// # fn dummy() -> MoltResult {
1707    /// # let mut interp = Interp::default();
1708    /// interp.set_scalar("a", Value::from(1))?;
1709    /// interp.set_element("b", "1", Value::from(2));
1710    ///
1711    /// assert!(!interp.array_exists("a"));
1712    /// assert!(interp.array_exists("b"));
1713    /// # molt_ok!()
1714    /// # }
1715    /// ```
1716    #[inline]
1717    pub fn array_exists(&self, array_name: &str) -> bool {
1718        self.scopes.array_exists(array_name)
1719    }
1720
1721    /// Gets a flat vector of the keys and values from the named array.  This is used to
1722    /// implement the `array get` command.
1723    ///
1724    /// # Example
1725    ///
1726    /// ```
1727    /// use molt::Interp;
1728    /// use molt::types::*;
1729    ///
1730    /// # let mut interp = Interp::default();
1731    /// for txt in interp.array_get("myArray") {
1732    ///     println!("Found index or value: {}", txt);
1733    /// }
1734    /// ```
1735    #[inline]
1736    pub fn array_get(&self, array_name: &str) -> MoltList {
1737        self.scopes.array_get(array_name)
1738    }
1739
1740    /// Merges a flat vector of keys and values into the named array.
1741    /// It's an error if the vector has an odd number of elements, or if the named variable
1742    /// is a scalar.  This method is used to implement the `array set` command.
1743    ///
1744    /// # Example
1745    ///
1746    /// For example, the following Rust code is equivalent to the following Molt code:
1747    ///
1748    /// ```tcl
1749    /// # Set individual elements
1750    /// set myArray(a) 1
1751    /// set myArray(b) 2
1752    ///
1753    /// # Set all at once
1754    /// array set myArray { a 1 b 2 }
1755    /// ```
1756    ///
1757    /// ```
1758    /// use molt::Interp;
1759    /// use molt::types::*;
1760    /// # use molt::molt_ok;
1761    ///
1762    /// # fn dummy() -> MoltResult {
1763    /// # let mut interp = Interp::default();
1764    /// interp.array_set("myArray", &vec!["a".into(), "1".into(), "b".into(), "2".into()])?;
1765    /// # molt_ok!()
1766    /// # }
1767    /// ```
1768    #[inline]
1769    pub fn array_set(&mut self, array_name: &str, kvlist: &[Value]) -> MoltResult {
1770        if kvlist.len() % 2 == 0 {
1771            self.scopes.array_set(array_name, kvlist)?;
1772            molt_ok!()
1773        } else {
1774            molt_err!("list must have an even number of elements")
1775        }
1776    }
1777
1778    /// Gets a list of the indices of the given array.  This is used to implement the
1779    /// `array names` command.  If the variable does not exist (or is not an array variable),
1780    /// the method returns the empty list.
1781    ///
1782    /// # Example
1783    ///
1784    /// ```
1785    /// use molt::Interp;
1786    /// use molt::types::*;
1787    ///
1788    /// # let mut interp = Interp::default();
1789    /// for name in interp.array_names("myArray") {
1790    ///     println!("Found index : {}", name);
1791    /// }
1792    /// ```
1793    #[inline]
1794    pub fn array_names(&self, array_name: &str) -> MoltList {
1795        self.scopes.array_indices(array_name)
1796    }
1797
1798    /// Gets the number of elements in the named array.  Returns 0 if the variable doesn't exist
1799    /// (or isn't an array variable).
1800    ///
1801    /// # Example
1802    ///
1803    /// ```
1804    /// use molt::Interp;
1805    /// use molt::types::*;
1806    ///
1807    /// # use molt::molt_ok;
1808    /// # fn dummy() -> MoltResult {
1809    /// let mut interp = Interp::default();
1810    ///
1811    /// assert_eq!(interp.array_size("a"), 0);
1812    ///
1813    /// interp.set_element("a", "1", Value::from("xyz"))?;
1814    /// assert_eq!(interp.array_size("a"), 1);
1815    /// # molt_ok!()
1816    /// # }
1817    /// ```
1818    #[inline]
1819    pub fn array_size(&self, array_name: &str) -> usize {
1820        self.scopes.array_size(array_name)
1821    }
1822
1823    // //--------------------------------------------------------------------------------------------
1824    // // Command Definition and Handling
1825
1826    // /// Adds a binary command with no related context to the interpreter.  This is the normal
1827    // /// way to add most commands.
1828    // ///
1829    // /// If the command needs access to some form of application or context data,
1830    // /// use [`add_context_command`](#method.add_context_command) instead.  See the
1831    // /// [module level documentation](index.html) for an overview and examples.
1832    // pub fn add_command(
1833    //   &mut self,
1834    //   name: &str,
1835    //   func: impl Fn(&mut Interp<Ctx>, &[Value]) -> MoltResult + 'static,
1836    // ) {
1837    //   self
1838    //     .commands
1839    //     .insert(name.into(), Rc::new(Command::Native(Box::new(func))));
1840    // }
1841
1842    /// Adds a procedure to the interpreter.
1843    ///
1844    /// This is how to add a Molt `proc` to the interpreter.  The arguments are the same
1845    /// as for the `proc` command and the `commands::cmd_proc` function.
1846    ///
1847    /// TODO: If this method is ever made public, the parameter list validation done
1848    /// in cmd_proc should be moved here.
1849    #[inline]
1850    pub(crate) fn add_proc(&mut self, name: &str, parms: &[Value], body: &Value) {
1851        self.procs.insert(
1852            name.into(),
1853            Rc::new(Procedure { parms: parms.to_owned(), body: body.clone() }),
1854        );
1855    }
1856
1857    /// Determines whether or not the interpreter contains a command with the given
1858    /// name.
1859    #[inline]
1860    pub fn has_proc(&self, name: &str) -> bool {
1861        self.procs.contains_key(name)
1862    }
1863
1864    /// Renames the command.
1865    ///
1866    /// **Note:** This does not update procedures that reference the command under the old
1867    /// name.  This is intentional: it is a common TCL programming technique to wrap an
1868    /// existing command by renaming it and defining a new command with the old name that
1869    /// calls the original command at its new name.
1870    ///
1871    /// # Example
1872    ///
1873    /// ```
1874    /// use molt::Interp;
1875    /// use molt::types::*;
1876    /// use molt::molt_ok;
1877    /// # fn dummy() -> MoltResult {
1878    /// let mut interp = Interp::default();
1879    ///
1880    /// interp.rename_command("expr", "=");
1881    ///
1882    /// let sum = interp.eval("= {1 + 1}")?.as_int()?;
1883    ///
1884    /// assert_eq!(sum, 2);
1885    /// # molt_ok!()
1886    /// # }
1887    /// ```
1888    #[inline]
1889    pub fn rename_proc(&mut self, old_name: &str, new_name: &str) {
1890        if let Some(proc) = self.procs.remove(old_name) {
1891            self.procs.insert(new_name.into(), proc);
1892        }
1893    }
1894
1895    /// Removes the command with the given name.
1896    ///
1897    /// This would typically be done when destroying an object command.
1898    ///
1899    /// # Example
1900    ///
1901    /// ```
1902    /// use molt::Interp;
1903    /// use molt::types::*;
1904    /// use molt::molt_ok;
1905    ///
1906    /// let mut interp = Interp::default();
1907    ///
1908    /// interp.remove_command("set");  // You'll be sorry....
1909    ///
1910    /// assert!(!interp.has_command("set"));
1911    /// ```
1912    #[inline]
1913    pub fn remove_proc(&mut self, name: &str) {
1914        // // FIRST, get the command's context ID, if any.
1915        // let context_ids = self.commands.get(name).expect("undefined command").context_ids();
1916
1917        // NEXT, If it has a non-empty context ID slice, decrement their references count; and if the reference
1918        // is zero, remove the context.
1919        // for context_id in context_ids {
1920        //   if self
1921        //     .context_map
1922        //     .get_mut(context_id)
1923        //     .expect("unknown context ID")
1924        //     .decrement()
1925        //   {
1926        //     self.context_map.remove(context_id);
1927        //   }
1928        // }
1929
1930        // FINALLY, remove the command itself.
1931        self.procs.remove(name);
1932    }
1933
1934    /// Gets a vector of the names of the existing commands.
1935    ///
1936    /// # Example
1937    ///
1938    /// ```
1939    /// use molt::Interp;
1940    /// use molt::types::*;
1941    /// use molt::molt_ok;
1942    ///
1943    /// let mut interp = Interp::default();
1944    ///
1945    /// for name in interp.command_names() {
1946    ///     println!("Found command: {}", name);
1947    /// }
1948    /// ```
1949    #[inline]
1950    pub fn command_names(&self) -> MoltList {
1951        let mut vec: MoltList =
1952            self.command.native_names.iter().map(|&s| Value::from(s)).collect();
1953        vec.extend(self.command.embedded_names.iter().map(|&s| Value::from(s)));
1954        vec.extend(self.procs.keys().map(Value::from));
1955        vec
1956    }
1957    #[inline]
1958    pub fn native_command_names(&self) -> String {
1959        self.command.native_names.join(", ")
1960    }
1961    #[inline]
1962    pub fn proc_command_names(&self) -> String {
1963        self.procs
1964            .keys()
1965            .map(String::as_str)
1966            .collect::<Vec<&str>>()
1967            .join(", ")
1968    }
1969
1970    /// Returns the body of the named procedure, or an error if the name doesn't
1971    /// name a procedure.
1972    #[inline]
1973    pub fn command_type(&self, cmd_name: &str) -> MoltResult {
1974        match (self.command.fn_type)(cmd_name, self) {
1975            Some(CommandType::Native) => molt_ok!("native"),
1976            Some(CommandType::Proc) => molt_ok!("proc"),
1977            Some(CommandType::Embedded) => molt_ok!(self.name),
1978            None => molt_err!("\"{}\" isn't a command", cmd_name),
1979        }
1980    }
1981
1982    /// Gets a vector of the names of the existing procedures.
1983    ///
1984    /// # Example
1985    ///
1986    /// ```
1987    /// use molt::Interp;
1988    /// use molt::types::*;
1989    /// use molt::molt_ok;
1990    ///
1991    /// let mut interp = Interp::default();
1992    ///
1993    /// for name in interp.proc_names() {
1994    ///     println!("Found procedure: {}", name);
1995    /// }
1996    /// ```
1997    #[inline]
1998    pub fn proc_names(&self) -> MoltList {
1999        let vec: MoltList =
2000            self.procs.iter().map(|(name, _)| Value::from(name)).collect();
2001        vec
2002    }
2003
2004    /// Returns the body of the named procedure, or an error if the name doesn't
2005    /// name a procedure.
2006    #[inline]
2007    pub fn proc_body(&self, procname: &str) -> MoltResult {
2008        if let Some(proc) = self.procs.get(procname) {
2009            return molt_ok!(proc.body.clone());
2010        }
2011
2012        molt_err!("\"{}\" isn't a procedure", procname)
2013    }
2014
2015    /// Returns a list of the names of the arguments of the named procedure, or an
2016    /// error if the name doesn't name a procedure.
2017    #[inline]
2018    pub fn proc_args(&self, procname: &str) -> MoltResult {
2019        if let Some(proc) = self.procs.get(procname) {
2020            // Note: the item is guaranteed to be parsible as a list of 1 or 2 elements.
2021            let vec: MoltList = proc
2022                .parms
2023                .iter()
2024                .map(|item| item.as_list().expect("invalid proc parms")[0].clone())
2025                .collect();
2026            return molt_ok!(Value::from(vec));
2027        }
2028
2029        molt_err!("\"{}\" isn't a procedure", procname)
2030    }
2031
2032    /// Returns the default value of the named argument of the named procedure, if it has one.
2033    /// Returns an error if the procedure has no such argument, or the `procname` doesn't name
2034    /// a procedure.
2035    #[inline]
2036    pub fn proc_default(
2037        &self,
2038        procname: &str,
2039        arg: &str,
2040    ) -> Result<Option<Value>, Exception> {
2041        if let Some(proc) = self.procs.get(procname) {
2042            for argvec in &proc.parms {
2043                let argvec = argvec.as_list()?; // Should never fail
2044                if argvec[0].as_str() == arg {
2045                    if argvec.len() == 2 {
2046                        return Ok(Some(argvec[1].clone()));
2047                    } else {
2048                        return Ok(None);
2049                    }
2050                }
2051            }
2052            return molt_err!(
2053                "procedure \"{}\" doesn't have an argument \"{}\"",
2054                procname,
2055                arg
2056            );
2057        }
2058
2059        molt_err!("\"{}\" isn't a procedure", procname)
2060    }
2061
2062    //--------------------------------------------------------------------------------------------
2063    // Interpreter Configuration
2064
2065    /// Gets the interpreter's recursion limit: how deep the stack of script evaluations may be.
2066    ///
2067    /// A script stack level is added by each nested script evaluation (i.e., by each call)
2068    /// to [`eval`](#method.eval) or [`eval_value`](#method.eval_value).
2069    ///
2070    /// # Example
2071    /// ```
2072    /// # use molt::types::*;
2073    /// # use molt::interp::Interp;
2074    /// let mut interp = Interp::default();
2075    /// assert_eq!(interp.recursion_limit(), 1000);
2076    /// ```
2077    #[inline]
2078    pub fn recursion_limit(&self) -> usize {
2079        self.recursion_limit
2080    }
2081
2082    /// Sets the interpreter's recursion limit: how deep the stack of script evaluations may
2083    /// be.  The default is 1000.
2084    ///
2085    /// A script stack level is added by each nested script evaluation (i.e., by each call)
2086    /// to [`eval`](#method.eval) or [`eval_value`](#method.eval_value).
2087    ///
2088    /// # Example
2089    /// ```
2090    /// # use molt::types::*;
2091    /// # use molt::interp::Interp;
2092    /// let mut interp = Interp::default();
2093    /// interp.set_recursion_limit(100);
2094    /// assert_eq!(interp.recursion_limit(), 100);
2095    /// ```
2096    #[inline]
2097    pub fn set_recursion_limit(&mut self, limit: usize) {
2098        self.recursion_limit = limit;
2099    }
2100
2101    //--------------------------------------------------------------------------------------------
2102    // Profiling
2103
2104    /// Unstable; use at own risk.
2105    pub fn profile_save(&mut self, name: &str, start: Instant) {
2106        let dur = Instant::now().duration_since(start).as_nanos();
2107        let rec = self.profile_map.entry(name.into()).or_insert_with(ProfileRecord::new);
2108
2109        rec.count += 1;
2110        rec.nanos += dur;
2111    }
2112
2113    /// Unstable; use at own risk.
2114    pub fn profile_clear(&mut self) {
2115        self.profile_map.clear();
2116    }
2117
2118    /// Unstable; use at own risk.
2119    pub fn profile_dump(&self) {
2120        if self.profile_map.is_empty() {
2121            println!("no profile data");
2122        } else {
2123            for (name, rec) in &self.profile_map {
2124                let avg = rec.nanos / rec.count;
2125                println!("{} nanos {}, count={}", avg, name, rec.count);
2126            }
2127        }
2128    }
2129
2130    //--------------------------------------------------------------------------------------------
2131    // Error control
2132
2133    /// get the current continue on error setting. the default is false.
2134    ///
2135    /// # Example
2136    /// ```
2137    /// # use molt::types::*;
2138    /// # use molt::interp::Interp;
2139    /// let mut interp = Interp::default();
2140    /// assert_eq!(interp.continue_on_error(), false);
2141    /// ```
2142    pub fn continue_on_error(&self) -> bool {
2143        self.continue_on_error
2144    }
2145
2146    /// set whether to continue anyway in case of error.
2147    ///
2148    /// # Example
2149    /// ```
2150    /// # use molt::types::*;
2151    /// # use molt::interp::Interp;
2152    /// let mut interp = Interp::default();
2153    /// interp.set_continue_on_error(true);
2154    /// assert_eq!(interp.continue_on_error(), true);
2155    /// ```
2156    pub fn set_continue_on_error(&mut self, c: bool) {
2157        self.continue_on_error = c;
2158    }
2159}
2160
2161/// How a procedure is defined: as an argument list and a body script.
2162/// The argument list is a list of Values, and the body is a Value; each will
2163/// retain its parsed form.
2164///
2165/// NOTE: We do not save the procedure's name; the name exists only in the
2166/// commands table, and can be changed there freely.  The procedure truly doesn't
2167/// know what its name is except when it is being executed.
2168#[derive(Debug, Clone)]
2169pub struct Procedure {
2170    /// The procedure's parameter list.  Each item in the list is a name or a
2171    /// name/default value pair.  (This is verified by the `proc` command.)
2172    parms: MoltList,
2173
2174    /// The procedure's body string, as a Value.  As such, it retains both its
2175    /// string value, as needed for introspection, and its parsed Script.
2176    body: Value,
2177}
2178
2179impl Procedure {
2180    pub fn execute<Ctx>(&self, interp: &mut Interp<Ctx>, argv: &[Value]) -> MoltResult
2181    where
2182        Ctx: 'static,
2183    {
2184        // FIRST, push the proc's local scope onto the stack.
2185        interp.push_scope();
2186
2187        // NEXT, process the proc's argument list.
2188        let mut argi = 1; // Skip the proc's name
2189
2190        for (speci, spec) in self.parms.iter().enumerate() {
2191            // FIRST, get the parameter as a vector.  It should be a list of
2192            // one or two elements.
2193            let vec = &*spec.as_list()?; // Should never fail
2194            assert!(vec.len() == 1 || vec.len() == 2);
2195
2196            // NEXT, if this is the args parameter, give the remaining args,
2197            // if any.  Note that "args" has special meaning only if it's the
2198            // final arg spec in the list.
2199            if vec[0].as_str() == "args" && speci == self.parms.len() - 1 {
2200                interp.set_scalar("args", Value::from(&argv[argi..]))?;
2201
2202                // We've processed all of the args
2203                argi = argv.len();
2204                break;
2205            }
2206
2207            // NEXT, do we have a matching argument?
2208            if argi < argv.len() {
2209                // Pair them up
2210                interp.set_scalar(vec[0].as_str(), argv[argi].clone())?;
2211                argi += 1;
2212                continue;
2213            }
2214
2215            // NEXT, do we have a default value?
2216            if vec.len() == 2 {
2217                interp.set_scalar(vec[0].as_str(), vec[1].clone())?;
2218            } else {
2219                // We don't; we're missing a required argument.
2220                return self.wrong_num_args(&argv[0]);
2221            }
2222        }
2223
2224        // NEXT, do we have any arguments left over?
2225
2226        if argi != argv.len() {
2227            return self.wrong_num_args(&argv[0]);
2228        }
2229
2230        // NEXT, evaluate the proc's body, getting the result.
2231        let result = interp.eval_value(&self.body);
2232
2233        // NEXT, pop the scope off of the stack; we're done with it.
2234        interp.pop_scope();
2235
2236        if let Err(mut exception) = result {
2237            // FIRST, handle the return -code, -level protocol
2238            if exception.code() == ResultCode::Return {
2239                exception.decrement_level();
2240            }
2241
2242            return match exception.code() {
2243                ResultCode::Okay => Ok(exception.value()),
2244                ResultCode::Error => Err(exception),
2245                ResultCode::Return => Err(exception), // -level > 0
2246                ResultCode::Break => molt_err!("invoked \"break\" outside of a loop"),
2247                ResultCode::Continue => {
2248                    molt_err!("invoked \"continue\" outside of a loop")
2249                }
2250                // TODO: Better error message
2251                ResultCode::Other(_) => molt_err!("unexpected result code."),
2252            };
2253        }
2254
2255        // NEXT, return the computed result.
2256        // Note: no need for special handling for return, break, continue;
2257        // interp.eval() returns only Ok or a real error.
2258        result
2259    }
2260
2261    // Outputs the wrong # args message for the proc.  The name is passed in
2262    // because it can be changed via the `rename` command.
2263    fn wrong_num_args(&self, name: &Value) -> MoltResult {
2264        let mut msg = String::new();
2265        msg.push_str("wrong # args: should be \"");
2266        msg.push_str(name.as_str());
2267
2268        for (i, arg) in self.parms.iter().enumerate() {
2269            msg.push(' ');
2270
2271            // "args" has special meaning only in the last place.
2272            if arg.as_str() == "args" && i == self.parms.len() - 1 {
2273                msg.push_str("?arg ...?");
2274                break;
2275            }
2276
2277            let vec = arg.as_list().expect("error in proc arglist validation!");
2278
2279            if vec.len() == 1 {
2280                msg.push_str(vec[0].as_str());
2281            } else {
2282                msg.push('?');
2283                msg.push_str(vec[0].as_str());
2284                msg.push('?');
2285            }
2286        }
2287        msg.push_str("\"");
2288
2289        molt_err!(&msg)
2290    }
2291}
2292
2293#[cfg(test)]
2294mod tests {
2295    use super::*;
2296
2297    #[test]
2298    fn test_new() {
2299        let interp = Interp::default();
2300
2301        // Interpreter is not empty
2302        assert!(!interp.command_names().is_empty());
2303
2304        // Note: in theory, we should test here that the normal set of commands is present.
2305        // In fact, that should be tested by the `molt test` suite.
2306    }
2307
2308    #[test]
2309    fn test_eval() {
2310        let mut interp = Interp::default();
2311
2312        assert_eq!(interp.eval("set a 1"), Ok(Value::from("1")));
2313        assert!(ex_match(&interp.eval("error 2"), Exception::molt_err(Value::from("2"))));
2314        assert_eq!(interp.eval("return 3"), Ok(Value::from("3")));
2315        assert!(ex_match(
2316            &interp.eval("break"),
2317            Exception::molt_err(Value::from("invoked \"break\" outside of a loop"))
2318        ));
2319        assert!(ex_match(
2320            &interp.eval("continue"),
2321            Exception::molt_err(Value::from("invoked \"continue\" outside of a loop"))
2322        ));
2323    }
2324
2325    // Shows that the result is matches the given exception.  Ignores the exception's
2326    // ErrorData, if any.
2327    fn ex_match(r: &MoltResult, expected: Exception) -> bool {
2328        // FIRST, if the results are of different types, there's no match.
2329        if let Err(e) = r {
2330            e.code() == expected.code() && e.value() == expected.value()
2331        } else {
2332            false
2333        }
2334    }
2335
2336    #[test]
2337    fn test_eval_value() {
2338        let mut interp = Interp::default();
2339
2340        assert_eq!(interp.eval_value(&Value::from("set a 1")), Ok(Value::from("1")));
2341        assert!(ex_match(
2342            &interp.eval_value(&Value::from("error 2")),
2343            Exception::molt_err(Value::from("2"))
2344        ));
2345        assert_eq!(interp.eval_value(&Value::from("return 3")), Ok(Value::from("3")));
2346        assert!(ex_match(
2347            &interp.eval_value(&Value::from("break")),
2348            Exception::molt_err(Value::from("invoked \"break\" outside of a loop"))
2349        ));
2350        assert!(ex_match(
2351            &interp.eval_value(&Value::from("continue")),
2352            Exception::molt_err(Value::from("invoked \"continue\" outside of a loop"))
2353        ));
2354    }
2355
2356    #[test]
2357    fn test_complete() {
2358        let mut interp = Interp::default();
2359
2360        assert!(interp.complete("abc"));
2361        assert!(interp.complete("a {bc} [def] \"ghi\" xyz"));
2362
2363        assert!(!interp.complete("a {bc"));
2364        assert!(!interp.complete("a [bc"));
2365        assert!(!interp.complete("a \"bc"));
2366    }
2367
2368    #[test]
2369    fn test_expr() {
2370        let mut interp = Interp::default();
2371        assert_eq!(interp.expr(&Value::from("1 + 2")), Ok(Value::from(3)));
2372        assert_eq!(
2373            interp.expr(&Value::from("a + b")),
2374            Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
2375        );
2376    }
2377
2378    #[test]
2379    fn test_expr_bool() {
2380        let mut interp = Interp::default();
2381        assert_eq!(interp.expr_bool(&Value::from("1")), Ok(true));
2382        assert_eq!(interp.expr_bool(&Value::from("0")), Ok(false));
2383        assert_eq!(
2384            interp.expr_bool(&Value::from("a")),
2385            Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
2386        );
2387    }
2388
2389    #[test]
2390    fn test_expr_int() {
2391        let mut interp = Interp::default();
2392        assert_eq!(interp.expr_int(&Value::from("1 + 2")), Ok(3));
2393        assert_eq!(
2394            interp.expr_int(&Value::from("a")),
2395            Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
2396        );
2397    }
2398
2399    #[test]
2400    fn test_expr_float() {
2401        let mut interp = Interp::default();
2402        let val = interp
2403            .expr_float(&Value::from("1.1 + 2.2"))
2404            .expect("floating point value");
2405
2406        assert!((val - 3.3).abs() < 0.001);
2407
2408        assert_eq!(
2409            interp.expr_float(&Value::from("a")),
2410            Err(Exception::molt_err(Value::from("unknown math function \"a\"")))
2411        );
2412    }
2413
2414    #[test]
2415    fn test_recursion_limit() {
2416        let mut interp = Interp::default();
2417
2418        assert_eq!(interp.recursion_limit(), 1000);
2419        interp.set_recursion_limit(100);
2420        assert_eq!(interp.recursion_limit(), 100);
2421
2422        assert!(dbg!(interp.eval("proc myproc {} { myproc }")).is_ok());
2423        assert!(ex_match(
2424            &interp.eval("myproc"),
2425            Exception::molt_err(Value::from(
2426                "too many nested calls to Interp::eval (infinite loop?)"
2427            ))
2428        ));
2429    }
2430
2431    #[test]
2432    fn context_forgotten_2_commands() {
2433        use crate::prelude::*;
2434        let _interp = Interp::new(
2435            (),
2436            gen_command!(
2437                (),
2438                // native commands
2439                [
2440                    // TODO: Requires file access.  Ultimately, might go in an extension crate if
2441                    // the necessary operations aren't available in core::).
2442                    (_SOURCE, cmd_source),
2443                    // TODO: Useful for entire programs written in Molt; but not necessarily wanted in
2444                    // extension scripts).
2445                    (_EXIT, cmd_exit),
2446                    // TODO: Developer Tools
2447                    (_PARSE, cmd_parse),
2448                    (_PDUMP, cmd_pdump),
2449                    (_PCLEAR, cmd_pclear),
2450                ],
2451                // embedded commands
2452                [("dummy", " ", dummy_cmd, ""), ("dummy2", "", dummy_cmd, ""),],
2453            ),
2454            true,
2455            "",
2456        );
2457    }
2458
2459    fn dummy_cmd(_: &mut Interp<()>, _: &[Value]) -> MoltResult {
2460        molt_err!("Not really meant to be called")
2461    }
2462}