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
128 pub(crate) print: Option<Box<OnPrintCallback>>,
130 pub(crate) debug: Option<Box<OnDebugCallback>>,
132 #[cfg(not(feature = "unchecked"))]
134 pub(crate) progress: Option<Box<crate::func::native::OnProgressCallback>>,
135
136 pub(crate) options: LangOptions,
138
139 pub(crate) def_tag: Dynamic,
141
142 #[cfg(not(feature = "no_optimize"))]
144 pub(crate) optimization_level: crate::OptimizationLevel,
145
146 #[cfg(not(feature = "unchecked"))]
148 pub(crate) limits: crate::api::limits::Limits,
149
150 #[cfg(feature = "debugging")]
152 pub(crate) debugger_interface: Option<(
153 Box<crate::eval::OnDebuggingInit>,
154 Box<crate::eval::OnDebuggerCallback>,
155 )>,
156}
157
158impl fmt::Debug for Engine {
159 #[cold]
160 #[inline(never)]
161 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
162 let mut f = f.debug_struct("Engine");
163
164 f.field("global_modules", &self.global_modules);
165
166 #[cfg(not(feature = "no_module"))]
167 f.field("global_sub_modules", &self.global_sub_modules);
168
169 f.field("disabled_symbols", &self.disabled_symbols);
170
171 #[cfg(not(feature = "no_custom_syntax"))]
172 f.field("custom_keywords", &self.custom_keywords).field(
173 "custom_syntax",
174 &self
175 .custom_syntax
176 .keys()
177 .map(crate::SmartString::as_str)
178 .collect::<String>(),
179 );
180
181 f.field("def_var_filter", &self.def_var_filter.is_some())
182 .field("resolve_var", &self.resolve_var.is_some())
183 .field("token_mapper", &self.token_mapper.is_some());
184
185 #[cfg(not(feature = "unchecked"))]
186 f.field("progress", &self.progress.is_some());
187
188 f.field("options", &self.options)
189 .field("default_tag", &self.def_tag);
190
191 #[cfg(not(feature = "no_optimize"))]
192 f.field("optimization_level", &self.optimization_level);
193
194 #[cfg(not(feature = "unchecked"))]
195 f.field("limits", &self.limits);
196
197 #[cfg(feature = "debugging")]
198 f.field("debugger_interface", &self.debugger_interface.is_some());
199
200 f.finish()
201 }
202}
203
204impl Default for Engine {
205 #[inline(always)]
206 fn default() -> Self {
207 Self::new()
208 }
209}
210
211#[cfg(not(feature = "no_object"))]
213#[inline(always)]
214#[must_use]
215pub fn make_getter(id: &str) -> Identifier {
216 let mut buf = Identifier::new_const();
217 buf.push_str(FN_GET);
218 buf.push_str(id);
219 buf
220}
221
222#[cfg(not(feature = "no_object"))]
224#[inline(always)]
225#[must_use]
226pub fn make_setter(id: &str) -> Identifier {
227 let mut buf = Identifier::new_const();
228 buf.push_str(FN_SET);
229 buf.push_str(id);
230 buf
231}
232
233impl Engine {
234 pub const RAW: Self = Self {
236 global_modules: Vec::new(),
237
238 #[cfg(not(feature = "no_module"))]
239 global_sub_modules: std::collections::BTreeMap::new(),
240
241 #[cfg(not(feature = "no_module"))]
242 module_resolver: None,
243
244 interned_strings: None,
245 disabled_symbols: BTreeSet::new(),
246 #[cfg(not(feature = "no_custom_syntax"))]
247 custom_keywords: std::collections::BTreeMap::new(),
248 #[cfg(not(feature = "no_custom_syntax"))]
249 custom_syntax: std::collections::BTreeMap::new(),
250
251 def_var_filter: None,
252 resolve_var: None,
253 token_mapper: None,
254
255 #[cfg(not(feature = "no_index"))]
256 #[cfg(feature = "internals")]
257 invalid_array_index: None,
258 #[cfg(not(feature = "no_object"))]
259 #[cfg(feature = "internals")]
260 missing_map_property: None,
261
262 print: None,
263 debug: None,
264
265 #[cfg(not(feature = "unchecked"))]
266 progress: None,
267
268 options: LangOptions::new(),
269
270 def_tag: Dynamic::UNIT,
271
272 #[cfg(not(feature = "no_optimize"))]
273 optimization_level: crate::OptimizationLevel::Simple,
274
275 #[cfg(not(feature = "unchecked"))]
276 limits: crate::api::limits::Limits::new(),
277
278 #[cfg(feature = "debugging")]
279 debugger_interface: None,
280 };
281
282 #[inline]
284 #[must_use]
285 pub fn new() -> Self {
286 let mut engine = Self::new_raw();
288
289 #[cfg(not(feature = "no_module"))]
290 #[cfg(not(feature = "no_std"))]
291 #[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
292 {
293 engine.module_resolver =
294 Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
295 }
296
297 engine.set_max_strings_interned(MAX_STRINGS_INTERNED);
299
300 #[cfg(not(feature = "no_std"))]
302 #[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
303 {
304 engine.print = Some(Box::new(|s| println!("{s}")));
305 engine.debug = Some(Box::new(|s, source, pos| match (source, pos) {
306 (Some(source), crate::Position::NONE) => println!("{source} | {s}"),
307 #[cfg(not(feature = "no_position"))]
308 (Some(source), pos) => println!("{source} @ {pos:?} | {s}"),
309 (None, crate::Position::NONE) => println!("{s}"),
310 #[cfg(not(feature = "no_position"))]
311 (None, pos) => println!("{pos:?} | {s}"),
312 }));
313 }
314
315 engine.register_global_module(StandardPackage::new().as_shared_module());
317
318 engine
319 }
320
321 #[inline]
328 #[must_use]
329 pub const fn new_raw() -> Self {
330 Self::RAW
331 }
332
333 #[inline]
340 #[must_use]
341 pub fn get_interned_string(
342 &self,
343 string: impl AsRef<str> + Into<ImmutableString>,
344 ) -> ImmutableString {
345 match self.interned_strings {
346 Some(ref interner) => match locked_write(interner) {
347 Some(mut cache) => cache.get(string),
348 None => string.into(),
349 },
350 None => string.into(),
351 }
352 }
353 #[cfg(not(feature = "no_object"))]
355 #[inline]
356 #[must_use]
357 pub(crate) fn get_interned_getter(
358 &self,
359 text: impl AsRef<str> + Into<ImmutableString>,
360 ) -> ImmutableString {
361 match self.interned_strings {
362 Some(ref interner) => match locked_write(interner) {
363 Some(mut cache) => {
364 cache.get_with_mapper(b'g', |s| make_getter(s.as_ref()).into(), text)
365 }
366 None => make_getter(text.as_ref()).into(),
367 },
368 None => make_getter(text.as_ref()).into(),
369 }
370 }
371
372 #[cfg(not(feature = "no_object"))]
374 #[inline]
375 #[must_use]
376 pub(crate) fn get_interned_setter(
377 &self,
378 text: impl AsRef<str> + Into<ImmutableString>,
379 ) -> ImmutableString {
380 match self.interned_strings {
381 Some(ref interner) => match locked_write(interner) {
382 Some(mut cache) => {
383 cache.get_with_mapper(b's', |s| make_setter(s.as_ref()).into(), text)
384 }
385 None => make_setter(text.as_ref()).into(),
386 },
387 None => make_setter(text.as_ref()).into(),
388 }
389 }
390
391 #[inline(always)]
393 #[must_use]
394 pub fn const_empty_string(&self) -> ImmutableString {
395 self.get_interned_string("")
396 }
397
398 #[cfg(feature = "debugging")]
400 #[inline(always)]
401 #[must_use]
402 pub(crate) const fn is_debugger_registered(&self) -> bool {
403 self.debugger_interface.is_some()
404 }
405}