molt_ng/
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::interp::Interp;
27pub use crate::test_harness::test_harness;
28pub use crate::types::*;
29
30mod commands;
31pub mod dict;
32mod eval_ptr;
33mod expr;
34pub mod interp;
35mod list;
36mod tokenizer;
37#[macro_use]
38mod macros;
39mod parser;
40mod scope;
41pub mod test_harness;
42pub mod types;
43mod util;
44pub mod value;
45
46/// This function is used in command functions to check whether the command's argument
47/// list is of a proper size for the given command.  If it is, `check_args` returns
48/// the empty result; if not, it returns a Molt error message
49/// `wrong # args: should be "syntax..."`, where _syntax_ is the command's syntax.
50/// It is typically called at the beginning of a command function.
51///
52/// The _argv_ is the argument list, including the command name.
53///
54/// The _namec_ is the number of tokens in the argument that constitute the command
55/// name.  It is usually 1, but would be 2 for a command with subcommands.  The
56/// error message will take those tokens verbatim from _argv_.
57///
58/// _min_ and _max_ are the minimum and maximum valid length for _argv_.  If
59/// _max_ is zero, the command takes an arbitrary number of arguments (but at least _min_).
60///
61/// _argsig_ is the argument signature, to be appended to the command name for inclusion
62/// in the error message.
63///
64/// ## Example
65///
66/// Here are a couple of examples from the Molt code base.  The relevant commands are
67/// documented in the Molt Book.
68///
69/// First, here is the call from the definition of the `set` command, which has the signature
70/// `set varName ?newValue?`.  In TCL command signatures, question marks denote optional
71/// values.  The first argument is the command name, and the _argv_ array must be at least 2
72/// arguments in length but no more than 3.
73///
74/// ```ignore
75/// check_args(1, argv, 2, 3, "varName ?newValue?")?;
76/// ```
77///
78/// Next, here the call from the definition of the `append` command, which appends strings to
79/// the content of a variable.  It has signature `append varName ?value value ...?`.  The first
80/// argument is the command name, and
81/// the second is the variable name to which data will be appended.  The remaining arguments
82/// are string values to append; the question marks indicate that they are optional, and the
83/// ellipsis indicates that there can be any number of them.
84///
85/// ```ignore
86/// check_args(1, argv, 2, 0, "varName ?value value ...?")?;
87/// ```
88pub fn check_args(
89    namec: usize,
90    argv: &[Value],
91    min: usize,
92    max: usize,
93    argsig: &str,
94) -> MoltResult {
95    assert!(namec >= 1);
96    assert!(min >= 1);
97    assert!(!argv.is_empty());
98
99    if argv.len() < min || (max > 0 && argv.len() > max) {
100        let cmd_tokens = Value::from(&argv[0..namec]);
101        molt_err!(
102            "wrong # args: should be \"{} {}\"",
103            cmd_tokens.to_string(),
104            argsig
105        )
106    } else {
107        molt_ok!()
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_check_args() {
117        assert_ok(&check_args(1, &mklist(vec!["mycmd"].as_slice()), 1, 1, ""));
118        assert_ok(&check_args(
119            1,
120            &mklist(vec!["mycmd"].as_slice()),
121            1,
122            2,
123            "arg1",
124        ));
125        assert_ok(&check_args(
126            1,
127            &mklist(vec!["mycmd", "data"].as_slice()),
128            1,
129            2,
130            "arg1",
131        ));
132        assert_ok(&check_args(
133            1,
134            &mklist(vec!["mycmd", "data", "data2"].as_slice()),
135            1,
136            0,
137            "arg1",
138        ));
139
140        assert_err(
141            &check_args(1, &mklist(vec!["mycmd"].as_slice()), 2, 2, "arg1"),
142            "wrong # args: should be \"mycmd arg1\"",
143        );
144        assert_err(
145            &check_args(
146                1,
147                &mklist(vec!["mycmd", "val1", "val2"].as_slice()),
148                2,
149                2,
150                "arg1",
151            ),
152            "wrong # args: should be \"mycmd arg1\"",
153        );
154    }
155
156    // TODO: stopgap until we have finalized the MoltList API.
157    fn mklist(argv: &[&str]) -> MoltList {
158        argv.iter().map(|s| Value::from(*s)).collect()
159    }
160
161    // Helpers
162
163    fn assert_err(result: &MoltResult, msg: &str) {
164        assert_eq!(molt_err!(msg), *result);
165    }
166
167    fn assert_ok(result: &MoltResult) {
168        assert!(result.is_ok(), "Result is not Ok");
169    }
170}