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}