Merge pull request #385 from schungx/master
Fix no_index+no_object build.
This commit is contained in:
commit
fb633f67e2
5
.github/workflows/build.yml
vendored
5
.github/workflows/build.yml
vendored
@ -35,6 +35,7 @@ jobs:
|
|||||||
- "--features no_closure"
|
- "--features no_closure"
|
||||||
- "--features unicode-xid-ident"
|
- "--features unicode-xid-ident"
|
||||||
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked"
|
- "--features sync,no_function,no_float,no_optimize,no_module,no_closure,metadata,serde,unchecked"
|
||||||
|
- "--features no_function,no_float,no_index,no_object,no_optimize,no_module,no_closure,unchecked"
|
||||||
toolchain: [stable]
|
toolchain: [stable]
|
||||||
experimental: [false]
|
experimental: [false]
|
||||||
include:
|
include:
|
||||||
@ -111,8 +112,8 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- {toolchain: nightly, os: ubuntu-latest, experimental: false, flags: ""}
|
- {toolchain: nightly, os: ubuntu-latest, experimental: false, flags: "--features metadata"}
|
||||||
- {toolchain: nightly, os: windows-latest, experimental: false, flags: ""}
|
- {toolchain: nightly, os: windows-latest, experimental: false, flags: "--features metadata"}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
@ -28,6 +28,11 @@ This avoids spending precious resources maintaining metadata for functions for t
|
|||||||
use cases where such information is not required.
|
use cases where such information is not required.
|
||||||
|
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* The feature flags `no_index + no_object` now compile without errors.
|
||||||
|
|
||||||
Breaking changes
|
Breaking changes
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
@ -42,6 +47,7 @@ Breaking changes
|
|||||||
* Shebangs at the very beginning of script files are skipped when loading them.
|
* Shebangs at the very beginning of script files are skipped when loading them.
|
||||||
* [`smartstring`](https://crates.io/crates/smartstring) is used for identifiers by default. Currently, a PR branch is pulled because it breaks on `no-std` builds. The official crate will be used once `smartstring` is fixed to support `no-std`.
|
* [`smartstring`](https://crates.io/crates/smartstring) is used for identifiers by default. Currently, a PR branch is pulled because it breaks on `no-std` builds. The official crate will be used once `smartstring` is fixed to support `no-std`.
|
||||||
* `Map` is now an alias to `BTreeMap<SmartString, Dynamic>` instead of `HashMap` because most object maps hold few properties.
|
* `Map` is now an alias to `BTreeMap<SmartString, Dynamic>` instead of `HashMap` because most object maps hold few properties.
|
||||||
|
* `EvalAltResult::FnWrongDefinition` is renamed `WrongFnDefinition` for consistency.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -55,6 +61,7 @@ Enhancements
|
|||||||
* Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny.
|
* Replaced all `HashMap` usage with `BTreeMap` for better performance because collections in Rhai are tiny.
|
||||||
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type.
|
* `Engine::register_result_fn` no longer requires the successful return type to be `Dynamic`. It can now be any clonable type.
|
||||||
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
* `#[rhai_fn(return_raw)]` can now return `Result<T, Box<EvalAltResult>>` where `T` is any clonable type instead of `Result<Dynamic, Box<EvalAltResult>>`.
|
||||||
|
* `Dynamic::clone_cast` is added to simplify casting from a `&Dynamic`.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.14
|
Version 0.19.14
|
||||||
|
12
Cargo.toml
12
Cargo.toml
@ -19,12 +19,11 @@ categories = ["no-std", "embedded", "wasm", "parser-implementations"]
|
|||||||
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
smallvec = { version = "1.6", default-features = false, features = ["union"] }
|
||||||
ahash = { version = "0.7", default-features = false }
|
ahash = { version = "0.7", default-features = false }
|
||||||
num-traits = { version = "0.2", default_features = false }
|
num-traits = { version = "0.2", default_features = false }
|
||||||
#smartstring = { version = "0.2.6" }
|
smartstring = { version = "0.2.6", default_features = false }
|
||||||
smartstring = { git = "https://github.com/okready/smartstring", branch = "fix-no_std-builds", default_features = false }
|
rhai_codegen = { version = "0.3.4", path = "codegen", default_features = false }
|
||||||
rhai_codegen = { version = "0.3.4", path = "codegen", features = ["metadata"] }
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = []
|
default = ["smartstring/std", "ahash/std", "num-traits/std"] # remove 'smartstring/std' when smartstring is updated to support no-std
|
||||||
unchecked = [] # unchecked arithmetic
|
unchecked = [] # unchecked arithmetic
|
||||||
sync = [] # restrict to only types that implement Send + Sync
|
sync = [] # restrict to only types that implement Send + Sync
|
||||||
no_optimize = [] # no script optimizer
|
no_optimize = [] # no script optimizer
|
||||||
@ -40,10 +39,9 @@ no_closure = [] # no automatic sharing and capture of anonymous
|
|||||||
no_module = [] # no modules
|
no_module = [] # no modules
|
||||||
internals = [] # expose internal data structures
|
internals = [] # expose internal data structures
|
||||||
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
unicode-xid-ident = ["unicode-xid"] # allow Unicode Standard Annex #31 for identifiers.
|
||||||
metadata = ["serde_json"] # enable exporting functions metadata
|
metadata = ["serde_json", "rhai_codegen/metadata"] # enable exporting functions metadata
|
||||||
|
|
||||||
no_smartstring = [] # set Identifier=ImmutableString
|
no_std = ["num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
||||||
no_std = ["smallvec/union", "num-traits/libm", "core-error", "libm", "ahash/compile-time-rng"]
|
|
||||||
|
|
||||||
# compiling for WASM
|
# compiling for WASM
|
||||||
wasm-bindgen = ["instant/wasm-bindgen"]
|
wasm-bindgen = ["instant/wasm-bindgen"]
|
||||||
|
@ -12,7 +12,7 @@ homepage = "https://github.com/rhaiscript/rhai/tree/no_std/no_std_test"
|
|||||||
repository = "https://github.com/rhaiscript/rhai"
|
repository = "https://github.com/rhaiscript/rhai"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rhai = { path = "../../", features = [ "no_std" ] }
|
rhai = { path = "../../", features = [ "no_std" ], default_features = false }
|
||||||
wee_alloc = { version = "0.4.5", default_features = false }
|
wee_alloc = { version = "0.4.5", default_features = false }
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
@ -36,3 +36,7 @@ inherits = "release"
|
|||||||
[profile.macos]
|
[profile.macos]
|
||||||
inherits = "release"
|
inherits = "release"
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
|
||||||
|
[patch.crates-io]
|
||||||
|
# Patch smartstring wth a PR fix because it doesn't properly handle no-std builds.
|
||||||
|
smartstring = { git = "https://github.com/okready/smartstring", branch = "fix-no_std-builds" }
|
||||||
|
@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
print("hello");
|
print("hello");
|
||||||
print("this\nis \\ nice"); // escape sequences
|
print("this\nis \\ nice"); // escape sequences
|
||||||
print("40 hex is \x40"); // hex escape sequence
|
print("0x40 hex is \x40"); // hex escape sequence
|
||||||
print("unicode fun: \u2764"); // Unicode escape sequence
|
print("Unicode fun: \u2764"); // Unicode escape sequence
|
||||||
print("more fun: \U0001F603"); // Unicode escape sequence
|
print("more fun: \U0001F603"); // Unicode escape sequence
|
||||||
print("foo" + " " + "bar"); // string building using strings
|
print("foo" + " " + "bar"); // string building using strings
|
||||||
print("foo" < "bar"); // string comparison
|
print("foo" < "bar"); // string comparison
|
||||||
@ -15,3 +15,22 @@ print("length=" + s.len); // should be 17
|
|||||||
|
|
||||||
s[s.len-3] = '?'; // change the string
|
s[s.len-3] = '?'; // change the string
|
||||||
print("Question: " + s); // should print 'Question: hello, world?'
|
print("Question: " + s); // should print 'Question: hello, world?'
|
||||||
|
|
||||||
|
// Line continuation:
|
||||||
|
let s = "This is a long \
|
||||||
|
string constructed using \
|
||||||
|
line continuation";
|
||||||
|
|
||||||
|
print("One string: " + s);
|
||||||
|
|
||||||
|
// Multi-line literal string:
|
||||||
|
let s = `
|
||||||
|
\U0001F603 This is a multi-line
|
||||||
|
"string" with \t\x20\r\n
|
||||||
|
made using multi-line literal
|
||||||
|
string syntax.
|
||||||
|
`;
|
||||||
|
|
||||||
|
print(s);
|
||||||
|
|
||||||
|
print(">>> END <<<");
|
||||||
|
18
src/ast.rs
18
src/ast.rs
@ -864,19 +864,23 @@ pub enum Stmt {
|
|||||||
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
/// `if` expr `{` stmt `}` `else` `{` stmt `}`
|
||||||
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
|
If(Expr, Box<(StmtBlock, StmtBlock)>, Position),
|
||||||
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
/// `switch` expr `{` literal or _ `=>` stmt `,` ... `}`
|
||||||
Switch(Expr, Box<(BTreeMap<u64, StmtBlock>, StmtBlock)>, Position),
|
Switch(
|
||||||
|
Expr,
|
||||||
|
Box<(BTreeMap<u64, Box<StmtBlock>>, StmtBlock)>,
|
||||||
|
Position,
|
||||||
|
),
|
||||||
/// `while` expr `{` stmt `}`
|
/// `while` expr `{` stmt `}`
|
||||||
While(Expr, Box<StmtBlock>, Position),
|
While(Expr, Box<StmtBlock>, Position),
|
||||||
/// `do` `{` stmt `}` `while`|`until` expr
|
/// `do` `{` stmt `}` `while`|`until` expr
|
||||||
Do(Box<StmtBlock>, Expr, bool, Position),
|
Do(Box<StmtBlock>, Expr, bool, Position),
|
||||||
/// `for` id `in` expr `{` stmt `}`
|
/// `for` id `in` expr `{` stmt `}`
|
||||||
For(Expr, Box<(Identifier, StmtBlock)>, Position),
|
For(Expr, Box<(Ident, StmtBlock)>, Position),
|
||||||
/// \[`export`\] `let` id `=` expr
|
/// \[`export`\] `let` id `=` expr
|
||||||
Let(Expr, Box<Ident>, bool, Position),
|
Let(Expr, Box<Ident>, bool, Position),
|
||||||
/// \[`export`\] `const` id `=` expr
|
/// \[`export`\] `const` id `=` expr
|
||||||
Const(Expr, Box<Ident>, bool, Position),
|
Const(Expr, Box<Ident>, bool, Position),
|
||||||
/// expr op`=` expr
|
/// expr op`=` expr
|
||||||
Assignment(Box<(Expr, Expr, Option<OpAssignment>)>, Position),
|
Assignment(Box<(Expr, Option<OpAssignment>, Expr)>, Position),
|
||||||
/// `{` stmt`;` ... `}`
|
/// `{` stmt`;` ... `}`
|
||||||
Block(Vec<Stmt>, Position),
|
Block(Vec<Stmt>, Position),
|
||||||
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
/// `try` `{` stmt; ... `}` `catch` `(` var `)` `{` stmt; ... `}`
|
||||||
@ -901,7 +905,7 @@ pub enum Stmt {
|
|||||||
Export(Vec<(Ident, Option<Ident>)>, Position),
|
Export(Vec<(Ident, Option<Ident>)>, Position),
|
||||||
/// Convert a variable to shared.
|
/// Convert a variable to shared.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Share(Box<Ident>),
|
Share(Identifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Stmt {
|
impl Default for Stmt {
|
||||||
@ -967,7 +971,7 @@ impl Stmt {
|
|||||||
Self::Export(_, pos) => *pos,
|
Self::Export(_, pos) => *pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(x) => x.pos,
|
Self::Share(_) => Position::NONE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Override the [position][Position] of this statement.
|
/// Override the [position][Position] of this statement.
|
||||||
@ -998,7 +1002,7 @@ impl Stmt {
|
|||||||
Self::Export(_, pos) => *pos = new_pos,
|
Self::Export(_, pos) => *pos = new_pos,
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Self::Share(x) => x.pos = new_pos,
|
Self::Share(_) => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
self
|
self
|
||||||
@ -1203,7 +1207,7 @@ impl Stmt {
|
|||||||
if !x.0.walk(path, on_node) {
|
if !x.0.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !x.1.walk(path, on_node) {
|
if !x.2.walk(path, on_node) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1131,6 +1131,37 @@ impl Dynamic {
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
/// Clone the [`Dynamic`] value and convert it into a specific type.
|
||||||
|
///
|
||||||
|
/// Casting to a [`Dynamic`] just returns as is, but if it contains a shared value,
|
||||||
|
/// it is cloned into a [`Dynamic`] with a normal value.
|
||||||
|
///
|
||||||
|
/// Returns [`None`] if types mismatched.
|
||||||
|
///
|
||||||
|
/// # Panics or Deadlocks
|
||||||
|
///
|
||||||
|
/// Panics if the cast fails (e.g. the type of the actual value is not the
|
||||||
|
/// same as the specified type).
|
||||||
|
///
|
||||||
|
/// Under the `sync` feature, this call may deadlock, or [panic](https://doc.rust-lang.org/std/sync/struct.RwLock.html#panics-1).
|
||||||
|
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||||
|
///
|
||||||
|
/// These normally shouldn't occur since most operations in Rhai is single-threaded.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// use rhai::Dynamic;
|
||||||
|
///
|
||||||
|
/// let x = Dynamic::from(42_u32);
|
||||||
|
/// let y = &x;
|
||||||
|
///
|
||||||
|
/// assert_eq!(y.clone_cast::<u32>(), 42);
|
||||||
|
/// ```
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn clone_cast<T: Variant + Clone>(&self) -> T {
|
||||||
|
self.read_lock::<T>().unwrap().clone()
|
||||||
|
}
|
||||||
/// Flatten the [`Dynamic`] and clone it.
|
/// Flatten the [`Dynamic`] and clone it.
|
||||||
///
|
///
|
||||||
/// If the [`Dynamic`] is not a shared value, it returns a cloned copy.
|
/// If the [`Dynamic`] is not a shared value, it returns a cloned copy.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Main module defining the script evaluation [`Engine`].
|
//! Main module defining the script evaluation [`Engine`].
|
||||||
|
|
||||||
use crate::ast::{Expr, FnCallExpr, FnCallHash, Ident, OpAssignment, ReturnType, Stmt, StmtBlock};
|
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt, StmtBlock};
|
||||||
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant};
|
||||||
use crate::fn_native::{
|
use crate::fn_native::{
|
||||||
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
||||||
@ -25,8 +25,8 @@ use crate::stdlib::{
|
|||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
use crate::utils::get_hasher;
|
use crate::utils::get_hasher;
|
||||||
use crate::{
|
use crate::{
|
||||||
Dynamic, EvalAltResult, FnPtr, Identifier, ImmutableString, Module, Position, RhaiResult,
|
Dynamic, EvalAltResult, FnPtr, Identifier, Module, Position, RhaiResult, Scope, Shared,
|
||||||
Scope, Shared, StaticVec,
|
StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -35,6 +35,9 @@ use crate::{calc_fn_hash, stdlib::iter::empty, Array};
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
|
use crate::ast::FnCallHash;
|
||||||
|
|
||||||
pub type Precedence = NonZeroU8;
|
pub type Precedence = NonZeroU8;
|
||||||
|
|
||||||
/// _(INTERNALS)_ A stack of imported [modules][Module].
|
/// _(INTERNALS)_ A stack of imported [modules][Module].
|
||||||
@ -394,7 +397,6 @@ impl<'a> Target<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Update the value of the `Target`.
|
/// Update the value of the `Target`.
|
||||||
#[cfg(any(not(feature = "no_object"), not(feature = "no_index")))]
|
|
||||||
pub fn set_value(
|
pub fn set_value(
|
||||||
&mut self,
|
&mut self,
|
||||||
new_val: Dynamic,
|
new_val: Dynamic,
|
||||||
@ -408,7 +410,7 @@ impl<'a> Target<'a> {
|
|||||||
Self::Value(_) => panic!("cannot update a value"),
|
Self::Value(_) => panic!("cannot update a value"),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Self::StringChar(s, index, _) => {
|
Self::StringChar(s, index, _) => {
|
||||||
let s = &mut *s.write_lock::<ImmutableString>().unwrap();
|
let s = &mut *s.write_lock::<crate::ImmutableString>().unwrap();
|
||||||
|
|
||||||
// Replace the character at the specified index position
|
// Replace the character at the specified index position
|
||||||
let new_ch = new_val.as_char().map_err(|err| {
|
let new_ch = new_val.as_char().map_err(|err| {
|
||||||
@ -589,7 +591,7 @@ pub struct Limits {
|
|||||||
/// Not available under `no_module`.
|
/// Not available under `no_module`.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub max_modules: usize,
|
pub max_modules: usize,
|
||||||
/// Maximum length of a [string][ImmutableString].
|
/// Maximum length of a [string][crate::ImmutableString].
|
||||||
pub max_string_size: Option<NonZeroUsize>,
|
pub max_string_size: Option<NonZeroUsize>,
|
||||||
/// Maximum length of an [array][Array].
|
/// Maximum length of an [array][Array].
|
||||||
///
|
///
|
||||||
@ -1569,8 +1571,8 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(map, _)) => {
|
Dynamic(Union::Map(map, _)) => {
|
||||||
// val_map[idx]
|
// val_map[idx]
|
||||||
let index = &*idx.read_lock::<ImmutableString>().ok_or_else(|| {
|
let index = &*idx.read_lock::<crate::ImmutableString>().ok_or_else(|| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(idx.type_name(), idx_pos)
|
self.make_type_mismatch_err::<crate::ImmutableString>(idx.type_name(), idx_pos)
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
if _create && !map.contains_key(index.as_str()) {
|
if _create && !map.contains_key(index.as_str()) {
|
||||||
@ -1957,7 +1959,7 @@ impl Engine {
|
|||||||
|
|
||||||
// var op= rhs
|
// var op= rhs
|
||||||
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => {
|
Stmt::Assignment(x, op_pos) if x.0.get_variable_access(false).is_some() => {
|
||||||
let (lhs_expr, rhs_expr, op_info) = x.as_ref();
|
let (lhs_expr, op_info, rhs_expr) = x.as_ref();
|
||||||
let rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -1998,7 +2000,7 @@ impl Engine {
|
|||||||
|
|
||||||
// lhs op= rhs
|
// lhs op= rhs
|
||||||
Stmt::Assignment(x, op_pos) => {
|
Stmt::Assignment(x, op_pos) => {
|
||||||
let (lhs_expr, rhs_expr, op_info) = x.as_ref();
|
let (lhs_expr, op_info, rhs_expr) = x.as_ref();
|
||||||
let rhs_val = self
|
let rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2083,7 +2085,9 @@ impl Engine {
|
|||||||
value.hash(hasher);
|
value.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
table.get(&hash).map(|StmtBlock { statements, .. }| {
|
table.get(&hash).map(|t| {
|
||||||
|
let statements = &t.statements;
|
||||||
|
|
||||||
if !statements.is_empty() {
|
if !statements.is_empty() {
|
||||||
self.eval_stmt_block(
|
self.eval_stmt_block(
|
||||||
scope, mods, state, lib, this_ptr, statements, true, level,
|
scope, mods, state, lib, this_ptr, statements, true, level,
|
||||||
@ -2178,7 +2182,7 @@ impl Engine {
|
|||||||
|
|
||||||
// For loop
|
// For loop
|
||||||
Stmt::For(expr, x, _) => {
|
Stmt::For(expr, x, _) => {
|
||||||
let (name, StmtBlock { statements, pos }) = x.as_ref();
|
let (Ident { name, .. }, StmtBlock { statements, pos }) = x.as_ref();
|
||||||
let iter_obj = self
|
let iter_obj = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -2425,7 +2429,7 @@ impl Engine {
|
|||||||
|
|
||||||
if let Some(path) = self
|
if let Some(path) = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, &expr, level)?
|
||||||
.try_cast::<ImmutableString>()
|
.try_cast::<crate::ImmutableString>()
|
||||||
{
|
{
|
||||||
use crate::ModuleResolver;
|
use crate::ModuleResolver;
|
||||||
|
|
||||||
@ -2458,7 +2462,7 @@ impl Engine {
|
|||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
} else {
|
} else {
|
||||||
Err(self.make_type_mismatch_err::<ImmutableString>("", expr.position()))
|
Err(self.make_type_mismatch_err::<crate::ImmutableString>("", expr.position()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2479,8 +2483,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(x) => {
|
Stmt::Share(name) => {
|
||||||
if let Some((index, _)) = scope.get_index(&x.name) {
|
if let Some((index, _)) = scope.get_index(name) {
|
||||||
let val = scope.get_mut_by_index(index);
|
let val = scope.get_mut_by_index(index);
|
||||||
|
|
||||||
if !val.is_shared() {
|
if !val.is_shared() {
|
||||||
@ -2513,17 +2517,17 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If no data size limits, just return
|
// If no data size limits, just return
|
||||||
let mut has_limit = self.limits.max_string_size.is_some();
|
let mut _has_limit = self.limits.max_string_size.is_some();
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
has_limit = has_limit || self.limits.max_array_size.is_some();
|
_has_limit = _has_limit || self.limits.max_array_size.is_some();
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
{
|
{
|
||||||
has_limit = has_limit || self.limits.max_map_size.is_some();
|
_has_limit = _has_limit || self.limits.max_map_size.is_some();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !has_limit {
|
if !_has_limit {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -866,7 +866,7 @@ impl Engine {
|
|||||||
|
|
||||||
// If new functions are defined within the eval string, it is an error
|
// If new functions are defined within the eval string, it is an error
|
||||||
if ast.lib().count().0 != 0 {
|
if ast.lib().count().0 != 0 {
|
||||||
return Err(ParseErrorType::FnWrongDefinition.into());
|
return Err(ParseErrorType::WrongFnDefinition.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the AST
|
// Evaluate the AST
|
||||||
|
@ -13,8 +13,8 @@ use crate::stdlib::{
|
|||||||
};
|
};
|
||||||
use crate::token::is_valid_identifier;
|
use crate::token::is_valid_identifier;
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, Identifier, ImmutableString, Module,
|
calc_fn_hash, Dynamic, Engine, EvalAltResult, EvalContext, ImmutableString, Module, Position,
|
||||||
Position, RhaiResult, StaticVec,
|
RhaiResult, StaticVec,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
/// Trait that maps to `Send + Sync` only under the `sync` feature.
|
||||||
@ -144,7 +144,9 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn iter_imports_raw(&self) -> impl Iterator<Item = (&Identifier, &Shared<Module>)> {
|
pub(crate) fn iter_imports_raw(
|
||||||
|
&self,
|
||||||
|
) -> impl Iterator<Item = (&crate::Identifier, &Shared<Module>)> {
|
||||||
self.mods.iter().flat_map(|&m| m.iter_raw())
|
self.mods.iter().flat_map(|&m| m.iter_raw())
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
/// _(INTERNALS)_ The current set of modules imported via `import` statements.
|
||||||
|
@ -768,7 +768,7 @@ impl Module {
|
|||||||
/// // Get the second parameter by 'consuming' it
|
/// // Get the second parameter by 'consuming' it
|
||||||
/// let double = std::mem::take(args[1]).cast::<bool>();
|
/// let double = std::mem::take(args[1]).cast::<bool>();
|
||||||
/// // Since it is a primary type, it can also be cheaply copied
|
/// // Since it is a primary type, it can also be cheaply copied
|
||||||
/// let double = args[1].clone().cast::<bool>();
|
/// let double = args[1].clone_cast::<bool>();
|
||||||
/// // Get a mutable reference to the first argument.
|
/// // Get a mutable reference to the first argument.
|
||||||
/// let mut x = args[0].write_lock::<i64>().unwrap();
|
/// let mut x = args[0].write_lock::<i64>().unwrap();
|
||||||
///
|
///
|
||||||
|
@ -385,10 +385,10 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
|
|||||||
match stmt {
|
match stmt {
|
||||||
// expr op= expr
|
// expr op= expr
|
||||||
Stmt::Assignment(x, _) => match x.0 {
|
Stmt::Assignment(x, _) => match x.0 {
|
||||||
Expr::Variable(_) => optimize_expr(&mut x.1, state),
|
Expr::Variable(_) => optimize_expr(&mut x.2, state),
|
||||||
_ => {
|
_ => {
|
||||||
optimize_expr(&mut x.0, state);
|
optimize_expr(&mut x.0, state);
|
||||||
optimize_expr(&mut x.1, state);
|
optimize_expr(&mut x.2, state);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use crate::plugin::*;
|
use crate::plugin::*;
|
||||||
use crate::stdlib::{format, string::ToString};
|
use crate::stdlib::{format, string::ToString};
|
||||||
use crate::{def_package, FnPtr, ImmutableString};
|
use crate::{def_package, FnPtr};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
@ -10,6 +10,7 @@ use crate::Array;
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
const FUNC_TO_DEBUG: &'static str = "to_debug";
|
||||||
|
|
||||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||||
@ -20,9 +21,15 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
|
|||||||
|
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn print_with_func(fn_name: &str, ctx: &NativeCallContext, value: &mut Dynamic) -> ImmutableString {
|
fn print_with_func(
|
||||||
|
fn_name: &str,
|
||||||
|
ctx: &NativeCallContext,
|
||||||
|
value: &mut Dynamic,
|
||||||
|
) -> crate::ImmutableString {
|
||||||
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
match ctx.call_fn_dynamic_raw(fn_name, true, &mut [value]) {
|
||||||
Ok(result) if result.is::<ImmutableString>() => result.take_immutable_string().unwrap(),
|
Ok(result) if result.is::<crate::ImmutableString>() => {
|
||||||
|
result.take_immutable_string().unwrap()
|
||||||
|
}
|
||||||
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
Ok(result) => ctx.engine().map_type_name(result.type_name()).into(),
|
||||||
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
Err(_) => ctx.engine().map_type_name(value.type_name()).into(),
|
||||||
}
|
}
|
||||||
|
@ -132,7 +132,7 @@ pub enum ParseErrorType {
|
|||||||
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
||||||
///
|
///
|
||||||
/// Never appears under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
FnWrongDefinition,
|
WrongFnDefinition,
|
||||||
/// Defining a function with a name that conflicts with an existing function.
|
/// Defining a function with a name that conflicts with an existing function.
|
||||||
/// Wrapped values are the function name and number of parameters.
|
/// Wrapped values are the function name and number of parameters.
|
||||||
///
|
///
|
||||||
@ -199,7 +199,7 @@ impl ParseErrorType {
|
|||||||
Self::VariableExpected => "Expecting name of a variable",
|
Self::VariableExpected => "Expecting name of a variable",
|
||||||
Self::Reserved(_) => "Invalid use of reserved keyword",
|
Self::Reserved(_) => "Invalid use of reserved keyword",
|
||||||
Self::ExprExpected(_) => "Expecting an expression",
|
Self::ExprExpected(_) => "Expecting an expression",
|
||||||
Self::FnWrongDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||||
Self::FnDuplicatedDefinition(_, _) => "Function already exists",
|
Self::FnDuplicatedDefinition(_, _) => "Function already exists",
|
||||||
Self::FnMissingName => "Expecting function name in function declaration",
|
Self::FnMissingName => "Expecting function name in function declaration",
|
||||||
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||||
|
@ -715,13 +715,13 @@ fn parse_map_literal(
|
|||||||
return Err(PERR::Reserved(s).into_err(pos));
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
(_, pos) if map.is_empty() => {
|
(Token::EOF, pos) => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
.into_err(pos),
|
.into_err(pos),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
(Token::EOF, pos) => {
|
(_, pos) if map.is_empty() => {
|
||||||
return Err(
|
return Err(
|
||||||
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
PERR::MissingToken(Token::RightBrace.into(), MISSING_RBRACE.into())
|
||||||
.into_err(pos),
|
.into_err(pos),
|
||||||
@ -813,7 +813,7 @@ fn parse_switch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = BTreeMap::<u64, StmtBlock>::new();
|
let mut table = BTreeMap::<u64, Box<StmtBlock>>::new();
|
||||||
let mut def_stmt = None;
|
let mut def_stmt = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -873,7 +873,7 @@ fn parse_switch(
|
|||||||
let need_comma = !stmt.is_self_terminated();
|
let need_comma = !stmt.is_self_terminated();
|
||||||
|
|
||||||
def_stmt = if let Some(hash) = hash {
|
def_stmt = if let Some(hash) = hash {
|
||||||
table.insert(hash, stmt.into());
|
table.insert(hash, Box::new(stmt.into()));
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(stmt.into())
|
Some(stmt.into())
|
||||||
@ -1390,14 +1390,14 @@ fn make_assignment_stmt<'a>(
|
|||||||
}
|
}
|
||||||
// var (non-indexed) = rhs
|
// var (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.0.is_none() => {
|
Expr::Variable(x) if x.0.is_none() => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||||
}
|
}
|
||||||
// var (indexed) = rhs
|
// var (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (index, _, Ident { name, pos, .. }) = x.as_ref();
|
let (index, _, Ident { name, pos, .. }) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
AccessMode::ReadWrite => {
|
AccessMode::ReadWrite => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||||
}
|
}
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
AccessMode::ReadOnly => {
|
AccessMode::ReadOnly => {
|
||||||
@ -1411,14 +1411,14 @@ fn make_assignment_stmt<'a>(
|
|||||||
Position::NONE => match &x.lhs {
|
Position::NONE => match &x.lhs {
|
||||||
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
// var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs
|
||||||
Expr::Variable(x) if x.0.is_none() => {
|
Expr::Variable(x) if x.0.is_none() => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||||
}
|
}
|
||||||
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
// var[???] (indexed) = rhs, var.??? (indexed) = rhs
|
||||||
Expr::Variable(x) => {
|
Expr::Variable(x) => {
|
||||||
let (index, _, Ident { name, pos, .. }) = x.as_ref();
|
let (index, _, Ident { name, pos, .. }) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
AccessMode::ReadWrite => {
|
AccessMode::ReadWrite => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, rhs, op_info)), op_pos))
|
Ok(Stmt::Assignment(Box::new((lhs, op_info, rhs)), op_pos))
|
||||||
}
|
}
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
AccessMode::ReadOnly => {
|
AccessMode::ReadOnly => {
|
||||||
@ -2091,9 +2091,9 @@ fn parse_for(
|
|||||||
settings.pos = eat_token(input, Token::For);
|
settings.pos = eat_token(input, Token::For);
|
||||||
|
|
||||||
// for name ...
|
// for name ...
|
||||||
let name = match input.next().unwrap() {
|
let (name, name_pos) = match input.next().unwrap() {
|
||||||
// Variable name
|
// Variable name
|
||||||
(Token::Identifier(s), _) => s,
|
(Token::Identifier(s), pos) => (s, pos),
|
||||||
// Reserved keyword
|
// Reserved keyword
|
||||||
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
(Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => {
|
||||||
return Err(PERR::Reserved(s).into_err(pos));
|
return Err(PERR::Reserved(s).into_err(pos));
|
||||||
@ -2131,7 +2131,13 @@ fn parse_for(
|
|||||||
|
|
||||||
Ok(Stmt::For(
|
Ok(Stmt::For(
|
||||||
expr,
|
expr,
|
||||||
Box::new((loop_var, body.into())),
|
Box::new((
|
||||||
|
Ident {
|
||||||
|
name: loop_var,
|
||||||
|
pos: name_pos,
|
||||||
|
},
|
||||||
|
body.into(),
|
||||||
|
)),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -2481,7 +2487,7 @@ fn parse_stmt(
|
|||||||
|
|
||||||
// fn ...
|
// fn ...
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Token::Fn if !settings.is_global => Err(PERR::FnWrongDefinition.into_err(settings.pos)),
|
Token::Fn if !settings.is_global => Err(PERR::WrongFnDefinition.into_err(settings.pos)),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
Token::Fn | Token::Private => {
|
Token::Fn | Token::Private => {
|
||||||
@ -2766,7 +2772,7 @@ fn parse_fn(
|
|||||||
fn make_curry_from_externals(
|
fn make_curry_from_externals(
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
fn_expr: Expr,
|
fn_expr: Expr,
|
||||||
externals: StaticVec<Ident>,
|
externals: StaticVec<Identifier>,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> Expr {
|
) -> Expr {
|
||||||
// If there are no captured variables, no need to curry
|
// If there are no captured variables, no need to curry
|
||||||
@ -2780,7 +2786,11 @@ fn make_curry_from_externals(
|
|||||||
args.push(fn_expr);
|
args.push(fn_expr);
|
||||||
|
|
||||||
externals.iter().for_each(|x| {
|
externals.iter().for_each(|x| {
|
||||||
args.push(Expr::Variable(Box::new((None, None, x.clone()))));
|
let var_def = Ident {
|
||||||
|
name: x.clone(),
|
||||||
|
pos: Position::NONE,
|
||||||
|
};
|
||||||
|
args.push(Expr::Variable(Box::new((None, None, var_def))));
|
||||||
});
|
});
|
||||||
|
|
||||||
let expr = Expr::FnCall(
|
let expr = Expr::FnCall(
|
||||||
@ -2800,7 +2810,7 @@ fn make_curry_from_externals(
|
|||||||
// Convert the entire expression into a statement block, then insert the relevant
|
// Convert the entire expression into a statement block, then insert the relevant
|
||||||
// [`Share`][Stmt::Share] statements.
|
// [`Share`][Stmt::Share] statements.
|
||||||
let mut statements: StaticVec<_> = Default::default();
|
let mut statements: StaticVec<_> = Default::default();
|
||||||
statements.extend(externals.into_iter().map(|v| Stmt::Share(Box::new(v))));
|
statements.extend(externals.into_iter().map(Stmt::Share));
|
||||||
statements.push(Stmt::Expr(expr));
|
statements.push(Stmt::Expr(expr));
|
||||||
Expr::Stmt(Box::new(StmtBlock { statements, pos }))
|
Expr::Stmt(Box::new(StmtBlock { statements, pos }))
|
||||||
}
|
}
|
||||||
@ -2863,16 +2873,13 @@ fn parse_anon_fn(
|
|||||||
|
|
||||||
// External variables may need to be processed in a consistent order,
|
// External variables may need to be processed in a consistent order,
|
||||||
// so extract them into a list.
|
// so extract them into a list.
|
||||||
let externals: StaticVec<Ident> = {
|
let externals: StaticVec<Identifier> = {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
{
|
{
|
||||||
state
|
state
|
||||||
.external_vars
|
.external_vars
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(name, &pos)| Ident {
|
.map(|(name, _)| name.clone())
|
||||||
name: name.clone(),
|
|
||||||
pos,
|
|
||||||
})
|
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -2882,7 +2889,7 @@ fn parse_anon_fn(
|
|||||||
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
|
let params: StaticVec<_> = if cfg!(not(feature = "no_closure")) {
|
||||||
externals
|
externals
|
||||||
.iter()
|
.iter()
|
||||||
.map(|k| k.name.clone())
|
.cloned()
|
||||||
.chain(params.into_iter().map(|(v, _)| v))
|
.chain(params.into_iter().map(|(v, _)| v))
|
||||||
.collect()
|
.collect()
|
||||||
} else {
|
} else {
|
||||||
|
@ -2,14 +2,20 @@
|
|||||||
|
|
||||||
use crate::stdlib::{fmt, string::ToString};
|
use crate::stdlib::{fmt, string::ToString};
|
||||||
use crate::{Dynamic, ImmutableString, INT};
|
use crate::{Dynamic, ImmutableString, INT};
|
||||||
use serde::de::{Deserialize, Deserializer, Error, MapAccess, SeqAccess, Visitor};
|
use serde::de::{Deserialize, Deserializer, Error, Visitor};
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
use serde::de::SeqAccess;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use serde::de::MapAccess;
|
||||||
|
|
||||||
struct DynamicVisitor;
|
struct DynamicVisitor;
|
||||||
|
|
||||||
impl<'d> Visitor<'d> for DynamicVisitor {
|
impl<'d> Visitor<'d> for DynamicVisitor {
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
use crate::dynamic::{Union, Variant};
|
use crate::dynamic::{Union, Variant};
|
||||||
use crate::stdlib::string::ToString;
|
use crate::stdlib::string::ToString;
|
||||||
use crate::{Dynamic, ImmutableString};
|
use crate::{Dynamic, ImmutableString};
|
||||||
use serde::ser::{Serialize, SerializeMap, Serializer};
|
use serde::ser::{Serialize, Serializer};
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
use serde::ser::SerializeMap;
|
||||||
|
|
||||||
impl Serialize for Dynamic {
|
impl Serialize for Dynamic {
|
||||||
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
|
||||||
|
33
src/token.rs
33
src/token.rs
@ -875,7 +875,7 @@ pub fn parse_string_literal(
|
|||||||
|
|
||||||
match next_char {
|
match next_char {
|
||||||
// \r - ignore if followed by \n
|
// \r - ignore if followed by \n
|
||||||
'\r' if stream.peek_next().unwrap_or('\0') == '\n' => {}
|
'\r' if stream.peek_next().map(|ch| ch == '\n').unwrap_or(false) => {}
|
||||||
// \...
|
// \...
|
||||||
'\\' if escape.is_empty() && !verbatim => {
|
'\\' if escape.is_empty() && !verbatim => {
|
||||||
escape.push('\\');
|
escape.push('\\');
|
||||||
@ -1294,12 +1294,33 @@ fn get_next_token_inner(
|
|||||||
return get_identifier(stream, pos, start_pos, c);
|
return get_identifier(stream, pos, start_pos, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
// " or ` - string literal
|
// " - string literal
|
||||||
('"', _) | ('`', _) => {
|
('"', _) => {
|
||||||
let multi_line = c == '`';
|
return parse_string_literal(stream, state, pos, c, true, false).map_or_else(
|
||||||
|
|err| Some((Token::LexError(err.0), err.1)),
|
||||||
|
|out| Some((Token::StringConstant(out), start_pos)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// ` - string literal
|
||||||
|
('`', _) => {
|
||||||
|
// Start from the next line if ` at the end of line
|
||||||
|
match stream.peek_next() {
|
||||||
|
// `\r - start from next line
|
||||||
|
Some('\r') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
// `\r\n
|
||||||
|
if stream.peek_next().map(|ch| ch == '\n').unwrap_or(false) {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// `\n - start from next line
|
||||||
|
Some('\n') => {
|
||||||
|
eat_next(stream, pos);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
return parse_string_literal(stream, state, pos, c, !multi_line, multi_line)
|
return parse_string_literal(stream, state, pos, c, false, true).map_or_else(
|
||||||
.map_or_else(
|
|
||||||
|err| Some((Token::LexError(err.0), err.1)),
|
|err| Some((Token::LexError(err.0), err.1)),
|
||||||
|out| Some((Token::StringConstant(out), start_pos)),
|
|out| Some((Token::StringConstant(out), start_pos)),
|
||||||
);
|
);
|
||||||
|
@ -59,6 +59,11 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
|||||||
5
|
5
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let mut a = Array::new();
|
||||||
|
a.push((42 as INT).into());
|
||||||
|
|
||||||
|
assert_eq!(a[0].clone_cast::<INT>(), 42);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -299,7 +299,7 @@ fn test_closures_shared_obj() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
// Make closure
|
// Make closure
|
||||||
let f = move |p1: TestStruct, p2: TestStruct| -> Result<(), Box<EvalAltResult>> {
|
let f = move |p1: TestStruct, p2: TestStruct| -> Result<(), Box<EvalAltResult>> {
|
||||||
let action_ptr = res["action"].clone().cast::<FnPtr>();
|
let action_ptr = res["action"].clone_cast::<FnPtr>();
|
||||||
let name = action_ptr.fn_name();
|
let name = action_ptr.fn_name();
|
||||||
engine.call_fn(&mut Scope::new(), &ast, name, (p1, p2))
|
engine.call_fn(&mut Scope::new(), &ast, name, (p1, p2))
|
||||||
};
|
};
|
||||||
|
@ -94,9 +94,9 @@ fn test_map_assign() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let x = engine.eval::<Map>(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?;
|
let x = engine.eval::<Map>(r#"let x = #{a: 1, b: true, "c$": "hello"}; x"#)?;
|
||||||
|
|
||||||
assert_eq!(x["a"].clone().cast::<INT>(), 1);
|
assert_eq!(x["a"].clone_cast::<INT>(), 1);
|
||||||
assert_eq!(x["b"].clone().cast::<bool>(), true);
|
assert_eq!(x["b"].clone_cast::<bool>(), true);
|
||||||
assert_eq!(x["c$"].clone().cast::<String>(), "hello");
|
assert_eq!(x["c$"].clone_cast::<String>(), "hello");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -107,9 +107,9 @@ fn test_map_return() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
let x = engine.eval::<Map>(r#"#{a: 1, b: true, "c$": "hello"}"#)?;
|
let x = engine.eval::<Map>(r#"#{a: 1, b: true, "c$": "hello"}"#)?;
|
||||||
|
|
||||||
assert_eq!(x["a"].clone().cast::<INT>(), 1);
|
assert_eq!(x["a"].clone_cast::<INT>(), 1);
|
||||||
assert_eq!(x["b"].clone().cast::<bool>(), true);
|
assert_eq!(x["b"].clone_cast::<bool>(), true);
|
||||||
assert_eq!(x["c$"].clone().cast::<String>(), "hello");
|
assert_eq!(x["c$"].clone_cast::<String>(), "hello");
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -152,11 +152,11 @@ fn test_map_json() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
assert!(!map.contains_key("x"));
|
assert!(!map.contains_key("x"));
|
||||||
|
|
||||||
assert_eq!(map["a"].clone().cast::<INT>(), 1);
|
assert_eq!(map["a"].clone_cast::<INT>(), 1);
|
||||||
assert_eq!(map["b"].clone().cast::<bool>(), true);
|
assert_eq!(map["b"].clone_cast::<bool>(), true);
|
||||||
assert_eq!(map["c"].clone().cast::<INT>(), 42);
|
assert_eq!(map["c"].clone_cast::<INT>(), 42);
|
||||||
assert_eq!(map["$d e f!"].clone().cast::<String>(), "hello");
|
assert_eq!(map["$d e f!"].clone_cast::<String>(), "hello");
|
||||||
assert_eq!(map["z"].clone().cast::<()>(), ());
|
assert_eq!(map["z"].clone_cast::<()>(), ());
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
{
|
{
|
||||||
|
@ -8,6 +8,10 @@ fn test_string() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<String>(r#""Test string: \u2764""#)?,
|
engine.eval::<String>(r#""Test string: \u2764""#)?,
|
||||||
"Test string: ❤"
|
"Test string: ❤"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>("\"Test\rstring: \\u2764\"")?,
|
||||||
|
"Test\rstring: ❤"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>(" \"Test string: \\u2764\\\n hello, world!\"")?,
|
engine.eval::<String>(" \"Test string: \\u2764\\\n hello, world!\"")?,
|
||||||
"Test string: ❤ hello, world!"
|
"Test string: ❤ hello, world!"
|
||||||
@ -16,6 +20,14 @@ fn test_string() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<String>(" `Test string: \\u2764\nhello,\\nworld!`")?,
|
engine.eval::<String>(" `Test string: \\u2764\nhello,\\nworld!`")?,
|
||||||
"Test string: \\u2764\nhello,\\nworld!"
|
"Test string: \\u2764\nhello,\\nworld!"
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(" `\nTest string: \\u2764\nhello,\\nworld!`")?,
|
||||||
|
"Test string: \\u2764\nhello,\\nworld!"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<String>(" `\r\nTest string: \\u2764\nhello,\\nworld!`")?,
|
||||||
|
"Test string: \\u2764\nhello,\\nworld!"
|
||||||
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<String>(r#""Test string: \x58""#)?,
|
engine.eval::<String>(r#""Test string: \x58""#)?,
|
||||||
"Test string: X"
|
"Test string: X"
|
||||||
|
Loading…
Reference in New Issue
Block a user