1use crate::api::default_limits::MAX_STRINGS_INTERNED;
4use crate::api::options::LangOptions;
5use crate::func::native::{
6 locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback,
7 OnVarCallback,
8};
9use crate::packages::{Package, StandardPackage};
10use crate::tokenizer::Token;
11use crate::types::StringsInterner;
12use crate::{Dynamic, Identifier, ImmutableString, Locked, SharedModule};
13#[cfg(feature = "no_std")]
14use std::prelude::v1::*;
15use std::{collections::BTreeSet, fmt, num::NonZeroU8};
16
17pub type Precedence = NonZeroU8;
18
19pub const KEYWORD_PRINT: &str = "print";
20pub const KEYWORD_DEBUG: &str = "debug";
21pub const KEYWORD_TYPE_OF: &str = "type_of";
22pub const KEYWORD_EVAL: &str = "eval";
23pub const KEYWORD_FN_PTR: &str = "Fn";
24pub const KEYWORD_FN_PTR_CALL: &str = "call";
25pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
26#[cfg(not(feature = "no_closure"))]
27pub const KEYWORD_IS_SHARED: &str = "is_shared";
28pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var";
29#[cfg(not(feature = "no_function"))]
30pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn";
31#[cfg(not(feature = "no_function"))]
32pub const KEYWORD_THIS: &str = "this";
33#[cfg(not(feature = "no_function"))]
34#[cfg(not(feature = "no_module"))]
35pub const KEYWORD_GLOBAL: &str = "global";
36#[cfg(not(feature = "no_object"))]
37pub const FN_GET: &str = "get$";
38#[cfg(not(feature = "no_object"))]
39pub const FN_SET: &str = "set$";
40#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
41pub const FN_IDX_GET: &str = "index$get$";
42#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
43pub const FN_IDX_SET: &str = "index$set$";
44#[cfg(not(feature = "no_function"))]
45pub const FN_ANONYMOUS: &str = "anon$";
46
47pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax();
52
53pub const OP_CONTAINS: &str = "contains";
57
58pub const OP_NOT: &str = Token::Bang.literal_syntax();
60
61#[cfg(not(feature = "no_module"))]
63pub const NAMESPACE_SEPARATOR: &str = Token::DoubleColon.literal_syntax();
64
65pub struct Engine {
89 pub(crate) global_modules: Vec<SharedModule>,
91 #[cfg(not(feature = "no_module"))]
93 pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, SharedModule>,
94
95 #[cfg(not(feature = "no_module"))]
97 pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,
98
99 pub(crate) interned_strings: Option<Locked<StringsInterner>>,
101
102 pub(crate) disabled_symbols: BTreeSet<Identifier>,
104 #[cfg(not(feature = "no_custom_syntax"))]
106 pub(crate) custom_keywords: std::collections::BTreeMap<Identifier, Option<Precedence>>,
107 #[cfg(not(feature = "no_custom_syntax"))]
109 pub(crate) custom_syntax:
110 std::collections::BTreeMap<Identifier, Box<crate::api::custom_syntax::CustomSyntax>>,
111
112 pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
114 pub(crate) resolve_var: Option<Box<OnVarCallback>>,
116 pub(crate) token_mapper: Option<Box<OnParseTokenCallback>>,
118
119 #[cfg(not(feature = "no_index"))]
121 #[cfg(feature = "internals")]
122 pub(crate) invalid_array_index: Option<Box<crate::func::native::OnInvalidArrayIndexCallback>>,
123 #[cfg(not(feature = "no_object"))]
125 #[cfg(feature = "internals")]
126 pub(crate) missing_map_property: Option<Box<crate::func::native::OnMissingMapPropertyCallback>>,
127 #[cfg(feature = "internals")]
129 pub(crate) missing_function: Option<Box<crate::func::native::OnMissingFunctionCallback>>,
130
131 pub(crate) print: Option<Box<OnPrintCallback>>,
133 pub(crate) debug: Option<Box<OnDebugCallback>>,
135 #[cfg(not(feature = "unchecked"))]
137 pub(crate) progress: Option<Box<crate::func::native::OnProgressCallback>>,
138
139 pub(crate) options: LangOptions,
141
142 pub(crate) def_tag: Dynamic,
144
145 #[cfg(not(feature = "no_optimize"))]
147 pub(crate) optimization_level: crate::OptimizationLevel,
148
149 #[cfg(not(feature = "unchecked"))]
151 pub(crate) limits: crate::api::limits::Limits,
152
153 #[cfg(feature = "debugging")]
155 pub(crate) debugger_interface: Option<(
156 Box<crate::eval::OnDebuggingInit>,
157 Box<crate::eval::OnDebuggerCallback>,
158 )>,
159}
160
161impl fmt::Debug for Engine {
162 #[cold]
163 #[inline(never)]
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 let mut f = f.debug_struct("Engine");
166
167 f.field("global_modules", &self.global_modules);
168
169 #[cfg(not(feature = "no_module"))]
170 f.field("global_sub_modules", &self.global_sub_modules);
171
172 f.field("disabled_symbols", &self.disabled_symbols);
173
174 #[cfg(not(feature = "no_custom_syntax"))]
175 f.field("custom_keywords", &self.custom_keywords).field(
176 "custom_syntax",
177 &self
178 .custom_syntax
179 .keys()
180 .map(crate::SmartString::as_str)
181 .collect::<String>(),
182 );
183
184 f.field("def_var_filter", &self.def_var_filter.is_some())
185 .field("resolve_var", &self.resolve_var.is_some())
186 .field("token_mapper", &self.token_mapper.is_some());
187
188 #[cfg(not(feature = "unchecked"))]
189 f.field("progress", &self.progress.is_some());
190
191 f.field("options", &self.options)
192 .field("default_tag", &self.def_tag);
193
194 #[cfg(not(feature = "no_optimize"))]
195 f.field("optimization_level", &self.optimization_level);
196
197 #[cfg(not(feature = "unchecked"))]
198 f.field("limits", &self.limits);
199
200 #[cfg(feature = "debugging")]
201 f.field("debugger_interface", &self.debugger_interface.is_some());
202
203 f.finish()
204 }
205}
206
207impl Default for Engine {
208 #[inline(always)]
209 fn default() -> Self {
210 Self::new()
211 }
212}
213
214#[cfg(not(feature = "no_object"))]
216#[inline(always)]
217#[must_use]
218pub fn make_getter(id: &str) -> Identifier {
219 let mut buf = Identifier::new_const();
220 buf.push_str(FN_GET);
221 buf.push_str(id);
222 buf
223}
224
225#[cfg(not(feature = "no_object"))]
227#[inline(always)]
228#[must_use]
229pub fn make_setter(id: &str) -> Identifier {
230 let mut buf = Identifier::new_const();
231 buf.push_str(FN_SET);
232 buf.push_str(id);
233 buf
234}
235
236impl Engine {
237 pub const RAW: Self = Self {
239 global_modules: Vec::new(),
240
241 #[cfg(not(feature = "no_module"))]
242 global_sub_modules: std::collections::BTreeMap::new(),
243
244 #[cfg(not(feature = "no_module"))]
245 module_resolver: None,
246
247 interned_strings: None,
248 disabled_symbols: BTreeSet::new(),
249 #[cfg(not(feature = "no_custom_syntax"))]
250 custom_keywords: std::collections::BTreeMap::new(),
251 #[cfg(not(feature = "no_custom_syntax"))]
252 custom_syntax: std::collections::BTreeMap::new(),
253
254 def_var_filter: None,
255 resolve_var: None,
256 token_mapper: None,
257
258 #[cfg(not(feature = "no_index"))]
259 #[cfg(feature = "internals")]
260 invalid_array_index: None,
261 #[cfg(not(feature = "no_object"))]
262 #[cfg(feature = "internals")]
263 missing_map_property: None,
264 #[cfg(feature = "internals")]
265 missing_function: None,
266
267 print: None,
268 debug: None,
269
270 #[cfg(not(feature = "unchecked"))]
271 progress: None,
272
273 options: LangOptions::new(),
274
275 def_tag: Dynamic::UNIT,
276
277 #[cfg(not(feature = "no_optimize"))]
278 optimization_level: crate::OptimizationLevel::Simple,
279
280 #[cfg(not(feature = "unchecked"))]
281 limits: crate::api::limits::Limits::new(),
282
283 #[cfg(feature = "debugging")]
284 debugger_interface: None,
285 };
286
287 #[inline]
289 #[must_use]
290 pub fn new() -> Self {
291 let mut engine = Self::new_raw();
293
294 #[cfg(not(feature = "no_module"))]
295 #[cfg(not(feature = "no_std"))]
296 #[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
297 {
298 engine.module_resolver =
299 Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
300 }
301
302 engine.set_max_strings_interned(MAX_STRINGS_INTERNED);
304
305 #[cfg(not(feature = "no_std"))]
307 #[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
308 {
309 engine.print = Some(Box::new(|s| println!("{s}")));
310 engine.debug = Some(Box::new(|s, source, pos| match (source, pos) {
311 (Some(source), crate::Position::NONE) => println!("{source} | {s}"),
312 #[cfg(not(feature = "no_position"))]
313 (Some(source), pos) => println!("{source} @ {pos:?} | {s}"),
314 (None, crate::Position::NONE) => println!("{s}"),
315 #[cfg(not(feature = "no_position"))]
316 (None, pos) => println!("{pos:?} | {s}"),
317 }));
318 }
319
320 engine.register_global_module(StandardPackage::new().as_shared_module());
322
323 engine
324 }
325
326 #[inline]
333 #[must_use]
334 pub const fn new_raw() -> Self {
335 Self::RAW
336 }
337
338 #[inline]
345 #[must_use]
346 pub fn get_interned_string(
347 &self,
348 string: impl AsRef<str> + Into<ImmutableString>,
349 ) -> ImmutableString {
350 match self.interned_strings {
351 Some(ref interner) => match locked_write(interner) {
352 Some(mut cache) => cache.get(string),
353 None => string.into(),
354 },
355 None => string.into(),
356 }
357 }
358 #[cfg(not(feature = "no_object"))]
360 #[inline]
361 #[must_use]
362 pub(crate) fn get_interned_getter(
363 &self,
364 text: impl AsRef<str> + Into<ImmutableString>,
365 ) -> ImmutableString {
366 match self.interned_strings {
367 Some(ref interner) => match locked_write(interner) {
368 Some(mut cache) => {
369 cache.get_with_mapper(b'g', |s| make_getter(s.as_ref()).into(), text)
370 }
371 None => make_getter(text.as_ref()).into(),
372 },
373 None => make_getter(text.as_ref()).into(),
374 }
375 }
376
377 #[cfg(not(feature = "no_object"))]
379 #[inline]
380 #[must_use]
381 pub(crate) fn get_interned_setter(
382 &self,
383 text: impl AsRef<str> + Into<ImmutableString>,
384 ) -> ImmutableString {
385 match self.interned_strings {
386 Some(ref interner) => match locked_write(interner) {
387 Some(mut cache) => {
388 cache.get_with_mapper(b's', |s| make_setter(s.as_ref()).into(), text)
389 }
390 None => make_setter(text.as_ref()).into(),
391 },
392 None => make_setter(text.as_ref()).into(),
393 }
394 }
395
396 #[inline(always)]
398 #[must_use]
399 pub fn const_empty_string(&self) -> ImmutableString {
400 self.get_interned_string("")
401 }
402
403 #[cfg(feature = "debugging")]
405 #[inline(always)]
406 #[must_use]
407 pub(crate) const fn is_debugger_registered(&self) -> bool {
408 self.debugger_interface.is_some()
409 }
410}