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}