Remove Option from source and use empty string as no source.

This commit is contained in:
Stephen Chung 2022-01-01 17:20:00 +08:00
parent 8329baea29
commit 135b1f54c2
10 changed files with 118 additions and 98 deletions

View File

@ -209,9 +209,8 @@ impl Engine {
level: usize,
) -> RhaiResult {
let mut state = EvalState::new();
if ast.source_raw().is_some() {
global.source = ast.source_raw().cloned();
}
global.source = ast.source_raw().clone();
#[cfg(not(feature = "no_module"))]
{
global.embedded_module_resolver = ast.resolver().cloned();

View File

@ -46,9 +46,8 @@ impl Engine {
pub fn run_ast_with_scope(&self, scope: &mut Scope, ast: &AST) -> RhaiResultOf<()> {
let global = &mut GlobalRuntimeState::new();
let mut state = EvalState::new();
if ast.source_raw().is_some() {
global.source = ast.source_raw().cloned();
}
global.source = ast.source_raw().clone();
#[cfg(not(feature = "no_module"))]
{
global.embedded_module_resolver = ast.resolver().cloned();

View File

@ -17,7 +17,8 @@ use std::{
#[derive(Debug, Clone)]
pub struct AST {
/// Source of the [`AST`].
source: Option<Identifier>,
/// No source if string is empty.
source: Identifier,
/// Global statements.
body: StmtBlock,
/// Script-defined functions.
@ -45,7 +46,7 @@ impl AST {
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
) -> Self {
Self {
source: None,
source: Identifier::new_const(),
body: StmtBlock::new(statements, Position::NONE),
#[cfg(not(feature = "no_function"))]
functions: functions.into(),
@ -63,7 +64,7 @@ impl AST {
#[cfg(not(feature = "no_function"))] functions: impl Into<crate::Shared<crate::Module>>,
) -> Self {
Self {
source: None,
source: Identifier::new_const(),
body: StmtBlock::new(statements, Position::NONE),
#[cfg(not(feature = "no_function"))]
functions: functions.into(),
@ -111,7 +112,7 @@ impl AST {
#[must_use]
pub fn empty() -> Self {
Self {
source: None,
source: Identifier::new_const(),
body: StmtBlock::NONE,
#[cfg(not(feature = "no_function"))]
functions: crate::Module::new().into(),
@ -123,13 +124,17 @@ impl AST {
#[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> {
self.source.as_ref().map(|s| s.as_str())
if self.source.is_empty() {
None
} else {
Some(&self.source)
}
}
/// Get a reference to the source.
#[inline(always)]
#[must_use]
pub(crate) fn source_raw(&self) -> Option<&Identifier> {
self.source.as_ref()
pub(crate) fn source_raw(&self) -> &Identifier {
&self.source
}
/// Set the source.
#[inline]
@ -139,13 +144,13 @@ impl AST {
crate::Shared::get_mut(&mut self.functions)
.as_mut()
.map(|m| m.set_id(source.clone()));
self.source = Some(source);
self.source = source;
self
}
/// Clear the source.
#[inline(always)]
pub fn clear_source(&mut self) -> &mut Self {
self.source = None;
self.source.clear();
self
}
/// Get the statements.
@ -467,8 +472,6 @@ impl AST {
(true, true) => StmtBlock::NONE,
};
let source = other.source.clone().or_else(|| self.source.clone());
#[cfg(not(feature = "no_function"))]
let functions = {
let mut functions = self.functions.as_ref().clone();
@ -476,12 +479,12 @@ impl AST {
functions
};
if let Some(source) = source {
if !other.source.is_empty() {
Self::new_with_source(
merged,
#[cfg(not(feature = "no_function"))]
functions,
source,
other.source.clone(),
)
} else {
Self::new(

View File

@ -2,6 +2,7 @@
use crate::api::custom_syntax::CustomSyntax;
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, Stmt, AST_OPTION_FLAGS::*};
use crate::func::call::FnResolutionCache;
use crate::func::native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback};
use crate::func::{get_hasher, CallableFunction, IteratorFn};
use crate::module::Namespace;
@ -507,7 +508,8 @@ pub struct GlobalRuntimeState {
/// Stack of imported [modules][Module].
modules: StaticVec<Shared<Module>>,
/// Source of the current context.
pub source: Option<Identifier>,
/// No source if the string is empty.
pub source: Identifier,
/// Number of operations performed.
pub num_operations: u64,
/// Number of modules loaded.
@ -539,7 +541,7 @@ impl GlobalRuntimeState {
Self {
keys: StaticVec::new_const(),
modules: StaticVec::new_const(),
source: None,
source: Identifier::new_const(),
num_operations: 0,
num_modules_loaded: 0,
#[cfg(not(feature = "no_module"))]
@ -629,11 +631,11 @@ impl GlobalRuntimeState {
/// Get the specified function via its hash key from the stack of globally-imported [modules][Module].
#[inline]
#[must_use]
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&Identifier>)> {
pub fn get_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&str>)> {
self.modules
.iter()
.rev()
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id())))
}
/// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of
/// globally-imported [modules][Module]?
@ -746,20 +748,6 @@ impl fmt::Debug for GlobalRuntimeState {
}
}
/// _(internals)_ An entry in a function resolution cache.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone)]
pub struct FnResolutionCacheEntry {
/// Function.
pub func: CallableFunction,
/// Optional source.
pub source: Option<Box<str>>,
}
/// _(internals)_ A function resolution cache.
/// Exported under the `internals` feature only.
pub type FnResolutionCache = BTreeMap<u64, Option<Box<FnResolutionCacheEntry>>>;
/// _(internals)_ A type that holds all the current states of the [`Engine`].
/// Exported under the `internals` feature only.
#[derive(Debug, Clone)]
@ -844,7 +832,11 @@ impl<'x, 'px, 'pt> EvalContext<'_, 'x, 'px, '_, '_, '_, '_, 'pt> {
#[inline(always)]
#[must_use]
pub fn source(&self) -> Option<&str> {
self.global.source.as_ref().map(|s| s.as_str())
if self.global.source.is_empty() {
None
} else {
Some(&self.global.source)
}
}
/// The current [`Scope`].
#[inline(always)]
@ -3041,8 +3033,8 @@ impl Engine {
err_map.insert("message".into(), err.to_string().into());
if let Some(ref source) = global.source {
err_map.insert("source".into(), source.as_str().into());
if !global.source.is_empty() {
err_map.insert("source".into(), global.source.clone().into());
}
if err_pos.is_none() {
@ -3174,7 +3166,11 @@ impl Engine {
{
use crate::ModuleResolver;
let source = global.source.as_ref().map(|s| s.as_str());
let source = if global.source.is_empty() {
None
} else {
Some(global.source.as_str())
};
let path_pos = expr.position();
let module = global

View File

@ -6,9 +6,8 @@ use super::{get_builtin_binary_op_fn, get_builtin_op_assignment_fn};
use crate::api::default_limits::MAX_DYNAMIC_PARAMETERS;
use crate::ast::{Expr, FnCallHashes, Stmt};
use crate::engine::{
EvalState, FnResolutionCacheEntry, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL,
KEYWORD_FN_PTR, KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT,
KEYWORD_TYPE_OF,
EvalState, GlobalRuntimeState, KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR,
KEYWORD_FN_PTR_CALL, KEYWORD_FN_PTR_CURRY, KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF,
};
use crate::module::Namespace;
use crate::tokenizer::Token;
@ -20,6 +19,7 @@ use crate::{
use std::prelude::v1::*;
use std::{
any::{type_name, TypeId},
collections::BTreeMap,
convert::TryFrom,
mem,
};
@ -125,6 +125,24 @@ pub fn ensure_no_data_race(
Ok(())
}
/// _(internals)_ An entry in a function resolution cache.
/// Exported under the `internals` feature only.
#[derive(Debug, Clone)]
pub struct FnResolutionCacheEntry {
/// Function.
pub func: CallableFunction,
/// Optional source.
/// No source if the string is empty.
pub source: Identifier,
}
/// _(internals)_ A function resolution cache.
/// Exported under the `internals` feature only.
///
/// [`FnResolutionCacheEntry`] is [`Box`]ed in order to pack as many entries inside a single B-Tree
/// level as possible.
pub type FnResolutionCache = BTreeMap<u64, Option<Box<FnResolutionCacheEntry>>>;
impl Engine {
/// Generate the signature for a function call.
#[inline]
@ -206,14 +224,14 @@ impl Engine {
.find_map(|m| {
m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
func,
source: m.id_raw().map(|s| s.to_string().into_boxed_str()),
source: m.id_raw().clone(),
})
})
.or_else(|| {
self.global_modules.iter().find_map(|m| {
m.get_fn(hash).cloned().map(|func| FnResolutionCacheEntry {
func,
source: m.id_raw().map(|s| s.to_string().into_boxed_str()),
source: m.id_raw().clone(),
})
})
})
@ -222,7 +240,8 @@ impl Engine {
.get_fn(hash)
.map(|(func, source)| FnResolutionCacheEntry {
func: func.clone(),
source: source.map(|s| s.to_string().into_boxed_str()),
source: source
.map_or_else(|| Identifier::new_const(), Into::into),
})
})
.or_else(|| {
@ -230,7 +249,7 @@ impl Engine {
m.get_qualified_fn(hash).cloned().map(|func| {
FnResolutionCacheEntry {
func,
source: m.id_raw().map(|s| s.to_string().into_boxed_str()),
source: m.id_raw().clone(),
}
})
})
@ -253,7 +272,7 @@ impl Engine {
func: CallableFunction::from_method(
Box::new(f) as Box<FnAny>
),
source: None,
source: Identifier::new_const(),
}
})
} else {
@ -265,7 +284,7 @@ impl Engine {
func: CallableFunction::from_method(
Box::new(f) as Box<FnAny>
),
source: None,
source: Identifier::new_const(),
})
}
.map(Box::new)
@ -353,10 +372,15 @@ impl Engine {
}
// Run external function
let source = source
.as_ref()
.map(|s| &**s)
.or_else(|| parent_source.as_ref().map(|s| s.as_str()));
let source = if source.is_empty() {
if parent_source.is_empty() {
None
} else {
Some(parent_source.as_str())
}
} else {
Some(source.as_str())
};
let context = (self, name, source, &*global, lib, pos).into();
@ -400,7 +424,11 @@ impl Engine {
pos,
)
})?;
let source = global.source.as_ref().map(|s| s.as_str());
let source = if global.source.is_empty() {
None
} else {
Some(global.source.as_str())
};
(debug(&text, source, pos).into(), false)
} else {
(Dynamic::UNIT, false)
@ -572,7 +600,7 @@ impl Engine {
// Script-defined function call?
#[cfg(not(feature = "no_function"))]
if let Some(FnResolutionCacheEntry { func, source }) = self
if let Some(FnResolutionCacheEntry { func, mut source }) = self
.resolve_fn(
global,
state,
@ -603,13 +631,12 @@ impl Engine {
}
};
mem::swap(&mut global.source, &mut source);
let result = if _is_method_call {
// Method call of script function - map first argument to `this`
let (first_arg, rest_args) = args.split_first_mut().expect("not empty");
let orig_source = global.source.take();
global.source = source.map(Into::into);
let level = _level + 1;
let result = self.call_script_fn(
@ -625,9 +652,6 @@ impl Engine {
level,
);
// Restore the original source
global.source = orig_source;
result?
} else {
// Normal call of script function
@ -641,18 +665,12 @@ impl Engine {
.change_first_arg_to_copy(args);
}
let orig_source = global.source.take();
global.source = source.map(Into::into);
let level = _level + 1;
let result = self.call_script_fn(
scope, global, state, lib, &mut None, func, args, pos, true, level,
);
// Restore the original source
global.source = orig_source;
// Restore the original reference
if let Some(bk) = backup {
bk.restore_first_arg(args)
@ -661,6 +679,9 @@ impl Engine {
result?
};
// Restore the original source
mem::swap(&mut global.source, &mut source);
return Ok((result, false));
}
@ -1058,11 +1079,7 @@ impl Engine {
return result.map_err(|err| {
ERR::ErrorInFunctionCall(
KEYWORD_EVAL.to_string(),
global
.source
.as_ref()
.map(Identifier::to_string)
.unwrap_or_default(),
global.source.to_string(),
err,
pos,
)
@ -1265,7 +1282,7 @@ impl Engine {
} else {
let new_scope = &mut Scope::new();
let mut source = module.id_raw().cloned();
let mut source = module.id_raw().clone();
mem::swap(&mut global.source, &mut source);
let level = level + 1;

View File

@ -90,7 +90,7 @@ impl<'a, M: AsRef<[&'a Module]> + ?Sized, S: AsRef<str> + 'a + ?Sized>
Self {
engine: value.0,
fn_name: value.1.as_ref(),
source: value.2.map(|v| v.as_ref()),
source: value.2.map(S::as_ref),
global: Some(value.3),
lib: value.4.as_ref(),
pos: value.5,
@ -156,7 +156,7 @@ impl<'a> NativeCallContext<'a> {
Self {
engine,
fn_name: fn_name.as_ref(),
source: source.map(|v| v.as_ref()),
source: source.map(S::as_ref),
global: Some(global),
lib,
pos,

View File

@ -47,9 +47,8 @@ impl Engine {
fn_def
.lib
.as_ref()
.and_then(|m| m.id().map(|id| id.to_string()))
.or_else(|| global.source.as_ref().map(|s| s.to_string()))
.unwrap_or_default(),
.and_then(|m| m.id().map(str::to_string))
.unwrap_or_else(|| global.source.to_string()),
err,
pos,
)

View File

@ -247,7 +247,10 @@ pub use ast::{
pub use ast::FloatWrapper;
#[cfg(feature = "internals")]
pub use engine::{EvalState, FnResolutionCache, FnResolutionCacheEntry, GlobalRuntimeState};
pub use engine::{EvalState, GlobalRuntimeState};
#[cfg(feature = "internals")]
pub use func::call::{FnResolutionCache, FnResolutionCacheEntry};
#[cfg(feature = "internals")]
pub use module::Namespace;

View File

@ -131,7 +131,8 @@ pub fn calc_native_fn_hash(
#[derive(Clone)]
pub struct Module {
/// ID identifying the module.
id: Option<Identifier>,
/// No ID if string is empty.
id: Identifier,
/// Is this module internal?
pub(crate) internal: bool,
/// Is this module part of a standard library?
@ -170,8 +171,9 @@ impl fmt::Debug for Module {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("Module");
self.id.as_ref().map(|id| d.field("id", id));
if !self.id.is_empty() {
d.field("id", &self.id);
}
if !self.modules.is_empty() {
d.field(
"modules",
@ -241,7 +243,7 @@ impl Module {
#[must_use]
pub fn new() -> Self {
Self {
id: None,
id: Identifier::new_const(),
internal: false,
standard: false,
modules: BTreeMap::new(),
@ -270,18 +272,24 @@ impl Module {
#[inline]
#[must_use]
pub fn id(&self) -> Option<&str> {
self.id_raw().map(|s| s.as_str())
if self.id_raw().is_empty() {
None
} else {
Some(self.id_raw())
}
}
/// Get the ID of the [`Module`] as an [`Identifier`], if any.
#[inline(always)]
#[must_use]
pub(crate) const fn id_raw(&self) -> Option<&Identifier> {
self.id.as_ref()
pub(crate) const fn id_raw(&self) -> &Identifier {
&self.id
}
/// Set the ID of the [`Module`].
///
/// If the string is empty, it is equivalent to clearing the ID.
///
/// # Example
///
/// ```
@ -292,7 +300,7 @@ impl Module {
/// ```
#[inline(always)]
pub fn set_id(&mut self, id: impl Into<Identifier>) -> &mut Self {
self.id = Some(id.into());
self.id = id.into();
self
}
/// Clear the ID of the [`Module`].
@ -309,7 +317,7 @@ impl Module {
/// ```
#[inline(always)]
pub fn clear_id(&mut self) -> &mut Self {
self.id = None;
self.id.clear();
self
}
@ -1616,11 +1624,7 @@ impl Module {
});
}
if let Some(s) = ast.source_raw() {
module.set_id(s.clone());
} else {
module.clear_id();
}
module.set_id(ast.source_raw().clone());
module.build_index();

View File

@ -80,21 +80,21 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!(
format!("{:?}", ast),
"AST { source: None, body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }"
"AST { source: \"\", body: Block[Expr(123 @ 1:53)], functions: Module, resolver: None }"
);
let ast = engine.compile("const DECISION = false; if DECISION { 42 } else { 123 }")?;
assert_eq!(
format!("{:?}", ast),
r#"AST { source: None, body: Block[Var(false @ 1:18, "DECISION" @ 1:7, (Constant), 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
r#"AST { source: "", body: Block[Var(false @ 1:18, "DECISION" @ 1:7, (Constant), 1:1), Expr(123 @ 1:51)], functions: Module, resolver: None }"#
);
let ast = engine.compile("if 1 == 2 { 42 }")?;
assert_eq!(
format!("{:?}", ast),
"AST { source: None, body: Block[], functions: Module, resolver: None }"
"AST { source: \"\", body: Block[], functions: Module, resolver: None }"
);
engine.set_optimization_level(OptimizationLevel::Full);
@ -103,7 +103,7 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
assert_eq!(
format!("{:?}", ast),
"AST { source: None, body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }"
"AST { source: \"\", body: Block[Expr(42 @ 1:1)], functions: Module, resolver: None }"
);
Ok(())