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}