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