use crate::api::default_limits::MAX_STRINGS_INTERNED;
use crate::api::options::LangOptions;
use crate::func::native::{
locked_write, OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback,
OnVarCallback,
};
use crate::packages::{Package, StandardPackage};
use crate::tokenizer::Token;
use crate::types::StringsInterner;
use crate::{Dynamic, Identifier, ImmutableString, Locked, SharedModule};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{collections::BTreeSet, fmt, num::NonZeroU8};
pub type Precedence = NonZeroU8;
pub const KEYWORD_PRINT: &str = "print";
pub const KEYWORD_DEBUG: &str = "debug";
pub const KEYWORD_TYPE_OF: &str = "type_of";
pub const KEYWORD_EVAL: &str = "eval";
pub const KEYWORD_FN_PTR: &str = "Fn";
pub const KEYWORD_FN_PTR_CALL: &str = "call";
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
#[cfg(not(feature = "no_closure"))]
pub const KEYWORD_IS_SHARED: &str = "is_shared";
pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var";
#[cfg(not(feature = "no_function"))]
pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn";
#[cfg(not(feature = "no_function"))]
pub const KEYWORD_THIS: &str = "this";
#[cfg(not(feature = "no_function"))]
#[cfg(not(feature = "no_module"))]
pub const KEYWORD_GLOBAL: &str = "global";
#[cfg(not(feature = "no_object"))]
pub const FN_GET: &str = "get$";
#[cfg(not(feature = "no_object"))]
pub const FN_SET: &str = "set$";
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
pub const FN_IDX_GET: &str = "index$get$";
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
pub const FN_IDX_SET: &str = "index$set$";
#[cfg(not(feature = "no_function"))]
pub const FN_ANONYMOUS: &str = "anon$";
pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax();
pub const OP_CONTAINS: &str = "contains";
pub const OP_NOT: &str = Token::Bang.literal_syntax();
#[cfg(not(feature = "no_module"))]
pub const NAMESPACE_SEPARATOR: &str = Token::DoubleColon.literal_syntax();
pub struct Engine {
pub(crate) global_modules: Vec<SharedModule>,
#[cfg(not(feature = "no_module"))]
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, SharedModule>,
#[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,
pub(crate) interned_strings: Option<Locked<StringsInterner>>,
pub(crate) disabled_symbols: BTreeSet<Identifier>,
#[cfg(not(feature = "no_custom_syntax"))]
pub(crate) custom_keywords: std::collections::BTreeMap<Identifier, Option<Precedence>>,
#[cfg(not(feature = "no_custom_syntax"))]
pub(crate) custom_syntax:
std::collections::BTreeMap<Identifier, Box<crate::api::custom_syntax::CustomSyntax>>,
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
pub(crate) resolve_var: Option<Box<OnVarCallback>>,
pub(crate) token_mapper: Option<Box<OnParseTokenCallback>>,
#[cfg(not(feature = "no_index"))]
#[cfg(feature = "internals")]
pub(crate) invalid_array_index: Option<Box<crate::func::native::OnInvalidArrayIndexCallback>>,
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "internals")]
pub(crate) missing_map_property: Option<Box<crate::func::native::OnMissingMapPropertyCallback>>,
pub(crate) print: Option<Box<OnPrintCallback>>,
pub(crate) debug: Option<Box<OnDebugCallback>>,
#[cfg(not(feature = "unchecked"))]
pub(crate) progress: Option<Box<crate::func::native::OnProgressCallback>>,
pub(crate) options: LangOptions,
pub(crate) def_tag: Dynamic,
#[cfg(not(feature = "no_optimize"))]
pub(crate) optimization_level: crate::OptimizationLevel,
#[cfg(not(feature = "unchecked"))]
pub(crate) limits: crate::api::limits::Limits,
#[cfg(feature = "debugging")]
pub(crate) debugger_interface: Option<(
Box<crate::eval::OnDebuggingInit>,
Box<crate::eval::OnDebuggerCallback>,
)>,
}
impl fmt::Debug for Engine {
#[cold]
#[inline(never)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut f = f.debug_struct("Engine");
f.field("global_modules", &self.global_modules);
#[cfg(not(feature = "no_module"))]
f.field("global_sub_modules", &self.global_sub_modules);
f.field("disabled_symbols", &self.disabled_symbols);
#[cfg(not(feature = "no_custom_syntax"))]
f.field("custom_keywords", &self.custom_keywords).field(
"custom_syntax",
&self
.custom_syntax
.keys()
.map(crate::SmartString::as_str)
.collect::<String>(),
);
f.field("def_var_filter", &self.def_var_filter.is_some())
.field("resolve_var", &self.resolve_var.is_some())
.field("token_mapper", &self.token_mapper.is_some());
#[cfg(not(feature = "unchecked"))]
f.field("progress", &self.progress.is_some());
f.field("options", &self.options)
.field("default_tag", &self.def_tag);
#[cfg(not(feature = "no_optimize"))]
f.field("optimization_level", &self.optimization_level);
#[cfg(not(feature = "unchecked"))]
f.field("limits", &self.limits);
#[cfg(feature = "debugging")]
f.field("debugger_interface", &self.debugger_interface.is_some());
f.finish()
}
}
impl Default for Engine {
#[inline(always)]
#[must_use]
fn default() -> Self {
Self::new()
}
}
#[cfg(not(feature = "no_object"))]
#[inline(always)]
#[must_use]
pub fn make_getter(id: &str) -> Identifier {
let mut buf = Identifier::new_const();
buf.push_str(FN_GET);
buf.push_str(id);
buf
}
#[cfg(not(feature = "no_object"))]
#[inline(always)]
#[must_use]
pub fn make_setter(id: &str) -> Identifier {
let mut buf = Identifier::new_const();
buf.push_str(FN_SET);
buf.push_str(id);
buf
}
impl Engine {
pub const RAW: Self = Self {
global_modules: Vec::new(),
#[cfg(not(feature = "no_module"))]
global_sub_modules: std::collections::BTreeMap::new(),
#[cfg(not(feature = "no_module"))]
module_resolver: None,
interned_strings: None,
disabled_symbols: BTreeSet::new(),
#[cfg(not(feature = "no_custom_syntax"))]
custom_keywords: std::collections::BTreeMap::new(),
#[cfg(not(feature = "no_custom_syntax"))]
custom_syntax: std::collections::BTreeMap::new(),
def_var_filter: None,
resolve_var: None,
token_mapper: None,
#[cfg(not(feature = "no_index"))]
#[cfg(feature = "internals")]
invalid_array_index: None,
#[cfg(not(feature = "no_object"))]
#[cfg(feature = "internals")]
missing_map_property: None,
print: None,
debug: None,
#[cfg(not(feature = "unchecked"))]
progress: None,
options: LangOptions::new(),
def_tag: Dynamic::UNIT,
#[cfg(not(feature = "no_optimize"))]
optimization_level: crate::OptimizationLevel::Simple,
#[cfg(not(feature = "unchecked"))]
limits: crate::api::limits::Limits::new(),
#[cfg(feature = "debugging")]
debugger_interface: None,
};
#[inline]
#[must_use]
pub fn new() -> Self {
let mut engine = Self::new_raw();
#[cfg(not(feature = "no_module"))]
#[cfg(not(feature = "no_std"))]
#[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
{
engine.module_resolver =
Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
}
engine.set_max_strings_interned(MAX_STRINGS_INTERNED);
#[cfg(not(feature = "no_std"))]
#[cfg(any(not(target_family = "wasm"), not(target_os = "unknown")))]
{
engine.print = Some(Box::new(|s| println!("{s}")));
engine.debug = Some(Box::new(|s, source, pos| match (source, pos) {
(Some(source), crate::Position::NONE) => println!("{source} | {s}"),
#[cfg(not(feature = "no_position"))]
(Some(source), pos) => println!("{source} @ {pos:?} | {s}"),
(None, crate::Position::NONE) => println!("{s}"),
#[cfg(not(feature = "no_position"))]
(None, pos) => println!("{pos:?} | {s}"),
}));
}
engine.register_global_module(StandardPackage::new().as_shared_module());
engine
}
#[inline]
#[must_use]
pub const fn new_raw() -> Self {
Self::RAW
}
#[inline]
#[must_use]
pub fn get_interned_string(
&self,
string: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
match self.interned_strings {
Some(ref interner) => locked_write(interner).unwrap().get(string),
None => string.into(),
}
}
#[cfg(not(feature = "no_object"))]
#[inline]
#[must_use]
pub(crate) fn get_interned_getter(
&self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
match self.interned_strings {
Some(ref interner) => locked_write(interner).unwrap().get_with_mapper(
b'g',
|s| make_getter(s.as_ref()).into(),
text,
),
None => make_getter(text.as_ref()).into(),
}
}
#[cfg(not(feature = "no_object"))]
#[inline]
#[must_use]
pub(crate) fn get_interned_setter(
&self,
text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString {
match self.interned_strings {
Some(ref interner) => locked_write(interner).unwrap().get_with_mapper(
b's',
|s| make_setter(s.as_ref()).into(),
text,
),
None => make_setter(text.as_ref()).into(),
}
}
#[inline(always)]
#[must_use]
pub fn const_empty_string(&self) -> ImmutableString {
self.get_interned_string("")
}
#[cfg(feature = "debugging")]
#[inline(always)]
#[must_use]
pub(crate) const fn is_debugger_registered(&self) -> bool {
self.debugger_interface.is_some()
}
}