molt_forked/
lib.rs

1//! # Molt Client Library
2//!
3//! This module defines the API for Molt clients.
4//! The [`interp`] module defines the Molt interpreter itself, and provides the primary
5//! API.  Values in the Molt language are stored internally using the [`Value`] struct.  Other
6//! relevant data types, including [`MoltResult`] and [`ResultCode`], are defined in
7//! the [`types`] module.
8//!
9//! The [`test_harness`] module defines the test runner for Molt's TCL-level testing.  It
10//! can be used directly in Cargo integration tests or via a Molt shell, whether standard or
11//! custom.
12//!
13//! See [The Molt Book] for an introduction to Molt.
14//!
15//! [The Molt Book]: https://wduquette.github.io/molt/
16//! [`MoltResult`]: types/type.MoltResult.html
17//! [`ResultCode`]: types/enum.ResultCode.html
18//! [`Value`]: value/index.html
19//! [`interp`]: interp/index.html
20//! [`types`]: types/index.html
21//! [`test_harness`]: test_harness/index.html
22
23// #![doc(html_root_url = "https://docs.rs/molt/0.3.0")]
24// #![doc(html_logo_url = "https://github.com/wduquette/molt/raw/master/MoltLogo.png")]
25
26pub use crate::types::*;
27mod commands;
28pub mod dict;
29mod eval_ptr;
30mod expr;
31pub mod interp;
32mod list;
33pub mod prelude;
34mod tokenizer;
35#[macro_use]
36mod macros;
37mod parser;
38mod scope;
39pub mod test_harness;
40pub mod types;
41mod util;
42pub mod value;
43
44/// This function is used in command functions to check whether the command's argument
45/// list is of a proper size for the given command.  If it is, `check_args` returns
46/// the empty result; if not, it returns a Molt error message
47/// `wrong # args: should be "syntax..."`, where _syntax_ is the command's syntax.
48/// It is typically called at the beginning of a command function.
49///
50/// The _argv_ is the argument list, including the command name.
51///
52/// The _namec_ is the number of tokens in the argument that constitute the command
53/// name.  It is usually 1, but would be 2 for a command with subcommands.  The
54/// error message will take those tokens verbatim from _argv_.
55///
56/// _min_ and _max_ are the minimum and maximum valid length for _argv_.  If
57/// _max_ is zero, the command takes an arbitrary number of arguments (but at least _min_).
58///
59/// _argsig_ is the argument signature, to be appended to the command name for inclusion
60/// in the error message.
61///
62/// ## Example
63///
64/// Here are a couple of examples from the Molt code base.  The relevant commands are
65/// documented in the Molt Book.
66///
67/// First, here is the call from the definition of the `set` command, which has the signature
68/// `set varName ?newValue?`.  In TCL command signatures, question marks denote optional
69/// values.  The first argument is the command name, and the _argv_ array must be at least 2
70/// arguments in length but no more than 3.
71///
72/// ```ignore
73/// check_args(1, argv, 2, 3, "varName ?newValue?")?;
74/// ```
75///
76/// Next, here the call from the definition of the `append` command, which appends strings to
77/// the content of a variable.  It has signature `append varName ?value value ...?`.  The first
78/// argument is the command name, and
79/// the second is the variable name to which data will be appended.  The remaining arguments
80/// are string values to append; the question marks indicate that they are optional, and the
81/// ellipsis indicates that there can be any number of them.
82///
83/// ```ignore
84/// check_args(1, argv, 2, 0, "varName ?value value ...?")?;
85/// ```
86pub fn check_args(
87    namec: usize,
88    argv: &[Value],
89    min: usize,
90    max: usize,
91    argsig: &str,
92) -> MoltResult {
93    assert!(namec >= 1);
94    assert!(min >= 1);
95    assert!(!argv.is_empty());
96
97    if argv.len() < min || (max > 0 && argv.len() > max) {
98        let cmd_tokens = Value::from(&argv[0..namec]);
99        molt_err!("wrong # args: should be \"{} {}\"", cmd_tokens.to_string(), argsig)
100    } else {
101        molt_ok!()
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn test_check_args() {
111        assert_ok(&check_args(1, &mklist(vec!["mycmd"].as_slice()), 1, 1, ""));
112        assert_ok(&check_args(1, &mklist(vec!["mycmd"].as_slice()), 1, 2, "arg1"));
113        assert_ok(&check_args(
114            1,
115            &mklist(vec!["mycmd", "data"].as_slice()),
116            1,
117            2,
118            "arg1",
119        ));
120        assert_ok(&check_args(
121            1,
122            &mklist(vec!["mycmd", "data", "data2"].as_slice()),
123            1,
124            0,
125            "arg1",
126        ));
127
128        assert_err(
129            &check_args(1, &mklist(vec!["mycmd"].as_slice()), 2, 2, "arg1"),
130            "wrong # args: should be \"mycmd arg1\"",
131        );
132        assert_err(
133            &check_args(
134                1,
135                &mklist(vec!["mycmd", "val1", "val2"].as_slice()),
136                2,
137                2,
138                "arg1",
139            ),
140            "wrong # args: should be \"mycmd arg1\"",
141        );
142    }
143
144    // TODO: stopgap until we have finalized the MoltList API.
145    fn mklist(argv: &[&str]) -> MoltList {
146        argv.iter().map(|s| Value::from(*s)).collect()
147    }
148
149    // Helpers
150
151    fn assert_err(result: &MoltResult, msg: &str) {
152        assert_eq!(molt_err!(msg), *result);
153    }
154
155    fn assert_ok(result: &MoltResult) {
156        assert!(result.is_ok(), "Result is not Ok");
157    }
158}