Skip to main content

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}