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