Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline strings interner. #837

Merged
merged 3 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Bug fixes
---------

* The position of an undefined operation call now points to the operator instead of the first operand.
* The `optimize` command in `rhai-repl` now works properly and cycles through `None`->`Simple`->`Full`.

Deprecated API's
----------------
Expand Down
45 changes: 23 additions & 22 deletions src/api/compile.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
//! Module that defines the public compilation API of [`Engine`].

use crate::func::native::locked_write;
use crate::parser::{ParseResult, ParseState};
use crate::{Engine, OptimizationLevel, Scope, AST};
use crate::{Engine, Scope, AST};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;

Expand Down Expand Up @@ -201,37 +200,40 @@ impl Engine {
scope: &Scope,
scripts: impl AsRef<[S]>,
) -> ParseResult<AST> {
self.compile_scripts_with_scope_raw(Some(scope), scripts, self.optimization_level)
self.compile_scripts_with_scope_raw(
Some(scope),
scripts,
#[cfg(not(feature = "no_optimize"))]
self.optimization_level,
)
}
/// Join a list of strings and compile into an [`AST`] using own scope at a specific optimization level.
///
/// ## Constants Propagation
///
/// If not [`OptimizationLevel::None`], constants defined within the scope are propagated
/// If not [`OptimizationLevel::None`][`crate::OptimizationLevel::None`], constants defined within the scope are propagated
/// throughout the script _including_ functions. This allows functions to be optimized based on
/// dynamic global constants.
#[inline]
pub(crate) fn compile_scripts_with_scope_raw<S: AsRef<str>>(
&self,
scope: Option<&Scope>,
scripts: impl AsRef<[S]>,
optimization_level: OptimizationLevel,
#[cfg(not(feature = "no_optimize"))] optimization_level: crate::OptimizationLevel,
) -> ParseResult<AST> {
let (stream, tc) = self.lex(scripts.as_ref());

let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let state = &mut ParseState::new(scope, interned_strings, input, tc, lib);
let mut _ast = self.parse(state, optimization_level)?;
let state = ParseState::new(scope, input, tc.clone(), lib);
let mut _ast = self.parse(
state,
#[cfg(not(feature = "no_optimize"))]
optimization_level,
)?;
#[cfg(feature = "metadata")]
{
let global_comments = &state.tokenizer_control.borrow().global_comments;
let global_comments = &tc.borrow().global_comments;
_ast.doc = global_comments.into();
}
Ok(_ast)
Expand Down Expand Up @@ -299,16 +301,15 @@ impl Engine {
let scripts = [script];
let (stream, t) = self.lex(&scripts);

let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let state = &mut ParseState::new(Some(scope), interned_strings, input, t, lib);
let state = ParseState::new(Some(scope), input, t, lib);

self.parse_global_expr(state, |_| {}, self.optimization_level)
self.parse_global_expr(
state,
|_| {},
#[cfg(not(feature = "no_optimize"))]
self.optimization_level,
)
}
}
19 changes: 7 additions & 12 deletions src/api/eval.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Module that defines the public evaluation API of [`Engine`].

use crate::eval::{Caches, GlobalRuntimeState};
use crate::func::native::locked_write;
use crate::parser::ParseState;
use crate::types::dynamic::Variant;
use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, AST, ERR};
Expand Down Expand Up @@ -66,8 +65,12 @@ impl Engine {
scope: &mut Scope,
script: &str,
) -> RhaiResultOf<T> {
let ast =
self.compile_scripts_with_scope_raw(Some(scope), [script], self.optimization_level)?;
let ast = self.compile_scripts_with_scope_raw(
Some(scope),
[script],
#[cfg(not(feature = "no_optimize"))]
self.optimization_level,
)?;
self.eval_ast_with_scope(scope, &ast)
}
/// Evaluate a string containing an expression, returning the result value or an error.
Expand Down Expand Up @@ -114,26 +117,18 @@ impl Engine {
) -> RhaiResultOf<T> {
let scripts = [script];
let ast = {
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let (stream, tc) = self.lex(&scripts);

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let state = &mut ParseState::new(Some(scope), interned_strings, input, tc, lib);
let state = ParseState::new(Some(scope), input, tc, lib);

// No need to optimize a lone expression
self.parse_global_expr(
state,
|_| {},
#[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
<_>::default(),
)?
};

Expand Down
18 changes: 4 additions & 14 deletions src/api/formatting.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
//! Module that provide formatting services to the [`Engine`].
use crate::func::locked_write;
use crate::packages::iter_basic::{BitRange, CharsStream, StepRange};
use crate::parser::{ParseResult, ParseState};
use crate::{
Expand Down Expand Up @@ -265,26 +264,17 @@ impl Engine {
tc.borrow_mut().compressed = Some(String::new());
stream.state.last_token = Some(SmartString::new_const());

let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let mut state = ParseState::new(None, interned_strings, input, tc, lib);
let state = ParseState::new(None, input, tc.clone(), lib);

let mut _ast = self.parse(
&mut state,
state,
#[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
(),
)?;

let tc = state.tokenizer_control.borrow();

Ok(tc.compressed.as_ref().unwrap().into())
let guard = tc.borrow();
Ok(guard.compressed.as_ref().unwrap().into())
}
}
11 changes: 1 addition & 10 deletions src/api/json.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Module that defines JSON manipulation functions for [`Engine`].
#![cfg(not(feature = "no_object"))]

use crate::func::native::locked_write;
use crate::parser::{ParseSettingFlags, ParseState};
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
Expand Down Expand Up @@ -117,23 +116,15 @@ impl Engine {
);

let ast = {
let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let state = &mut ParseState::new(None, interned_strings, input, tokenizer_control, lib);
let state = ParseState::new(None, input, tokenizer_control, lib);

self.parse_global_expr(
state,
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
#[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
<_>::default(),
)?
};

Expand Down
15 changes: 6 additions & 9 deletions src/api/run.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! Module that defines the public evaluation API of [`Engine`].

use crate::eval::Caches;
use crate::func::native::locked_write;
use crate::parser::ParseState;
use crate::{Engine, RhaiResultOf, Scope, AST};
#[cfg(feature = "no_std")]
Expand Down Expand Up @@ -60,17 +59,15 @@ impl Engine {
let ast = {
let (stream, tc) = self.lex(&scripts);

let guard = &mut self
.interned_strings
.as_ref()
.and_then(|interner| locked_write(interner));
let interned_strings = guard.as_deref_mut();

let input = &mut stream.peekable();
let lib = &mut <_>::default();
let state = &mut ParseState::new(Some(scope), interned_strings, input, tc, lib);
let state = ParseState::new(Some(scope), input, tc, lib);

self.parse(state, self.optimization_level)?
self.parse(
state,
#[cfg(not(feature = "no_optimize"))]
self.optimization_level,
)?
};
self.run_ast_with_scope(scope, &ast)
}
Expand Down
5 changes: 3 additions & 2 deletions src/ast/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ pub use stmt::{
SwitchCasesCollection,
};

/// _(internals)_ Placeholder for a script-defined function.
/// _(internals)_ Empty placeholder for a script-defined function.
/// Exported under the `internals` feature only.
#[cfg(feature = "no_function")]
pub type ScriptFuncDef = ();
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default)]
pub struct ScriptFuncDef;
22 changes: 14 additions & 8 deletions src/bin/rhai-repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,15 +438,21 @@ fn main() {
continue;
}
#[cfg(not(feature = "no_optimize"))]
"optimize" if optimize_level == rhai::OptimizationLevel::Simple => {
optimize_level = rhai::OptimizationLevel::None;
println!("Script optimization turned OFF.");
continue;
}
#[cfg(not(feature = "no_optimize"))]
"optimize" => {
optimize_level = rhai::OptimizationLevel::Simple;
println!("Script optimization turned ON.");
match optimize_level {
rhai::OptimizationLevel::Full => {
optimize_level = rhai::OptimizationLevel::None;
println!("Script optimization turned OFF.");
}
rhai::OptimizationLevel::Simple => {
optimize_level = rhai::OptimizationLevel::Full;
println!("Script optimization turned to FULL.");
}
_ => {
optimize_level = rhai::OptimizationLevel::Simple;
println!("Script optimization turned to SIMPLE.");
}
}
continue;
}
"scope" => {
Expand Down
44 changes: 39 additions & 5 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use crate::func::native::{
use crate::packages::{Package, StandardPackage};
use crate::tokenizer::Token;
use crate::types::StringsInterner;
use crate::{Dynamic, Identifier, ImmutableString, Locked, OptimizationLevel, SharedModule};
use crate::{Dynamic, Identifier, ImmutableString, Locked, SharedModule};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{collections::BTreeSet, fmt, num::NonZeroU8};
Expand Down Expand Up @@ -131,7 +131,8 @@ pub struct Engine {
pub(crate) def_tag: Dynamic,

/// Script optimization level.
pub(crate) optimization_level: OptimizationLevel,
#[cfg(not(feature = "no_optimize"))]
pub(crate) optimization_level: crate::OptimizationLevel,

/// Max limits.
#[cfg(not(feature = "unchecked"))]
Expand Down Expand Up @@ -254,9 +255,7 @@ impl Engine {
def_tag: Dynamic::UNIT,

#[cfg(not(feature = "no_optimize"))]
optimization_level: OptimizationLevel::Simple,
#[cfg(feature = "no_optimize")]
optimization_level: (),
optimization_level: crate::OptimizationLevel::Simple,

#[cfg(not(feature = "unchecked"))]
limits: crate::api::limits::Limits::new(),
Expand Down Expand Up @@ -333,6 +332,41 @@ impl Engine {
None => string.into(),
}
}
/// Get an interned property getter, creating one if it is not yet interned.
#[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(),
}
}

/// Get an interned property setter, creating one if it is not yet interned.
#[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(),
}
}

/// Get an empty [`ImmutableString`] which refers to a shared instance.
#[inline(always)]
Expand Down
2 changes: 0 additions & 2 deletions src/func/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1621,8 +1621,6 @@ impl Engine {
[script],
#[cfg(not(feature = "no_optimize"))]
crate::OptimizationLevel::None,
#[cfg(feature = "no_optimize")]
<_>::default(),
)?;

// If new functions are defined within the eval string, it is an error
Expand Down
4 changes: 0 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -323,10 +323,6 @@ pub use module::resolvers as module_resolvers;
#[cfg(not(feature = "no_optimize"))]
pub use optimizer::OptimizationLevel;

/// Placeholder for the optimization level.
#[cfg(feature = "no_optimize")]
type OptimizationLevel = ();

// Expose internal data structures.

#[cfg(feature = "internals")]
Expand Down
Loading
Loading