rhai/api/mod.rs
1//! Module defining the public API of the Rhai engine.
2
3pub mod eval;
4
5pub mod run;
6
7pub mod compile;
8
9pub mod json;
10
11pub mod files;
12
13pub mod register;
14
15pub mod call_fn;
16
17pub mod options;
18
19pub mod optimize;
20
21pub mod limits;
22
23pub mod events;
24
25pub mod formatting;
26
27pub mod custom_syntax;
28
29pub mod build_type;
30
31#[cfg(feature = "metadata")]
32pub mod definitions;
33
34pub mod deprecated;
35
36use crate::func::{locked_read, locked_write, Locked};
37use crate::types::StringsInterner;
38use crate::{Dynamic, Engine, Identifier};
39
40#[cfg(feature = "no_std")]
41use std::prelude::v1::*;
42
43/// Default limits.
44pub mod default_limits {
45 #[cfg(not(feature = "unchecked"))]
46 #[cfg(feature = "internals")]
47 pub use super::limits::default_limits::*;
48
49 /// Maximum number of parameters in functions with [`Dynamic`][crate::Dynamic] support.
50 pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
51 /// Maximum number of strings interned.
52 pub const MAX_STRINGS_INTERNED: usize = 256;
53}
54
55impl Engine {
56 /// Set the maximum number of strings to be interned.
57 #[inline(always)]
58 pub fn set_max_strings_interned(&mut self, max: usize) -> &mut Self {
59 if max == 0 {
60 self.interned_strings = None;
61 } else if let Some(ref interner) = self.interned_strings {
62 if let Some(mut guard) = locked_write(interner) {
63 guard.set_max(max);
64 }
65 } else {
66 self.interned_strings = Some(Locked::new(StringsInterner::new(max)));
67 }
68 self
69 }
70 /// The maximum number of strings to be interned.
71 #[inline(always)]
72 #[must_use]
73 pub fn max_strings_interned(&self) -> usize {
74 self.interned_strings.as_ref().map_or(0, |interner| {
75 locked_read(interner).map_or(0, |guard| guard.max())
76 })
77 }
78
79 /// The module resolution service used by the [`Engine`].
80 ///
81 /// Not available under `no_module`.
82 #[cfg(not(feature = "no_module"))]
83 #[inline(always)]
84 #[must_use]
85 pub fn module_resolver(&self) -> &dyn crate::ModuleResolver {
86 static DUMMY_RESOLVER: crate::module::resolvers::DummyModuleResolver =
87 crate::module::resolvers::DummyModuleResolver;
88
89 self.module_resolver.as_deref().unwrap_or(&DUMMY_RESOLVER)
90 }
91
92 /// Set the module resolution service used by the [`Engine`].
93 ///
94 /// Not available under `no_module`.
95 #[cfg(not(feature = "no_module"))]
96 #[inline(always)]
97 pub fn set_module_resolver(
98 &mut self,
99 resolver: impl crate::ModuleResolver + 'static,
100 ) -> &mut Self {
101 self.module_resolver = Some(Box::new(resolver));
102 self
103 }
104
105 /// Disable a particular keyword or operator in the language.
106 ///
107 /// # Examples
108 ///
109 /// The following will raise an error during parsing because the `if` keyword is disabled and is
110 /// recognized as a reserved symbol!
111 ///
112 /// ```rust,should_panic
113 /// # fn main() -> Result<(), rhai::ParseError> {
114 /// use rhai::Engine;
115 ///
116 /// let mut engine = Engine::new();
117 ///
118 /// engine.disable_symbol("if"); // disable the 'if' keyword
119 ///
120 /// engine.compile("let x = if true { 42 } else { 0 };")?;
121 /// // ^ 'if' is rejected as a reserved symbol
122 /// # Ok(())
123 /// # }
124 /// ```
125 ///
126 /// The following will raise an error during parsing because the `+=` operator is disabled.
127 ///
128 /// ```rust,should_panic
129 /// # fn main() -> Result<(), rhai::ParseError> {
130 /// use rhai::Engine;
131 ///
132 /// let mut engine = Engine::new();
133 ///
134 /// engine.disable_symbol("+="); // disable the '+=' operator
135 ///
136 /// engine.compile("let x = 42; x += 1;")?;
137 /// // ^ unknown operator
138 /// # Ok(())
139 /// # }
140 /// ```
141 #[inline]
142 pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
143 self.disabled_symbols.insert(symbol.into());
144 self
145 }
146
147 /// Is a particular keyword or operator disabled?
148 ///
149 /// # Examples
150 ///
151 /// ```rust
152 /// use rhai::Engine;
153 ///
154 /// let mut engine = Engine::new();
155 ///
156 /// engine.disable_symbol("if"); // disable the 'if' keyword
157 ///
158 /// assert!(engine.is_symbol_disabled("if"));
159 /// ```
160 #[inline(always)]
161 #[must_use]
162 pub fn is_symbol_disabled(&self, symbol: &str) -> bool {
163 self.disabled_symbols.contains(symbol)
164 }
165
166 /// Register a custom operator with a precedence into the language.
167 ///
168 /// Not available under `no_custom_syntax`.
169 ///
170 /// The operator can be a valid identifier, a reserved symbol, a disabled operator or a disabled keyword.
171 ///
172 /// The precedence cannot be zero.
173 ///
174 /// # Example
175 ///
176 /// ```rust
177 /// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
178 /// use rhai::Engine;
179 ///
180 /// let mut engine = Engine::new();
181 ///
182 /// // Register a custom operator called '#' and give it
183 /// // a precedence of 160 (i.e. between +|- and *|/).
184 /// engine.register_custom_operator("#", 160).expect("should succeed");
185 ///
186 /// // Register a binary function named '#'
187 /// engine.register_fn("#", |x: i64, y: i64| (x * y) - (x + y));
188 ///
189 /// assert_eq!(
190 /// engine.eval_expression::<i64>("1 + 2 * 3 # 4 - 5 / 6")?,
191 /// 15
192 /// );
193 /// # Ok(())
194 /// # }
195 /// ```
196 #[cfg(not(feature = "no_custom_syntax"))]
197 pub fn register_custom_operator(
198 &mut self,
199 keyword: impl AsRef<str>,
200 precedence: u8,
201 ) -> Result<&mut Self, String> {
202 use crate::tokenizer::Token;
203
204 let precedence = crate::engine::Precedence::new(precedence)
205 .ok_or_else(|| "precedence cannot be zero".to_string())?;
206
207 let keyword = keyword.as_ref();
208
209 match Token::lookup_symbol_from_syntax(keyword) {
210 // Standard identifiers and reserved keywords are OK
211 None | Some(Token::Reserved(..)) => (),
212 // custom keywords are OK
213 #[cfg(not(feature = "no_custom_syntax"))]
214 Some(Token::Custom(..)) => (),
215 // Active standard keywords cannot be made custom
216 // Disabled keywords are OK
217 Some(token)
218 if token.is_standard_keyword()
219 && !self.is_symbol_disabled(token.literal_syntax()) =>
220 {
221 return Err(format!("'{keyword}' is a reserved keyword"))
222 }
223 // Active standard symbols cannot be made custom
224 Some(token)
225 if token.is_standard_symbol()
226 && !self.is_symbol_disabled(token.literal_syntax()) =>
227 {
228 return Err(format!("'{keyword}' is a reserved operator"))
229 }
230 // Active standard symbols cannot be made custom
231 Some(token) if !self.is_symbol_disabled(token.literal_syntax()) => {
232 return Err(format!("'{keyword}' is a reserved symbol"))
233 }
234 // Disabled symbols are OK
235 Some(_) => (),
236 }
237
238 // Add to custom keywords
239 self.custom_keywords
240 .insert(keyword.into(), Some(precedence));
241
242 Ok(self)
243 }
244
245 /// Get the default value of the custom state for each evaluation run.
246 #[inline(always)]
247 pub const fn default_tag(&self) -> &Dynamic {
248 &self.def_tag
249 }
250 /// Get a mutable reference to the default value of the custom state for each evaluation run.
251 #[inline(always)]
252 pub fn default_tag_mut(&mut self) -> &mut Dynamic {
253 &mut self.def_tag
254 }
255 /// Set the default value of the custom state for each evaluation run.
256 #[inline(always)]
257 pub fn set_default_tag(&mut self, value: impl Into<Dynamic>) -> &mut Self {
258 self.def_tag = value.into();
259 self
260 }
261}